interpolate a prop in interpolation vue js - javascript

I have a component that accepts props
<BarChart
header="header name"
dataPoint="data_to_look_at"
size=35
/>
With the dataPoint prop I want to use this in my component so that I can use it (I think, idk if this is the right solution) in an interpolated string like this to access items in an object
// inside of a v-for loop that iterates over an object etc
{{ data[index].attributes.${dataPoint} }}
I'm not sure how to do this and of course the above doesn't work
string interpolation Vue js
Not relevant to my question
How can I solve "Interpolation inside attributes has been removed. Use v-bind or the colon shorthand"? Vue.js 2
Not quite it either
How do interpolate a prop in a interpolation?

Observation : As you are iterating your item list by using v-for loop, No need to access the item by index. You can simply do like this :
<p v-for="data in items" :key="data.id">
{{ data.attributes[datapoint] }}
</p>
Live Demo :
Vue.component('child', {
data: function() {
return {
items: [{
id: 1,
attributes: {
name: 'Alpha'
}
}, {
id: 2,
attributes: {
name: 'Beta'
}
}, {
id: 3,
attributes: {
name: 'Gamma'
}
}]
}
},
props: ['header', 'datapoint'],
template: `<div>
<p v-for="data in items" :key="data.id">{{ data.attributes[datapoint] }}</p>
</div>`
});
var app = new Vue({
el: '#app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<child header="Header Name" dataPoint="name"></child>
</div>

Related

Vue js, how to bind input value from array?

i have a number field, that i want to bind value from an array like this :
<div v-for="p in products">
<input type="number" :value="carModel[p.name]" />
</div>
where p.name is a string (product name), car_a :
const app = Vue.createApp({
data() {
return {
products: {...},
carModel:[{car_a:4}, {car_b:2} ]
}
}
...
But this does not works, the input remains empty, while following runs without issue :
<div v-for="p in products">
<input type="number" :value="carModel" />
</div>
const app = Vue.createApp({
data() {
return {
products: {...},
carModel:4
}
}
...
So, my question is, how to bind the value properly from array if i have the key ?
thank you
First off, I wonder if your carModel array item object structure could be improved; perhaps better would be something like:
products: ["car_a", "car_b"],
carModel: [
{
name: "car_a",
value: 4,
},
{
name: "car_b",
value: 2,
},
{
name: "car_c",
value: 3,
},
]
I assume that the Strings held in the products array are a sub-set of the Strings in the carModel array of objects, and since you want an input that is reactive with the data, you will want the input's model to be the values held by each carModel object. So rather than v-for loop over the products array, v-for loop over the carModel but filter out the elements whose Strings are not held by the products array. Since we should not combine v-for with v-if, this filtering should be done in a computed property:
computed: {
filteredCarModel() {
return this.carModel.filter(cm => {
return this.products.includes(cm.name);
});
}
},
so then in the HTML template you can loop over the computed property:
<div v-for="cm in filteredCarModel" :key="cm.name">
<label :for="cm.name">{{ cm.name }} </label>
<input type="number" :id="cm.name" v-model="cm.value" />
</div>
This is key:
This will display proper values in the input elements, and these elements will remain reactive to the model such that changes to the inputs will cause changes to the data model, and visa-versa. Thus the display will be truly bound to the data model, which is what you're asking and what Vue.js is all about.
Here is a sample HTML showing that the data is in fact reactive:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<html>
<head>
<meta charset=utf-8" />
<title>Vue Example</title>
<script src="https://unpkg.com/vue#next"></script>
</head>
<body>
<h1>Vue Example</h1>
<div id="app">
<h2>Show Data</h2>
<div v-for="cm in carModel" :key="cm.name">
{{ cm.name }} : {{ cm.value }}
</div>
<h2>Change Data</h2>
<div v-for="cm in filteredCarModel" :key="cm.name">
<label :for="cm.name">{{ cm.name }} </label>
<input type="number" :id="cm.name" v-model="cm.value" />
</div>
</div>
<script>
Vue.createApp({
data() {
return {
products: ["car_a", "car_b"],
carModel: [
{
name: "car_a",
value: 4,
},
{
name: "car_b",
value: 2,
},
{
name: "car_c",
value: 3,
},
],
};
},
computed: {
filteredCarModel() {
return this.carModel.filter((cm) => {
return this.products.includes(cm.name);
});
},
},
}).mount("#app");
</script>
</body>
</html>
</html>
Note that if the products array is not a subset of the carModel array, if all the Strings present in products are also present in carModel name fields, then there will be no need to have a filteredCarModel() computed property.
change your carModel to hash
carModel: { 'car_a': 4, 'car_b': 2 }
because what you are trying to do is access array by string(which it should be number as index of the array)
Observations :
products should be an array to iterate via v-for.
As carModel is an array. You can not access object properties directly. Access it via index.
Working Demo :
new Vue({
el: "#app",
data: {
products: [{name: 'car_a'}, {name: 'car_b'}],
carModel:[{car_a: 4}, {car_b: 2}]
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(product, index) in products" :key="index">
<input type="number" :value="carModel[index][product.name]"/>
</div>
</div>

Mustache result is not updated

I was just playing around with Vue.js and found a problem I cannot explain why it happens. Here is my MWE:
Vue.component('list-entry', {
'template': '<input type="text" :value="t" #input="fireEvent()" ref="text">',
'props': ['t', 'idx'],
'methods': {
fireEvent() {
this.$emit('update:t', {
'value': this.$refs.text.value,
'idx': this.idx
})
}
}
})
new Vue({
el: "#app",
data: () => ({
texts: [
'Foo', 'Bar', 'Baz'
]
}),
methods: {
update(ev) {
console.log('Set ' + ev.value + ' at index ' + ev.idx)
this.texts[ev.idx] = ev.value
console.log(this.texts)
}
}
})
input {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class='container'>
<list-entry
v-for="(t, idx) in texts"
:key="t"
:t="t"
:idx="idx"
#update:t="update($event)"
></list-entry>
</div>
{{ texts }}
</div>
Let me explain it a bit. There is a global data property texts that contains a few strings. For each of these strings, a custom component list-entry is generated. The strings are propagated using v-bind to these components.
Upon change of the text in the inputs, I want to update the global data property. Therefore a update:t event is fired and listened on in the main app. Here the update(ev) function should do the things.
If you run it you will see that in the console, the corresponding messages appear and the array is updated as well. However, in the output on the HTML screen (replaced from {{ texts }}), the value is not updated.
Now I am confused. Is the data property updated or not? Why is it not visible in the mustache output? Why is it correctly output in the console at the same time?
You could achieve the desired behavior with a short code by using Using v-model on Components
Vue.component('list-entry', {
'template': `<input type="text" :value="value" #input="$emit('input', $event.target.value)" />`,
props: ['value'],
})
new Vue({
el: "#app",
data: () => ({
texts: [
'Foo', 'Bar', 'Baz'
]
}),
})
input {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class='container'>
<list-entry v-for="(t, idx) in texts" :key="idx" v-model="texts[idx]"></list-entry>
</div>
{{ texts }}
</div>

How do I render objects items in groups based on values

I am looking for a way to group objects in different DIV's with a header text.
The following allows me to group 'category 'bla1' but I'd like to have categories 'bla2' and 'bla3' to be grouped as well. The number of categories isn't fixed and can be less or more.
This is what I have so far: JSFIddle
<div id="app">
<h2>Todos:</h2>
<ol>
<div v-for="todo in todos">
<p v-if="todo.category == 'bla1'">
{{ todo.text }}
</p>
</div>
</ol>
</div>
new Vue({
el: "#app",
data: {
todos: [
{ text: "Learn JavaScript", category: "bla1" },
{ text: "Learn Vue", category: "bla2" },
{ text: "Play around in JSFiddle", category: "bla1" },
{ text: "Build something awesome", category: "bla3" }
]
}
})
resulting is:
Todos:
Learn JavaScript
Play around in JSFiddle
But the desired result is:
Todos:
bla1
Learn JavaScript
Play around in JSFiddle
bla2
Learn Vue
bla3
Build something awesome
Create a computed property to group you list by category, and then do a nested loop in your template:
computed: {
groupedToDos() {
let groupedToDos = {};
this.todos.forEach(todo => {
if (todo.category in groupedToDos) {
groupedToDos[todo.category].push(todo)
}
else {
groupedToDos[todo.category] = [todo]
}
})
return groupedToDos
}
}
And then in your template:
<div id="app">
<h2>Todos:</h2>
<ol>
<div v-for="(todos, key) in groupedToDos">
<h3>{{ key }}</h3>
<p v-for="todo in todos">
{{ todo.text }}
</p>
<br />
</div>
</ol>
</div>
jsfiddle
The computed property approach is the best IMHO.
computed:{
sampleComputed(){
if (this.sampleData.value === "Sample 1") return "Sample 1 Value"
if (this.sampleData.value === "Sample 2") return "Sample 2 Value"
return "Sample 0 Value"
}
}
Above sample code can be used with Switch case also if you have more than 5 conditions, or even using object literals to fetch returning data to improve time complexity.
However, if you want you can add more v-if inside the template itself, but it is not advisable to do so since you should focus on separating complex logic from template as a best practice.
References: Proper way to express switch statement with Vue data bindings

How to get First Letter of First & Last name: Vue.js?

Suppose, My Name is "Aerofoil Todo Kite" I want AK
I got a code from Stackoverflow. I hope it will work. But my question is, I am printing data from Array of Objects with v-for loop.
How do I pass the Name to compute that?
I think, Computed Property don't accept Parameter.
Then what will be the process??
Method can do. But It is calling for many times!!!
data:
tableData: [
{ customer: 'EE Fashion'},
{ customer: 'Tom Hangs Ron'}
}]
methods: {
nameOfCompany(fullName) {
console.log(fullName);
return "HL";
}
}
Code of Mine:
<template slot-scope="scope">
<p style="margin-top: 5px;"><b>{{ nameOfCompany(scope.row.customer) }}</b></p>
</template>
Here is the problem:
{{ nameOfCompany(scope.row.customer) }}
This function is calling for many times!!!!
What will be the approach to do that?
You may write a Customer component so you only compute the company's name once:
It takes a name, and in data, computes the associated companyName.
const mytable = {
props: ['rows'],
template: `
<table>
<tr v-for="row in rows">
<slot :row="row"></slot>
</tr>
</table>
`
}
const mycustomer = {
props: ['name'],
data () {
return {
companyName: this.name.split(' ').map(x => x[0].toUpperCase()).join('')
}
},
template: `
<td>{{ name }} - <abbr>{{ companyName }}</abbr></td>
`
}
let vm = new Vue({
el:'#el',
components: { mytable, mycustomer },
template: `
<mytable :rows="['grod zi', 'tu rok']">
<template v-slot:default="{ row: user }">
<mycustomer :name="user"/>
</template>
</mytable>
`
});
abbr {
color: blue;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="el"></div>
You can use either filters or computed.
Filters: Vue.js allows you to define filters that can be used to apply common text formatting. Filters are usable in two places: mustache
interpolations and v-bind expressions (the latter supported in
2.1.0+). Filters should be appended to the end of the JavaScript expression, denoted by the “pipe” symbol: doc
new Vue({
el: "#app",
data: {
tableData: [
{ customer: 'EE Fashion', company_name: "FOO BAR" },
{ customer: 'Tom Hangs Ron', company_name: "BAZ FOO BAR"},
{ customer: 'Jerry', company_name: "Lorem Ipsum Dorsum Zaren" }
],
},
filters: {
short_hand (company_name) {
// You can put your logic here...
let words = company_name.split(" ")
let short_hand = words[0][0] + words[words.length-1][0]
return short_hand // <-- The return value as Per logic
}
},
computed: {
getTableData () {
return this.tableData.map(data => {
let words = data.company_name.split(" ")
let short_hand = words[0][0] + words[words.length-1][0]
return { short_hand, ...data }
})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
USING FILTER: <br>
<div
v-for="(data, i) in tableData"
:key="'using-filter-'+i"
>
{{ data.company_name | short_hand }}
</div>
<hr> USING COMPUTED: <br>
<div
v-for="(data, i) in getTableData"
:key="'using-computed-'+i"
>
{{ data.short_hand }}
</div>
</div>

how to see changes in new data generated in a vue component?

Well I hope to explain, I'm generating this data from a component, when I click the checkbox changes in the generated data are not reflected, but when clicking the button with a data already in the instance changes are made, I appreciate if you explain Why or do they have a solution?
this my code
js
Vue.component('fighters', {
template: '#fighters-template',
data() {
return {
items: [
{ name: 'Ryu' },
{ name: 'Ken' },
{ name: 'Akuma' }
],
msg: 'hi'
}
},
methods: {
newData() {
this.items.forEach(item => {
item.id = Math.random().toString(36).substr(2, 9);
item.game = 'Street Figther';
item.show = false;
});
this.items.push()
},
greeting() {
this.msg = 'hola'
}
}
});
new Vue({
el: '#app'
})
html
<main id="app">
<fighters></fighters>
</main>
<template id="fighters-template">
<div class="o-container--sm u-my1">
<ul>
<li v-for="item in items">
<input type="checkbox" v-model="item.show">
{{ item.name }}</li>
</ul>
<button class="c-button c-button--primary" #click="newData()">New Data</button>
<h2>{{ msg }}</h2>
<button class="c-button c-button--primary" #click="greeting()">Greeting</button>
<hr>
<pre>{{ items }}</pre>
</div>
</template>
this live code
https://jsfiddle.net/cqx12a00/1/
Thanks for you help
You don't declare the show variables that your checkboxes are bound to, so they are not reactive – Vue is not aware when one is updated.
It should be initialized like so:
items: [
{ name: 'Ryu', show: false },
{ name: 'Ken', show: false },
{ name: 'Akuma', show: false }
]
You might think that newData would fix it, since it assigns a show member to each item, but Vue cannot detect added properties so they're still not reactive. If you initialized the data as I show above, then the assignment would be reactive.
If you want to add a new reactive property to an object, you should use Vue.set.

Categories