Display value emitted from Vue component - vue.js

I created two separated Vue components and I able to emit a message thru a bus.
How can I render/display the message in the component that receives the message.
Example of the Vue component that receives the message:
<template>
<div v-model="cars">
Car model: {{ model }}
<input type="button" #click="display" value="example" />
</div>
</template>
<script>
export default {
data() {
return {
cars: null
}
},
mounted() {
bus.$on('CARS_LOADED', (cars) => {
this.cars = cars;
});
},
methods: {
display()
{
console.log(this.cars);
}
}
}
</script>
I can successfully emit and received the message, however the car model is not updated. I checked the message received and it contains the "model" key with a right value.
I cannot see any error in the Vue console and however if I replace "{{ model }}" by "{{ cars }}" I can see the full message object updated.
I am using Vue 2.x.
Update:
I enclose an example:
https://jsfiddle.net/kvzvxk4f/1/
As you can see in the example I cannot render an specific field from the object, however I can render the object as string.

I think that you are misunderstanding some parts of the vue syntax.
How to access properties of an object:
You just need to write {{ car.model }} to access a property of an object.
How to iterate through an array in a template:
If you want to display all the cars in your template, you should write:
<div v-for="car in cars">
{{ car }}
</div>
As you see, the v-for directive allows you to iterate through an array.
What is v-model?
v-model is used to bind a variable to an input or a component.
<template>
<div>
<input type="text" v-model="foo" />
</div>
</template>
<script>
export default {
data () {
return {
foo: 'bar'
}
}
}
</script>
In that case, the foo property will be bound to the input text.
Last point:
In your case, to make it work, you also need to create a root element for your template, because a template can't have multiple root elements:
<template>
<div>
<div v-for="car in cars">
{{ car }}
</div>
</div>
</div>

I found the answer.
I just have to type property separated by ".". Like for example {{cars.model}}.
<template id="compo2">
<div>
<div>
{{ field.name }}
</div>
<div>
Received: {{ field }}
</div>
</div>
</template>
Example:
https://jsfiddle.net/zuhb7s8q/3/

Related

Nuxt / Vue : Component call a Method in a page

There is my issue:
My page listing.vue list all products.
Theses products are in a Component, Product.vue.
In this component, there is a button to add this product to a selection, displaying on the listing.vue.
page/listing.vue :
<template>
<div>
<product v-for="...." />
<section>
<ul>
<li>Produit 1</li>
<li>Produit 3</li>
</section>
</div>
</template>
<script>
export default {
methods: {
addToSelection(id) {
// Code to add Product to <ul> //
}
}
}
</script>
component/product.vue:
<template>
<div>
{{ product.title }}
<button #click="addToSelection(product.id)">
Add product to selection
</button>
</div>
</template>
<script>
export default {
props: ['product'],
}
</script>
The problem is nuxt render an error:
the addToSelection method is unknown.
You should emit from your children to your parent
Product.vue
<button #click="emitProductToParent(product.id)">
...
methods: {
emitProductToParent(id) {
this.$emit('input', id)
}
}
Listing.vue
<Product #input="addToSelection" v-for="...." />
You cannot use a method that is not in the component your event listener is on. And even if you could, this is not the way to do. Use:
props to pass things down to children
emit to pass things up to parents
As explained in the official documentation here: https://v2.vuejs.org/v2/guide/components.html#Listening-to-Child-Components-Events

How can I dynamically render an array passed as a prop in vue.js?

