Accessing the computed properties of components in Vue from the parent - javascript

How do you access the computed properties of components in Vue from the parent?
In this example, I have a cart with item components and I want to compute and display the sum of the cart items:
cart.js
var vm = new Vue({
el: '#cart',
components: {
cartItem: require('./components/cart-item.js'),
},
data: {
items: [
{ name: 'apple', qty: 5, price: 5.00 },
{ name: 'orange', qty: 7, price; 6.00 },
],
},
computed: {
// I want to do something like this and access lineTotal from cart
cartTotal: function() {
return this.items.reduce(function(prev,curr) {
return prev + curr.lineTotal;
}, 0)
}
}
});
cart-item.js
module.exports = {
template: require('./cart-item.template.html'),
props: ['fruit'],
computed: {
lineTotal: function() {
return this.fruit.price * this.fruit.qty;
}
},
};
main html
<li v-for="item in items" is="cart-item" :fruit="item">
...
#{{ cartTotal }}
How do I access the lineTotal properties of each cart-item to use in summing cartTotal?
Note that I do not want to redo the calculations done in lineTotal but instead use the computed properties directly.

You have to name the children, for example by means of the v-ref directive. Then from the parent you resolve the children properties with this.$refs.mychild.myproperty

Related

How do I display one object of an array in Vue.JS

Let's say I have this list of objects in Vue.JS
data () {
return{
examples: [
{
exampleID: 5,
exampleText: 'foo'
},
{
exampleID: 3,
exampleText: 'bar'
}
]
}
}
Now let's say I want to display the object with the exampleID of 3 in an element i created before
<Task
v-for="example in examples"
:key="example.exampleID"
:example="example"
/>
I want to display everything, that is in the object (the ID and the text)
Task component :
<template>
<div class="exercise">
<div class="exercise-id">
<h1>ID NUMBER: {{ example.exampleID }}</h1>
</div>
<div class="exercise-task">
<h2>{{ example.exampleText}}</h2>
</div>
</div>
</template>
<script>
export default {
name: 'Task',
props: ['example']
}
</script>
You shouldn't use v-if and v-for in the same element, in this case i suggest to use a computed property that only return the desired example :
data () {
return{
examples: [
{
exampleID: 5,
exampleText: 'foo'
},
{
exampleID: 3,
exampleText: 'bar'
}
]
}
},
computed:{
myExample(){
return this.examples.find(ex=>ex.exampleID===3)
}
}
then render it like :
<Task :example="myExample"/>
Another efficient way of doing it without v-for is
data () {
return{
examples: [
{
exampleID: 5,
exampleText: 'foo'
},
{
exampleID: 3,
exampleText: 'bar'
}
],
id: 3
}
},
computed:{
myExample(){
return id => this.examples.find(ex=>ex.exampleID===id)
}
}
rendering part will be like
<Task :example="myExample(id)"/>
In this way you no need to hardcode the value as 3 in the computed property.

Vue.js find which component emitted an event

