Using Vue2 I have an array of objects in data which have an html string rendered in a v-for loop. Part of each string is a prop, which renders correctly initially. However, when the prop value is updated with v-model the data in the v-for loop is not updated.
jsfiddle: When the input is changed from "Bob" to "Sally" all instances should change, but those in the for-loop do not.
html
<div id="app">
<h2>Testing</h2>
<ul>
<li v-for="statement in statements" v-html="statement.text"></li>
</ul>
<input v-model="name" placeholder="edit name">
<p>Name is: {{ name }}</p>
<p >Outside loop: <b>{{name}}</b> likes dogs.</p>
</div>
vue
new Vue({
el: "#app",
data: function() {
return {
statements: [
{
id: 'food',
text: '<b>' this.name '</b> likes to eat ice cream.',
},
{
id: 'fun',
text: 'Running is the favorite activity of <b>' this.name '</b>',
},
],
}
},
props: {
name: {
type: String,
default: 'Bob',
},
},
})
The code has been simplified - the actual HTML strings have ~3 variables each that need to update, and are at different locations in each string, so I can't think of another way to replace the values when they are updated, while preserving the html tags. This is intended to be a single-page vue application, but is using Laravel and blade for some general page formatting.
CodePudding user response:
If I understood you correctly try like following snippet:
(you should not mutate props, emit event from child component and update your props)
Vue.component('Child', {
template: `
<div >
<h2>Testing</h2>
<ul><li v-for="statement in statements" v-html="statement.text"></li></ul>
<input v-model="cname" @input="$emit('namechanged', $event.target.value)" placeholder="edit name">
<p>Name is: {{ cname }}</p>
<p >Outside loop: <b>{{ cname }}</b> likes dogs.</p>
</div>`,
props: {
name: {
type: String,
default: 'Bob',
},
},
data() {
return {
cname: this.name,
}
},
computed: {
statements() {
return [{id: 'food', text: '<b>' this.cname '</b> likes to eat ice cream.',}, {id: 'fun', text: 'Running is the favorite activity of <b>' this.cname '</b>',},]
}
},
})
new Vue({
el: '#demo',
data() {
return {
pname: ''
}
},
methods: {
updateName(val) {
this.pname = val
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<child @namechanged="updateName"></child>
</div>
CodePudding user response:
There are several problems with what you've shown so far.
- you're trying to mutate a
prop
. If you do that, when the value in the parent updates, your change will be overridden. So you need to update the parent. Use .sync. this
insidedata
is not what you think it is. Use computed to compute a value based on other reactive props of your component. When the reactive references change, your computed gets updated as well- your looping without
key
s - you're using
v-html
(it's a security risk). You're better off using markdown in this case.
Another problem is you probably simplified your example too much. I doubt you have props on the app itself, you probably have a <Person />
component...
Here's how something like this would work:
Vue.component('person', {
template: `#person-tpl`,
props: {
person: {
type: Object,
default: () => ({})
}
},
data: () => ({
inputs: [
{ name: 'name', label: 'Name', placeholder: 'Bob' },
{ name: 'food', label: 'Favorite food', placeholder: 'icecream' },
{ name: 'fun', label: 'Favorite activity', placeholder: 'Running' }
]
}),
computed: {
name: {
get() { return this.person?.name || '' },
set(name) { this.update({ name }) }
},
fun: {
get() { return this.person?.fun || '' },
set(fun) { this.update({ fun }) }
},
food: {
get() { return this.person?.food || '' },
set(food) { this.update({ food }) }
},
statements() {
return [{
id: 'food',
text: `<b>${this.name}</b> likes to eat ${this.food}.`
}, {
id: 'fun',
text: `${this.fun} is the favourite activity of <b>${this.name}</b>`
}]
}
},
methods: {
update(payload) {
this.$emit('update:person', {
...this.person,
...payload
});
}
}
})
new Vue({
el: "#app",
data: () => ({
persons: [
{name: 'Jane', food: 'apples', fun: 'Swimming'},
{},
{name: 'John', food: 'milk', fun: 'Sleeping' }
]
}),
methods: {
updatePerson(key, val) {
this.persons.splice(key, 1, val)
}
}
})
<script src="https://v2.vuejs.org/js/vue.min.js"></script>
<div id="app">
<h2>Testing</h2>
<person v-for="(person, key) in persons" :key="key"
:person.sync="person"
@update:person="val => updatePerson(key, val)" />
</div>
<template id="person-tpl">
<div>
<ul>
<li v-for="(statement, key) in statements" :key="key" v-html="statement.text"></li>
</ul>
<template v-for="prop in inputs">
<label :key="prop.name">{{ prop.label }}
<input :value="person[prop.name]"
:placeholder="prop.placeholder"
@input="e => update({[prop.name]: e.target.value })">
</label>
</template>
</div>
</template>