I am trying to pass this data to a component in vue. I can get the data in the child component, and can render the array, or access each object properties by calling products[0].name, but I am looking to render each object separately in a v-for loop. please help!!
parent component:
<template>
<div>
<h1>Welcome To Our Shop</h1>
<div class="products">
<div v-for="product in products" v-bind:key="product.name">
<div><ShopItem v-bind:products="products" /></div>
</div>
</div>
</div>
</template>
<script>
import ShopItem from "../components/Shop/ShopItem";
export default {
name: "Shop",
components: { ShopItem },
data() {
return {
products: [
{
name: "Basic Deck",
price: 7,
description:
"The Basic Deck includes 68 cards: 10 cards in each of six categories, three icon legend cards, five blank cards for developing your own backstory elements, and instructions.",
image: require("#/assets/Draeorc.png"),
},
{
name: "Card Bundle",
price: 10,
description:
"The Card Bundle includes the Basic Deck, Technical Booster, Mystical Booster and instructions as a single self-printable PDF.",
image: require("#/assets/Twilight.png"),
},
{
name: "Full Bundle with Box",
price: 12,
description:
"The Full Bundle includes the Basic Deck, Technical Booster, Mystical Booster, instructions and tuck box as a single self-printable PDF.",
image: require("#/assets/Orig_Godbringer.png"),
},
],
};
},
};
</script>
child component:
<template>
<div class="product-container">
<div>
<h2>{{ products[0].name }}</h2> //this is where I want to call on the name
<div class="card-container">
<img src="../../assets/Draeorc.png" alt="cards" />
</div>
</div>
</div>
</template>
<script>
export default {
name: "ShopItem",
props: ["products"],
};
</script>
Here
<div v-for="product in products" v-bind:key="product.name">
<div><ShopItem v-bind:products="products" /></div>
</div>
your code does not make sense why?
because you want to go through the array which is here products and
show each item inside the products array. When you go through the
array, an item which is right for that iteration will be passed to
ShopItem component and no need to access index by using
products[index]
so it is better to do the following
<div><ShopItem v-bind:product="product" /></div>
Therefore, your ShopItem component will have access to a product one at the time when it goes through the v-for loop
Change
v-bind:products="products"
to
v-bind:products="product"
since you are using for-of loop
and on child component, change:
products[0].name
to
products.name
and since the property is an object, not an array, it's better to change your property name to product instead of products
So you will have this on parent component:
<div v-for="product in products" v-bind:key="product.name">
<div><ShopItem :product="product" /></div>
// :product is a shorthand for v-bind:product
</div>
and this on child component:
<template>
<div class="product-container">
<div>
<h2>{{ product.name }}</h2> //this is where I want to call on the name
<div class="card-container">
<img src="../../assets/Draeorc.png" alt="cards" />
</div>
</div>
</div>
</template>
<script>
export default {
name: "ShopItem",
props: ["product"],
};
</script>

How do I fit the value of one v-model into another v-model?