I'm trying to have a component representing a shopping item.
I'll have one of this component for every item in my shopping list.
I don't know how to update the parent data (the shopping list) when the child is edited (the shopping item)
Shopping List
<template>
<div id="app">
<shopping-item
v-for="(item, index) in shoppingList"
:key="index"
:propsName="item.name"
:propsQuantity="item.quantity"
#shoppingItemEdited="handleEdit"
></shopping-item>
</div>
</template>
<script>
import ShoppingItem from "./components/ShoppingItem.vue";
export default {
name: "App",
components: {
ShoppingItem,
},
data() {
return {
shoppingList: [
{ name: "apple", quantity: 8 },
{ name: "banana", quantity: 3 },
{ name: "kiwi", quantity: 7 },
{ name: "peach", quantity: 5 },
],
};
},
methods: {
handleEdit(itemEdited) {
// How to get the index of the shopping-item that has been updated ?
// shoppingList[???] = itemEdited
console.log(itemEdited);
// => {name: "white peach", quantity: "6"}
},
},
};
</script>
Shopping Item
<template>
<div>
<input v-model="name" placeholder="ex: banana" #change="updateParent" />
<input
v-model="quantity"
type="number"
placeholder="ex: 3"
#change="updateParent"
/>
</div>
</template>
<script>
export default {
data() {
return {
name: "",
quantity: null,
};
},
props: {
propsName: String,
propsQuantity: Number,
},
created() {
this.name = this.propsName;
this.quantity = this.propsQuantity;
},
methods: {
updateParent() {
this.$emit("shoppingItemEdited", {
name: this.name,
quantity: this.quantity,
});
},
},
};
</script>
So I have few questions:
How can I know witch component emited the event 'shoppingItemEdited' ? If I knew it, I could find out which shoppingList item I should update.
I red I should not update props in the child, so I create data based on props, is that a standard way of doing that ?
this.name = this.propsName;
this.quantity = this.propsQuantity;
Just pass an index to a handler: #shoppingItemEdited="handleEdit(index, $event)"
No it's not "standard" - created hook is called only once when component is created, so if value of prop changes later (from parent), data will not update. It's probably not a problem in your case but usually its better to use computed:
computed: {
name: {
get() { return this.propsName },
set(value) {
this.$emit("shoppingItemEdited", {
name: value,
quantity: this.quantity,
});
}
}
}
...handle event in parent and the change will propagate (by props) to a child

Use a template in vue component passed as a prop

I'm a total newbie, so please bear with me if I'm still grasping with the coding fundamentals.
I want to use a template that is defined in the prop. The template is inside the DOM. The reason I want to do it this way is that I want to reuse the component logic (specifically the pagination), but may want to change how the way the template displays the data in different pages. So I wanted to seaparate the template from the script file.
This is the HTML File:
<div id="store-list">
<paginated-list :list-data="stores" use-template="#displayList"></paginated-list>
</div>
<script type="text/template" id="display-list">
<div>
<div v-for="p in paginatedData">
{{p.BusinessName}}
</div>
</div>
</script>
This is the .js file:
Vue.component('paginated-list', {
data() {
return {
pageNumber: 0
}
},
props: {
listData: {
type: Array,
required: true
},
useTemplate: {
type: String,
required: false
},
size: {
type: Number,
required: false,
default: 10
}
},
computed: {
pageCount() {
let l = this.listData.length,
s = this.size;
return Math.floor(l / s);
},
paginatedData() {
const start = this.pageNumber * this.size,
end = start + this.size;
return this.listData
.slice(start, end);
}
},
//template: document.querySelector('#display-list').innerHTML // this works
template: document.querySelector(useTemplate).innerHTML // this does not
});
var sl = new Vue({
el: '#store-list',
data: {
stores: [{
"BusinessName": "Shop Number 1"
}, {
"BusinessName": "Shop Number 2"
}, {
"BusinessName": "Shop Number 3"
}]
}
});
var shoppingList = new Vue({
el: '#shopping-list',
data: {
header: 'shopping list app',
newItem: '',
items: [
'1',
'2',
'3',
]
}
})
Any help is greatly appreciated. Thank you.
You can use the inline-template attribute to override the component's template at render time. For example
<paginated-list :list-data="stores" inline-template>
<div>
<div v-for="p in paginatedData">
{{p.BusinessName}}
</div>
</div>
</paginated-list>
See https://v2.vuejs.org/v2/guide/components-edge-cases.html#Inline-Templates
Your component can still have a default template but this will override it if set.

How to display value on dialog and console in different ways for an object array using paper-check box inside dom-repeat

