Related
How to pass argument to $store.getters with onclick event? I can see the default value but not the new value. This is my code
const store = new Vuex.Store({
state: {
notes: [
{ id: 1, text: 'Hello World 1, Vuex!', deleted: false},
{ id: 2, text: 'Hello World 2, Vuex!', deleted: true},
{ id: 3, text: 'Hello World 3, Vuex!', deleted: false}
]
},
getters: {
getNoteById: (state) => (id) => {
return state.notes.find(note => note.id === id);
}
}
});
new Vue({
el: "#app",
data() {
return {
result: ''
}
},
store,
computed: {
note() {
return this.$store.getters.getNoteById(3);
}
},
methods: {
showText(id) {
this.result = this.$store.getters.getNoteById(id);
}
}
});
and html
<div id='app'>
<p>Default value: {{ note.text }}</p>
<button #click="showText('1')">1</button>
<button #click="showText('2')">2</button>
<button #click="showText('3')">3</button>
<p>New value: {{ result.text }}</p>
</div>
Your getter is working perfectly. And it correctly returns undefined when searching for any item with an id of '1', '2' or '3'.
Because the store doesn't contain any such item.
All your store items have number ids, none of them has a string id. See it working (I changed the template to send numbers):
Vue.config.productionTip = false;
Vue.config.devtools = false;
const store = new Vuex.Store({
state: {
notes: [
{ id: 1, text: 'Hello World 1, Vuex!', deleted: false},
{ id: 2, text: 'Hello World 2, Vuex!', deleted: true},
{ id: 3, text: 'Hello World 3, Vuex!', deleted: false}
]
},
getters: {
getNoteById: (state) => (id) => {
return state.notes.find(note => note.id === id);
}
}
});
new Vue({
el: "#app",
data() {
return {
result: ''
}
},
store,
computed: {
note() {
return this.$store.getters.getNoteById(3);
}
},
methods: {
showText(id) {
this.result = this.$store.getters.getNoteById(id);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.js"></script>
<script src="https://unpkg.com/vuex"></script>
<div id='app'>
<p>Default value: {{ note.text }}</p>
<button #click="showText(1)">1</button>
<button #click="showText(2)">2</button>
<button #click="showText(3)">3</button>
<p>New value: {{ result.text }}</p>
</div>
If, for any reason, you don't have control over the incoming type (you might have this problem when reading the value of an <input>, which is always a string, even when the type of the input is "number"), you might want to cast the value as number: +'5' => 5.
So, as an alternative to fixing the template, as above, you could do this in the method:
showText(id) {
this.result = this.$store.getters.getNoteById(+id);
}
I try to display an array of object sort by date.
When it's mount there is no problem but when i try to add new data, it is not shown in the correct order. I'm sorry but I cannot be more clear ...
Here is my code :
<template>
<div class="container">
<div class="row">
<div class="col" style="position: relative; height: 50vh">
<canvas id="graphique"></canvas>
</div>
<div>
<h1>Finds</h1>
<div v-for="(find, index) in finds" v-bind:key="index">
<input v-model="find.date" />
<input v-model="find.value" />
<input v-model="find.label" />
</div>
<input v-model="dateToAdd" />
<input v-model="valueToAdd" />
<input v-model="labelToAdd" />
<button #click="addFind">Add cost</button>
<p v-if="Chart != null">{{ Chart.data.datasets[0].data }}</p>
</div>
</div>
</div>
</template>
<script>
import Chart from "chart.js/auto";
export default {
name: "PlanetChart",
data() {
return {
Chart: null,
valueToAdd: 20,
labelToAdd: "A",
dateToAdd: "12/01/1900",
finds: [
{ date: "12/01/2020", label: "1", value: 12 },
{ date: "12/01/2021", label: "2", value: 2 },
{ date: "12/01/2000", label: "0", value: 1 },
{ date: "12/01/2023", label: "3", value: 12 },
],
};
},
mounted() {
const ctx = document.getElementById("graphique");
this.finds.sort(function (a, b) {
return new Date(a.date) - new Date(b.date);
});
this.Chart = new Chart(ctx, {
type: "line",
data: {
datasets: [
{
label: "My dataset",
fill: false,
lineTension: 0.1,
backgroundColor: "rgba(75,192,192,0.4)",
borderColor: "rgba(75,192,192,1)",
borderCapStyle: "butt",
borderDash: [],
borderDashOffset: 0.0,
borderJoinStyle: "miter",
pointBorderColor: "rgba(75,192,192,1)",
pointBackgroundColor: "#fff",
pointBorderWidth: 1,
pointHoverRadius: 5,
pointHoverBackgroundColor: "rgba(75,192,192,1)",
pointHoverBorderColor: "rgba(220,220,220,1)",
pointHoverBorderWidth: 2,
pointRadius: 5,
pointHitRadius: 10,
data: this.finds,
},
],
},
options: {
parsing: {
xAxisKey: "date",
yAxisKey: "value",
},
responsive: true,
maintainAspectRatio: false,
},
});
},
methods: {
addFind: function () {
if (this.valueToAdd > 0 && this.labelToAdd != "") {
this.finds.push({
date: this.dateToAdd,
value: this.valueToAdd,
label: this.labelToAdd,
});
this.finds.sort(function (a, b) {
return new Date(a.date) - new Date(b.date);
});
this.Chart.data.datasets[0].data = this.finds;
console.log(this.Chart.data.datasets[0].data);
this.Chart.update();
this.valueToAdd = 0;
this.dateToAdd = "";
this.labelToAdd = "";
}
},
},
};
</script>
Here the result I have :
The point at the extrem right must be the first because his date is before the others points.
So it seems to be a rendering probleme and not a sort probleme.
If you need a js.fiddle let me know.
Thanks for your help !
EDIT : The only solution found is to create a function to create the graph and inside the addFind function do this :
this.Chart.destroy();
this.Chart = this.createChart(this.finds)
This is a working example with you data.
Here is the template code, I used a computed property to sort the array of data instead of sorting the actual data.
Please review the dependencies of the Chart.js package, as time series chart require external date adapters as mentioned here.
Also, there seems to be a Chart.js wrapper for Vue, perhaps you should try it.
<template>
<div class="container">
<div class="row">
<div class="col" style="position: relative; height: 50vh">
<canvas id="graphique"></canvas>
</div>
<div>
<h1>Finds</h1>
<div v-for="(find, index) in finds" v-bind:key="index">
<input v-model="find.date" />
<input v-model="find.value" />
<input v-model="find.label" />
</div>
<input v-model="dateToAdd" />
<input v-model="valueToAdd" />
<input v-model="labelToAdd" />
<button #click="addFind">Add cost</button>
<p v-if="Chart">{{ Chart.data }}</p>
</div>
</div>
</div>
</template>
<script>
import Chart from "chart.js/auto";
import "chartjs-adapter-moment";
export default {
name: "PlanetChart",
data() {
return {
Chart: null,
valueToAdd: 20,
labelToAdd: "A",
dateToAdd: "12/01/1900",
finds: [
{ date: "12/01/2020", label: "1", value: 12 },
{ date: "12/01/2021", label: "2", value: 2 },
{ date: "12/01/2000", label: "0", value: 1 },
{ date: "12/01/2023", label: "3", value: 12 },
],
};
},
mounted() {
const ctx = document.getElementById("graphique");
this.Chart = new Chart(ctx, {
type: "line",
data: {
datasets: [
{
label: "US Dates",
backgroundColor: "#fff",
borderWidth: 1,
pointBorderWidth: 1,
borderColor: "#f00",
data: this.transformedData,
fill: false,
borderColor: "red",
},
],
},
options: {
scales: {
x: {
type: "time",
time: {
unit: "month",
},
},
},
},
});
},
computed: {
transformedData() {
const d = this.finds.map((i) => {
return { x: new Date(i.date), y: i.value };
});
d.sort(function (a, b) {
return a.x - b.x;
});
return d;
},
},
methods: {
addFind: function () {
if (this.valueToAdd > 0 && this.labelToAdd !== "") {
this.finds.push({
date: this.dateToAdd,
value: this.valueToAdd,
label: this.labelToAdd,
});
this.finds.sort(function (a, b) {
return new Date(a.date) - new Date(b.date);
});
this.Chart.data.datasets[0].data = this.transformedData;
console.log(this.Chart.data.datasets[0].data);
this.Chart.update();
this.valueToAdd = 0;
this.dateToAdd = "";
this.labelToAdd = "";
}
},
},
};
</script>
So without using a computed method is it possible to do it simply by using the
import "chartjs-adapter-moment";
as Juand David mentioned. And don't forget to convert the string in Date.
Here is my solution :
<template>
<div class="container">
<div class="row">
<div class="col">
<canvas id="graphique"></canvas>
</div>
</div>
<div class="row">
<div class="col-xxs">
<h1>Mes catégories</h1>
<div v-for="(cat, index) in category" v-bind:key="index">
{{ cat.label }}
</div>
</div>
<div class="col">
<h1>Mes dépenses</h1>
<div v-for="(find, index) in finds" v-bind:key="index">
<input v-model="find.date" readonly />
<input v-model="find.value" readonly />
<input v-model="find.label" readonly />
</div>
<div>
<input v-model="dateToAdd" />
<input v-model="valueToAdd" />
<select v-model="labelToAdd">
<option
v-for="(cat, index) in category"
:value="cat.label"
v-bind:key="index"
>
{{ cat.label }}
</option>
</select>
<button #click="addFind">Add cost</button>
</div>
</div>
</div>
</div>
</template>
<script>
import Chart from "chart.js/auto";
import "chartjs-adapter-moment";
export default {
name: "PlanetChart",
data() {
return {
ctx: null,
Chart: null,
valueToAdd: null,
labelToAdd: null,
dateToAdd: null,
category: [
{ id: "salary", label: "Salaire" },
{ id: "fun", label: "Loisir" },
],
finds: [
{ date: new Date("12/01/2020"), label: "Dépense 1", value: 12 },
{ date: new Date("12/01/2021"), label: "Salariée", value: 2 },
{ date: new Date("12/01/2000"), label: "Ciné", value: 1 },
{ date: new Date("12/01/2023"), label: "Restaurant", value: 12 },
],
};
},
mounted() {
this.ctx = document.getElementById("graphique");
this.finds.sort(function (a, b) {
return a.date - b.date;
});
this.Chart = this.createChart(this.finds);
},
methods: {
createChart: function (dataToAdd) {
var toRet = new Chart(this.ctx, {
type: "line",
data: {
datasets: [
{
label: "My dataset",
fill: false,
lineTension: 0.1,
backgroundColor: "rgba(75,192,192,0.4)",
borderColor: "rgba(75,192,192,1)",
borderCapStyle: "butt",
borderDash: [],
borderDashOffset: 0.0,
borderJoinStyle: "miter",
pointBorderColor: "rgba(75,192,192,1)",
pointBackgroundColor: "#fff",
pointBorderWidth: 1,
pointHoverRadius: 5,
pointHoverBackgroundColor: "rgba(75,192,192,1)",
pointHoverBorderColor: "rgba(220,220,220,1)",
pointHoverBorderWidth: 2,
pointRadius: 5,
pointHitRadius: 10,
data: dataToAdd,
},
],
},
options: {
parsing: {
xAxisKey: "date",
yAxisKey: "value",
},
scales: {
x: {
type: "time",
time: {
unit: "month",
},
},
},
responsive: true,
maintainAspectRatio: false,
},
});
return toRet;
},
addCategory: function () {},
addFind: function () {
if (this.valueToAdd !== null && this.labelToAdd !== null) {
this.finds.push( { date: new Date("12/01/1990"), label: "Dépense nouvelle", value: 5 });
this.finds.sort(function (a, b) {
return new Date(a.date) - new Date(b.date);
});
this.Chart.data.datasets[0].data = this.finds;
// console.log(this.Chart.data.datasets);
this.Chart.update();
// this.Chart = this.createChart(this.finds);
this.valueToAdd = 0;
this.dateToAdd = "";
this.labelToAdd = "";
}
},
},
};
</script>
I'm trying to create an element in vue.js, so that when I update my cart it will show a warning with the item added/updated to cart. So if I add a new car, it would show that last car added.
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
]
to
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3},
{ name: 'Mustang', quantity: 1}
]
will show
<div>
You have 1 x Mustang in Cart
</div>
But if I update the quantity of a car that was already in the cart, it will show that last car updated.
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
]
to
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 4}
]
will show
<div>
You have 4 x Toyota in Cart
</div>
So far I made it work based in this answer
new Vue({
el: '#app',
data: {
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
]
}
});
Vue.component('car-component', {
props: ["car"],
data: function() {
return {
lastAdded:''
}
},
template: `
<div>
You have {{lastAdded.quantity}} x {{lastAdded.name}} in Cart
</div>`,
watch: {
car: {
handler: function(newValue) {
this.lastAdded = newValue;
},
deep: true
}
}
});
html
<script src="https://unpkg.com/vue#2.5.17/dist/vue.js"></script>
<body>
<div id="app">
<p>Added to Cart:</p>
<car-component :car="car" v-for="car in cars"></car-component>
</div>
</body>
The point is that now it just detects when a object is already in the cart and changes quantity, but not when there is a new car added. I tried to play with another watcher, but it didn't work. Thanks in advance!
hmm how would I do this?
seems to me we have an array of objects and we are tracking the most recently added or modified object. Sure.
So, I think I'd want to only track the recently modified object and render that.
first the html:
<div id="app">
<p>Added to Cart:</p>
<car-component :car="latestCar"></car-component>
</div>
and the vue instance:
new Vue({
el: '#app',
data: {
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
],
latestCar: {}
},
methods: {
updateLatestCar(car) {
this.latestCar = car;
//call this method from any other method where updates take place
//so if would be called from your addCar method and your updateCar method
//(which I assume exist even though they are not shown in your code)
}
}
});
Vue.component('car-component', {
props: ["car"],
data: function() {
return {
lastAdded:''
}
},
template: `
<div>
You have {{lastAdded.quantity}} x {{lastAdded.name}} in Cart
</div>`,
watch: {
car: {
handler: function(newValue) {
this.lastAdded = newValue;
},
deep: true
}
}
});
If you are modifying your array of objects via some method that is external to the Vue instance then that will require some additional thought.
But it seems like for this you'd have some methods in the Vue instance methods block like this:
addCar(car) {
this.cars.push(car);
this.updateLatestCar(car);
},
updateCar(index, car) {
this.cars[index] = car;
this.updateLatestCar(car);
}
You could pass the entire cars[] array to <car-component>, and allow the component to determine which element of cars[] to display a message about:
In car-component, add a prop (typed for safety) to hold the passed-in cars[]:
Vue.component('car-component', {
// ...
props: {
cars: Array
},
}
Add two data properties:
* `car` - the current car.
* `copyOfCars` - the last known copy of `cars[]`, used to determine which array element has changed. *Note: While watchers are provided both the old and new values of the watched property, the old value does not actually indicate the previous value for arrays of objects.*
Vue.component('car-component', {
//...
data() {
return {
car: {},
copyOfCars: undefined, // `undefined` because we don't need it to be reactive
};
},
}
Define a method (e.g., named findActiveCar) that determines which element in a given cars[] is most recently "active" (newly added or modified).
Vue.component('car-component', {
// ...
methods: {
/**
* Gets the newest/modified car from the given cars
*/
findActiveCar(newCars) {
if (!newCars || newCars.length === 0) return {};
let oldCars = this.copyOfCars;
// Assume the last item of `newCars` is the most recently active
let car = newCars[newCars.length - 1];
// Search `newCars` for a car that doesn't match its last copy in `oldCars`
if (oldCars) {
for (let i = 0; i < Math.min(newCars.length, oldCars.length); i++) {
if (newCars[i].name !== oldCars[i].name
|| newCars[i].quantity !== oldCars[i].quantity) {
car = newCars[i];
break;
}
}
}
this.copyOfCars = JSON.parse(JSON.stringify(newCars));
return car;
}
}
}
Define a watcher on the cars property that sets car to the new/modified item from findActiveCar().
Vue.component('car-component', {
// ...
watch: {
cars: {
handler(newCars) {
this.car = this.findActiveCar(newCars);
},
deep: true, // watch subproperties of array elements
immediate: true, // run watcher immediately on this.cars[]
}
},
}
Vue.component('car-component', {
props: {
cars: Array,
},
data() {
return {
car: {},
copyOfCars: undefined,
}
},
template: `<div>You have {{car.quantity}} x {{car.name}} in Cart</div>`,
watch: {
cars: {
handler(newCars) {
this.car = this.findActiveCar(newCars);
},
deep: true,
immediate: true,
}
},
methods: {
findActiveCar(newCars) {
if (!newCars || newCars.length === 0) return {};
let oldCars = this.copyOfCars;
let car = newCars[newCars.length - 1];
if (oldCars) {
for (let i = 0; i < Math.min(newCars.length, oldCars.length); i++) {
if (newCars[i].name !== oldCars[i].name
|| newCars[i].quantity !== oldCars[i].quantity) {
car = newCars[i];
break;
}
}
}
this.copyOfCars = JSON.parse(JSON.stringify(newCars));
return car;
}
}
});
new Vue({
el: '#app',
data: () => ({
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
]
}),
methods: {
addCar() {
this.cars.push({
name: 'Mustang', quantity: 1
})
}
}
})
<script src="https://unpkg.com/vue#2.5.17"></script>
<div id="app">
<h1>Added to Cart</h1>
<button #click="addCar">Add car</button>
<ul>
<li v-for="(car, index) in cars" :key="car.name + index">
<span>{{car.name}} ({{car.quantity}})</span>
<button #click="car.quantity++">+</button>
</li>
</ul>
<car-component :cars="cars" />
</div>
I want to remove category_id from {{ columnValue }}, but what is the best way to to that, because i need category_id in the first part ?
<table>
<tr>
<td v-for="columnValue, column in record">
<template v-if="editing.id === record.id && isUpdatable(column)">
<template v-if="columnValue === record.category_id">
<select class="form-control" v-model="editing.form[column]">
<option v-for="column in response.joins">
{{ column.category }} {{ column.id }}
</option>
</select>
</template>
<template v-else="">
<div class="form-group">
<input class="form-control" type="text" v-model= "editing.form[column]">
<span class="helper-block" v-if="editing.errors[column]">
<strong>{{ editing.errors[column][0]}}</strong>
</span>
</div>
</template>
</template>
<template v-else="">
{{ columnValue }} // REMOVE category_id here!
</template>
</td>
</tr>
</table>
And the view (its the number under group i want to remove):
The DataTable view
The script:
<script>
import queryString from 'query-string'
export default {
props: ['endpoint'],
data () {
return {
response: {
table: null,
columntype: [],
records: [],
joins: [],
displayable: [],
updatable: [],
allow: {},
},
sort: {
key: 'id',
order: 'asc'
},
limit: 50,
quickSearchQuery : '',
editing: {
id: null,
form: {},
errors: []
},
search: {
value: '',
operator: 'equals',
column: 'id'
},
creating: {
active: false,
form: {},
errors: []
},
selected: []
}
},
filters: {
removeCategoryId: function (value) {
if (!value) return ''
delete value.category_id
return value
}
},
computed: {
filteredRecords () {
let data = this.response.records
data = data.filter((row) => {
return Object.keys(row).some((key) => {
return String(row[key]).toLowerCase().indexOf(this.quickSearchQuery.toLowerCase()) > -1
})
})
if (this.sort.key) {
data = _.orderBy(data, (i) => {
let value = i[this.sort.key]
if (!isNaN(parseFloat(value)) && isFinite(value)) {
return parseFloat(value)
}
return String(i[this.sort.key]).toLowerCase()
}, this.sort.order)
}
return data
},
canSelectItems () {
return this.filteredRecords.length <=500
}
},
methods: {
getRecords () {
return axios.get(`${this.endpoint}?${this.getQueryParameters()}`).then((response) => {
this.response = response.data.data
})
},
getQueryParameters () {
return queryString.stringify({
limit: this.limit,
...this.search
})
},
sortBy (column){
this.sort.key = column
this.sort.order = this.sort.order == 'asc' ? 'desc' : 'asc'
},
edit (record) {
this.editing.errors = []
this.editing.id = record.id
this.editing.form = _.pick(record, this.response.updatable)
},
isUpdatable (column) {
return this.response.updatable.includes(column)
},
toggleSelectAll () {
if (this.selected.length > 0) {
this.selected = []
return
}
this.selected = _.map(this.filteredRecords, 'id')
},
update () {
axios.patch(`${this.endpoint}/${this.editing.id}`, this.editing.form).then(() => {
this.getRecords().then(() => {
this.editing.id = null
this.editing.form = {}
})
}).catch((error) => {
if (error.response.status === 422) {
this.editing.errors = error.response.data.errors
}
})
},
store () {
axios.post(`${this.endpoint}`, this.creating.form).then(() => {
this.getRecords().then(() => {
this.creating.active = false
this.creating.form = {}
this.creating.errors = []
})
}).catch((error) => {
if (error.response.status === 422) {
this.creating.errors = error.response.data.errors
}
})
},
destroy (record) {
if (!window.confirm(`Are you sure you want to delete this?`)) {
return
}
axios.delete(`${this.endpoint}/${record}`).then(() => {
this.selected = []
this.getRecords()
})
}
},
mounted () {
this.getRecords()
},
}
</script>
And here is the json:
records: [
{
id: 5,
name: "Svineskank",
price: "67.86",
category_id: 1,
category: "Flæskekød",
visible: 1,
created_at: "2017-09-25 23:17:23"
},
{
id: 56,
name: "Brisler vv",
price: "180.91",
category_id: 3,
category: "Kalvekød",
visible: 0,
created_at: "2017-09-25 23:17:23"
},
{
id: 185,
name: "Mexico griller 500 gram",
price: "35.64",
category_id: 8,
category: "Pølser",
visible: 0,
created_at: "2017-09-25 23:17:23"
},
{
id: 188,
name: "Leverpostej 250 gr.",
price: "14.25",
category_id: 9,
category: "Pålæg",
visible: 1,
created_at: "2017-09-25 23:17:23"
},
}]
.. and so on......
I would recommend using a filter in Vue to remove the property, such as:
new Vue({
// ...
filters: {
removeCategoryId: function (value) {
if (!value) return ''
delete value.category_id
return value
}
}
})
An then use this in your template:
{{ columnValue | removeCategoryId }}
Update: I misunderstood the scope of the loop. This works, and I verified on jsfiddle: https://jsfiddle.net/spLxew15/1/
<td v-for="columnValue, column in record" v-if="column != 'category_id'">
Suppose I'm trying to make a simple questionnaire, where the user answers a list of questions.
new Vue(
{
el: "#app",
data:
{
questions:
[
{
id: 1,
name: "What is your favorite color?",
selectedId: 2,
choices:
[
{ id: 1, name: "red" },
{ id: 2, name: "green" },
{ id: 3, name: "blue" },
]
},
...
]
}
});
How do I go about making a question component with two-way binding. That is, if the user swaps their favorite color from green to red, by clicking on the respective input, the selectedId will automatically update. I'm not very clear on how v-model works within a component. Does it only have access to the components data? Also, I don't understand the difference between props/data.
There are lots of ways you can approach this, here's my attempt:
let id = 0;
Vue.component('question', {
template: '#question',
props: ['question'],
data() {
return {
radioGroup: `question-${id++}`,
};
},
methods: {
onChange(choice) {
this.question.selectedId = choice.id;
},
isChoiceSelected(choice) {
return this.question.selectedId === choice.id;
},
},
});
new Vue({
el: '#app',
data: {
questions: [
{
title: 'What is your favorite color?',
selectedId: null,
choices: [
{ id: 1, text: 'Red' },
{ id: 2, text: 'Green' },
{ id: 3, text: 'Blue' },
],
},
{
title: 'What is your favorite food?',
selectedId: null,
choices: [
{ id: 1, text: 'Beans' },
{ id: 2, text: 'Pizza' },
{ id: 3, text: 'Something else' },
],
},
],
},
});
.question {
margin: 20px 0;
}
<script src="https://rawgit.com/yyx990803/vue/master/dist/vue.js"></script>
<div id="app">
<question v-for="question of questions" :question="question"></question>
</div>
<template id="question">
<div class="question">
<div>{{ question.title }}</div>
<div v-for="choice of question.choices">
<input type="radio" :name="radioGroup" :checked="isChoiceSelected(choice)" #change="onChange(choice)"> {{ choice.text }}
</div>
<div>selectedId: {{ question.selectedId }}</div>
</div>
</template>