In this example, I'm trying to fit the value from div id="message" into textarea using the Vue v-model construct, but this not work
<template>
<div>
<textarea v-model="text"></textarea>
</div>
<div>
<div id="message" v-model="text2">{{ comment.message }}</div>
<button #click="update(text2);">
Edit
</button>
</div>
</template>
<script>
export default {
data() {
return {
text: [],
text2: null
}
},
methods: {
/* not work */
update(text2) {
this.text = text2;
}
}
<script>
How do I make sure that when I click on the "edit" button, the value of v-model="text2" insert into v-model="text" ?
You cannot use v-model on a <div> because it isn't an input element.
It seems what you want to do is set text to the comment message when you click the edit button so that it can be edited by the textarea. All you have to do is pass comment.message as the argument:
<button #click="update(comment.message)">
A couple of other things:
You cannot have multiple root elements in your template (you have two root <div> elements). You can just wrap everything in a single <div>.
text has initial value [] which isn't compatible with a textarea's v-model; did you mean ''?

All dynamically generated components are changing to the same value in VUEJS

we are building a chat application in Vuejs, now every chat message is component in our application, now whenever we are changing the value of one chat message, the value of all chat messages changes
What is happening
source code
App Component
const App = new Vue({
el: '#myApp',
data: {
children: [
MyCmp
],
m1: '',
m2: '',
m3: 'Hello world',
m4: 'How are you'
},
methods: {
sendMessage (event) {
if(event.key == "Enter") {
this.m2= this.m3;
this.children.push(MyCmp);
}
},
}
});
component code
let MyCmp = {
props: ['myMessage'],
template: `
<li class="self">
<div class="avatar"><img src="" draggable="false"/></div>
<div class="msg">
<p>{{ myMessage }}</p>
</div>
</li>
`
};
** view where components are generating **
<ol class="chat">
<template v-for="(child, index) in children">
<component :is="child" :key="child.name" v-bind="{myMessage: m3}"></component>
</template>
</ol>
Even though you are creating new components by pushing them into the children array, they are still getting bound to the same data via the line v-bind="{myMessage: m3}". Whenever you change m3, it will be passed down to all the child components and they will all render the same message.
This is an odd way of creating custom components since you could easily do so using the templating syntax or render function provided by Vue.
Solution 1 - Recommended
Change your approach - push message strings instead of card component definitions into children and use MyCmp inside v-for to render the message cards.
So the new template could be refactored as :-
<ol class="chat">
<template v-for="(message, index) in children">
<my-cmp :key="index" :my-message="message"></my-cmp>
</template>
</ol>
And inside App component, you can replace this.children.push(MyCmp) with this.children.push(messageVariable); where messageVariable contains the message that you receive from the input box.
Why is the recommended? This is a standard approach where component lists are rendered based on an array of data. It will be easier to scale and maintain.
Solution 2
You can use the v-once directive to bind the message one-time as static text. The binding won't be updated even if m3 changes on the parent.
Then MyCmp template will become :-
<li class="self">
<div class="avatar"><img src="" draggable="false"/></div>
<div class="msg">
<p v-once>{{ myMessage }}</p>
</div>
</li>
You bind myMessage of all your components instances with one variable m3. So, when m3 is changed myMessage in all instances changes respectively. Use another variable (e.g. msg) for rendering the message and then use myMessage property only for the initialisation of msg, like this:
let MyCmp = {
props: ['myMessage'],
data: function () {
return {
msg: this.myMessage
}
},
template: `
<li class="self">
<div class="avatar"><img src="" draggable="false"/></div>
<div class="msg">
<p>{{ msg }}</p>
</div>
</li>
`
};

Vue JS - Access root computed property inside a component

I'm attempting to access a computed property from the root Vue instance and access it inside a component. The <p class="currency"> element which is output outside of the component template outputs {{ currency }} correctly, but when trying to access {{ currency }} inside of the component nothing is output. I have tried setting currency as a prop but this doesn't appear to make any difference. I'm sure there must be a way to access the root Vue instance from within the component, something like {{ vm.currency }} but again I have tried this to no avail.
Here is the HTML.
<div id="app">
<ul class="plans">
<plan-component : name="Basic" ></plan-component>
<plan-component : name="Rec" ></plan-component>
<plan-component : name="Team" ></plan-component>
<plan-component : name="Club" ></plan-component>
</ul>
<template id="plan-component">
<li>
<h2 class="plan-name">{{ name }}</h2>
<h3 class="plan-cost">{{ currency }}</h3>
</li>
</template>
<p class="currency">{{ currency }}</p>
</div><!-- end #app -->
Here is the JS. The variable countryCode is defined elsewhere in my app, but like I said {{ currency }} is working outside of the component so this isn't an issue.
Vue.component('plan-component', {
template: '#plan-component',
props: {
name: String,
}
});
var vm = new Vue({
el: '#app',
computed: {
currency: function() {
if(countryCode === 'GB') {
return "£";
} else {
return "$";
}
}
}
});
For anyone with the same issue, you simply need to define $root before the property. So in my example instead of this...
<h3 class="plan-cost">{{ currency }}</h3>
...it needs to be this...
<h3 class="plan-cost">{{ $root.currency }}</h3>
The VueJS docs do talk about this under the Parent Chain section of Components.