How to display value on dialog and console in different ways for an object array using paper-check box inside dom-repeat
This is the program and How I want the output is mentioned below. Please check the code and help me solve this.
Polymer({
is: 'check-list',
properties: {
checkdata: {
type: Array,
value: [{
name: 'Bike',
no: 1,
}, {
name: 'Car',
no: 2,
}, {
name: 'Cycle',
no: 3,
}, {
name: 'Bus',
no: 4,
}, {
name: 'Truck',
no: 5,
}],
},
},
checkall: function() {
var checkvalue = this.checkdata;
var checktext = [];
for (i = 0; i < checkvalue.length; i++) {
var checkarray = {
vehiclename:checkvalue[i].name,
vehiclenumber:checkvalue[i].no,
};
if (checkvalue[i].checked == true) {
checktext.push(checkarray);
this.checkeditem = checkarray.vehiclename;
}
}
console.log(checktext);
});
<dom-module id="check-list">
<template>
<template is="dom-repeat" items="{{checkdata}}">
<paper-checkbox on-tap="checkall" checked="{{item.checked}}">{{item.name}}</paper-checkbox>
</template>
</template>
</dom-module>
There are several things to be fixed, just as a starting point, since I'm not quite sure what you're trying to do, and what your context is:
It's necessary to add all values (which you want to be able to bind to in your local DOM, or expose for use in parent elements) to your properties array. I'm not sure about checkedItem, but I added it anyway.
If you want bindings on your properties to be updated by Polymer, add the notify attribute to your property. Also, if you want to notify subproperties on Objects (e.g. this.checkdata[i].checked, which is set by the checkbox), you need to define that subproperty on initialization.
When initializing a property's value with something other than a primitive (i.e. Array or Object), use a function for that; otherwise you'll end up setting the value on your Polymer Object's prototype, which will apply subsequent changes (e.g. Array pushes) to all instances of your component.
Use const/let instead of var within a for-loop. Any var declaration would be hoisted to the top of your function and closured within any functions defined in the loop's scope, which might lead to unexpected results (though your code would be safe as it is—for the lack of functions—but again, good practice).
I have integrated the above points into your code snippet.
Also, checkeditem is now a bindable/notifying array property, which is appended with each selection. Obviously, you'd have to also remove items, whenever its according checkbox is deselected.
Polymer({
is: 'check-list',
properties: {
checkdata: {
type: Array,
notify: true, // ??
value: function() {
return [{
name: 'Bike',
no: 1,
checked: false
}, {
name: 'Car',
no: 2,
checked: false
}, {
name: 'Cycle',
no: 3,
checked: false
}, {
name: 'Bus',
no: 4,
checked: false
}, {
name: 'Truck',
no: 5,
checked: false
}];
}
},
checkeditem: {
type: Array,
value: function() { return []; },
notify: true
}
},
checkall: function() {
const checkvalue = this.checkdata;
const checktext = [];
for (i = 0; i < checkvalue.length; i++) {
const checkarray = {
vehiclename: checkvalue[i].name,
vehiclenumber: checkvalue[i].no,
};
if (checkvalue[i].checked) {
checktext.push(checkarray);
this.push('checkeditem', checkarray.vehiclename);
}
}
console.log(checktext);
}
});
<dom-module id="check-list">
<template>
<template is="dom-repeat" items="{{checkdata}}">
<paper-checkbox on-tap="checkall" checked="{{item.checked}}">[[item.name]]</paper-checkbox>
</template>
</template>
</dom-module>

vue.js how to get the value of the custom element attribute parameters?

In the use of vue.js, on the definition of attribute id number how to get it?
I want to $refs read id="20" value. I hope console.log is 21,10,15. go to the jsfiddle
Look at:
var app = new Vue({
el: "#app",
data: {
fruit: [{
id: 21,
name: 'Peach'
}, {
id: 10,
name: 'Apple'
}, {
id: 15,
name: 'Lemon'
}],
branid: ''
},
mounted() {
console.log(this.$refs)
}
})
<div id="app">
<ul>
<li v-for="item in fruit" :id="item.id" ref="branid">{{item.name}}</li>
</ul>
</div>
You should be able to simply refer to the branid key and extract the id from each li item, see the updated fiddle here:
console.log(this.$refs.branid.map(li => li.id))
you don't need to get it through this.$refs, it is in your fruit array so you can easily get it by using map or filter function:
Example:
mounted() {
var ids = this.fruit.map(function(obj) {
return obj.id;
});
console.log(ids)
}

Categories