vue-test-utils not updating component after click - vue.js

I am using vue-test-utils/mocha-webpack/expect to test the following component:
<template>
<div>
<div id="agent-customer-container" class="top-tabs" v-if="agentName || customerName || miniBasket">
<div v-if="agentName || customerName">
<div class="text-primary bg-white top-tabs__item px-5" id="customer-slot" v-if="customerName" #click="customerDetailShow = !customerDetailShow">
<span>
<i class="fa fa-user-circle fa-2x mr-3"></i>
{{ customerName }}
</span>
<i :class="'fa fa-caret-'+(customerDetailShow?'up':'down')" id="customer-caret"></i>
</div>
<div class="text-primary top-tabs__item top-tabs__item--light px-5" id="agent-slot" v-if="agentName">
{{ agentName }}
</div>
</div>
<div class="top-tabs__mini-basket text-primary px-5" v-if="miniBasket && !loading">
<span v-if="firstEnquiry !== null && firstEnquiry.cruise !== null && firstEnquiry.cruise !== undefined">
<span id="first-enquiry-cruise-summary">{{ firstEnquiry.cruise.name }} ({{ firstEnquiry.cruise.code }})</span>
- <span class="text-semibold">£XX.XX</span>
</span>
<span id="toggle__mini-basket" :class="[{'fa-counter': (enquiryCount > 0), 'mini-basket__count' : (enquiryCount > 0)}, 'fa-stack v-top ml-3 cursor-pointer']" #click="toggleMiniBasket" :data-count="enquiryCount">
<i class="fas fa-circle fa-stack-2x"></i>
<i class="fas fa-shopping-cart fa-stack-1x fa-inverse"></i>
</span>
</div>
<div class="customer-detail bord-rgt" v-show="customerDetailShow">
<div class="row p-5 bord-btm" v-for="i in 2" :key="i">
<div class="col-sm-6">
<p>Sydney to Hong Kong</p>
<p>Text<br>Text</p>
</div>
<div class="col-sm-6">
<p><strong>Grade data</strong>
<br>
Lorem ipsum
</p>
</div>
</div>
<div class="row p-5 bg-primary customer-detail__footer">
<div class="row">
<div class="col-sm-6">
Text
</div>
<div class="col-sm-6 text-right text-bold">
Total £1000
</div>
</div>
</div>
</div>
<div class="mini-basket-detail bord-lft" v-if="miniBasketDetailShow">
<div v-if="myEnquiry !== false && myEnquiry !== undefined && myEnquiry['cruise_enquiries'] !== undefined && firstEnquiry !== null && firstEnquiry.cruise !== null">
<div class="row p-5 bord-btm" v-for="(cruiseEnquiry, cruiseEnquiryIndex) in myEnquiry['cruise_enquiries']" :key="cruiseEnquiry.id">
<div class="col-sm-12 text-sm" v-if="cruiseEnquiry.cruise !== undefined">
<cruise-enquiry
:cruise-name="cruiseEnquiry.cruise.name"
:cruise-code="cruiseEnquiry.cruise.code"
:ship-name="cruiseEnquiry.cruise.ship.name"
:departure-port-name="cruiseEnquiry.cruise.departure_port.name"
:departure-date="new Date(cruiseEnquiry.cruise.departure_date)"
:duration="cruiseEnquiry.cruise.duration"
:cruise-enquiry-cabins="cruiseEnquiry.cruise_cabin_cruise_enquiry"/>
</div>
</div>
</div>
<div v-else>
<div class="row p-5 bord-btm">
<div class="col-sm-12 text-sm">
<p>Your itinerary is currently empty. Please start by selecting a cruise.</p>
</div>
</div>
</div>
<div id="toggle__mini-basket--close" class="row p-3 bg-primary customer-detail__footer text-center cursor-pointer d-block" #click="miniBasketDetailShow = false">
<caret :up="true" additional-classes="fa-2x"></caret>
</div>
</div>
<span class="float-right"></span>
</div>
</div>
</template>
<script>
import Caret from "./Caret";
import CruiseEnquiry from "./enquiry-panel/CruiseEnquiry";
export default {
name: "EnquiryPanel",
components: {
CruiseEnquiry,
Caret,
},
props: {
loading: {
type: Boolean,
required: false,
default: false,
},
customerName: {
type: String,
required: false,
default: ''
},
agentName: {
type: String,
required: false,
default: ''
},
myEnquiry: {
type: Object|Boolean,
required: false,
default: false,
},
miniBasket: {
type: Boolean,
required: false,
default: false,
}
},
data() {
return {
customerDetailShow: false,
miniBasketDetailShow: false,
firstEnquiry: null,
enquiryCount: 0,
miniBasketCabinNumber: 0,
}
},
watch: {
myEnquiry: {
deep: true,
immediate: true,
handler(newValue) {
if (this.myEnquiry !== false && this.myEnquiry['cruise_enquiries'] !== undefined && this.myEnquiry['cruise_enquiries'][0] !== undefined) {
this.firstEnquiry = this.myEnquiry['cruise_enquiries'][0];
for (let i = 0; i <= (this.myEnquiry['cruise_enquiries'].length - 1); i++) {
if (this.myEnquiry['cruise_enquiries'][i].cruise !== undefined && this.myEnquiry['cruise_enquiries'][i].cruise !== null) {
this.setEnquiryCount(this.enquiryCount + 1);
}
}
}
}
}
},
methods: {
setEnquiryCount(count) {
this.enquiryCount = count;
},
toggleMiniBasket() {
this.miniBasketDetailShow != this.miniBasketDetailShow;
}
}
}
</script>
When I click on #toggle__mini-basket I expect miniBasketDetailShow to be set to true (defaults to false on mount) and the basket should show (using `v-if="miniBasketDetailsShow" on the element).
This is my test for it:
import {createLocalVue, shallowMount, mount} from '#vue/test-utils';
import expect from 'expect';
import EnquiryPanel from '~/components/EnquiryPanel';
import VueMoment from "vue-moment";
describe('EnquiryPanel', () => {
let localVue;
beforeEach(() => {
localVue = createLocalVue();
localVue.use(VueMoment);
});
// If a user clicks on the mini basket icon and the basket panel is not open, the basket panel should open
it('opens mini basket if user clicks icon and basket is not open', () => {
let component = shallowMount(EnquiryPanel, {
localVue,
propsData: {
miniBasket: true
}
});
expect(component.vm.miniBasket).toBe(true);
expect(component.vm.miniBasketDetailShow).toBe(false);
component.find('#toggle__mini-basket').trigger('click');
component.vm.$forceUpdate();
expect(component.vm.miniBasketDetailShow).toBe(true);
});
});
This is a simple test so I'm unsure what is going wrong. The result is as follows:
3) EnquiryPanel
opens mini basket if user clicks icon and basket is not open:
Error: expect(received).toBe(expected) // Object.is equality
Expected: true
Received: false
at Context.eval (webpack-internal:///./tests/Javascript/EnquiryPanel.spec.js:62:86)
I would expect this test to be passing. I have tried making the shallowMount be async with sync: false. I have also tried adding in await component.vm.$nextTick but no luck. Any help would be appreciated.

The error was with the toggleMiniBasket method:
this.miniBasketDetailShow != this.miniBasketDetailShow;
should be
this.miniBasketDetailShow = !this.miniBasketDetailShow;

Related

How to rotate single chevron on accordion in vue?

I am using vue chevron in accordion inside for loop. When I am clicking a single dropdown, all other chevrons are rotating. How to rotate the specific chevron for the clicked dropdown in vue ? I am using vue chevron package from https://ispal.github.io/vue-chevron/ . My dropdown looks like this -
My vue code:
<div class="accordion" id="accordionExample">
<div
v-for="(data, index) in topicList"
:key="index"
class="card"
>
<div class="card_header" id="headingOne">
<h2 class="mb-0">
<div
class="btn btn-topic example text-left collapsed"
#click="toggle"
type="button"
data-toggle="collapse"
:data-target="'#collapseOne' + index"
aria-expanded="true"
aria-controls="collapseOne"
>
<vue-chevron
:point-down="pointDown"
:duration="duration"
:thickness="thickness"
:angle="angle"
:round-edges="roundEdges"
/>
{{ data.title }}
</div>
</h2>
</div>
<div
:id="'collapseOne' + index"
class="collapse"
aria-labelledby="headingOne"
data-parent="#accordionExample"
>
<div class="card-body">
<ul
v-for="(data, index) in topicList[index].lessons"
:key="index"
#click="getContent(data.lessonId)"
class="list-group py-1"
>
<li class="list-group-item list-style">
{{ data.title }}
</li>
</ul>
</div>
</div>
</div>
</div>
<script>
import VueChevron from "vue-chevron"
export default{
components:{ VueChevron }
data(){
return{
pointDown: false,
thickness: 8,
duration: 500,
angle: 40,
roundEdges: true,
easing: function n(t) {
return t;
}
}
methods: {
toggle() {
this.pointDown = !this.pointDown;
},
}
}
courseAccess() {
this.$axios
.get(this.$api + "api/v1/courses/" + this.courseId, {
headers: {
Authorization: "Bearer " + localStorage.getItem("_utoken"),
},
})
.then((response) => {
// this.topicList = response.data.data.topics.map((element) => {
// return { ...element, pointDown: true };
// });
this.topicList = response.data.data.topics
this.firstTopic = response.data.data.topics[0].lessons[0].lessonId;
this.getFirstContent(this.firstTopic);
this.loadTopics = true;
});
},
All vue-chevron are binding there point-down prop to the same boolean. You could try to use an array of boolean instead and change your toggle method.
<div class="accordion" id="accordionExample">
<div
v-for="(data, index) in topicList"
:key="index"
class="card"
>
<div class="card_header" id="headingOne">
<h2 class="mb-0">
<div
class="btn btn-topic example text-left collapsed"
#click="toggle(index)"
type="button"
data-toggle="collapse"
:data-target="'#collapseOne' + index"
aria-expanded="true"
aria-controls="collapseOne"
>
<vue-chevron
:point-down="pointDown[index]"
:duration="duration"
:thickness="thickness"
:angle="angle"
:round-edges="roundEdges"
/>
{{ data.title }}
</div>
</h2>
</div>
<div
:id="'collapseOne' + index"
class="collapse"
aria-labelledby="headingOne"
data-parent="#accordionExample"
>
<div class="card-body">
<ul
v-for="(data, index) in topicList[index].lessons"
:key="index"
#click="getContent(data.lessonId)"
class="list-group py-1"
>
<li class="list-group-item list-style">
{{ data.title }}
</li>
</ul>
</div>
</div>
</div>
</div>
<script>
import VueChevron from "vue-chevron"
export default{
components:{ VueChevron }
data(){
return{
pointDown: [],
thickness: 8,
duration: 500,
angle: 40,
roundEdges: true,
easing: function n(t) {
return t;
}
}
methods: {
toggle(index) {
this.pointDown[index] = !this.pointDown[index];
},
}
}
EDIT
The problem discussed in the comments could be caused by reactivity issues. To solve these we are trying to initialize pointDown with an array of the correct size.
courseAccess() {
this.$axios
.get(this.$api + "api/v1/courses/" + this.courseId, {
headers: {
Authorization: "Bearer " + localStorage.getItem("_utoken"),
},
})
.then((response) => {
// this.topicList = response.data.data.topics.map((element) => {
// return { ...element, pointDown: true };
// });
this.topicList = response.data.data.topics;
this.pointDown = new Array(this.topicList.length).fill(false);
this.firstTopic = response.data.data.topics[0].lessons[0].lessonId;
this.getFirstContent(this.firstTopic);
this.loadTopics = true;
});
},

Vue-formulate - Group item collapsible / toggle collapse

Is there a possibility to make group item collapsible?
<FormulateInput type="group" name="employments" :repeatable="true" label="Employments"
add-label="+ Add Employment" #default="groupProps">
<!-- Clickable area -->
<div class="group text-sm font-semibold py-2 cursor-pointer relative" #click="groupProps.showForm">
....
</div>
<!-- Nested form: must be collapsible accordion -->
<div class="nested-form" v-show="groupProps.showForm">
....
</div>
</FormulateInput>
I thought to add showForm property to the group context.
For this I need to do Custom input types or is there some other way?
If anyone has any other ideas?
Thanks
I figured it out with the gist of #jpschroeder.
CollapsableGroupItem.vue:
<template>
<div class="group-item" :data-is-open="itemId === showIndex">
<div class="group group-item-title text-sm font-semibold py-2 cursor-pointer relative hover:text-blue-400" #click="toggleBody">
<slot name="title" v-bind="groupItem">#{{ context.index }}</slot>
</div>
<div class="group-item-body" v-show="itemId === showIndex">
<slot name="body">
<FormulateInput type="pelleditor" name="description" label="Description"/>
</slot>
</div>
</div>
</template>
<script>
export default {
name: "CollapsableGroupItem",
props: {
context: {
type: Object,
required: true,
},
showIndex: {
type: [Number, String],
required: true,
},
groupItem: {
type: Object,
required: true,
},
},
data () {
return {
itemId: this.context.name + this.context.index
}
},
created: function () {
// show current item
this.$emit("open", this.itemId);
},
methods: {
toggleBody() {
if (this.itemId === this.showIndex) {
// dont show anything
this.$emit("open", -1);
} else {
// show this one
this.$emit("open", this.itemId);
}
},
}
};
FormTemplate.vue:
<CollapsableGroupItem
:context="context"
:show-index="showIndex"
:group-item="educations[context.index]"
#open="showIndex = $event"
>
<template v-slot:title="education">
<span v-if="education.institution || education.degree"
>
{{ education.institution }}
<span v-if="education.institution && education.degree">at</span>
{{ education.degree }}
</span>
...
</template>
<template v-slot:body>
...
</template>
</CollapsableGroupItem>
Maybe it will help someone else or will be useful 😀

Data sent from one Vue component to another remains reactive

I have an input-component which has a form which collects start and finish times, job number and a select option.
This is attached to a data property with v-model.
This is then emitted with Event.$emit('addedData', this.hours)
In the display-component the Event.$on takes this data and checks an attribute and based on the check adds it to another data property (array) with this.todays_hours.push().
The template then displays this reactively using v-for in the template.
To this point all works fine. However when I then attempt to add another line of hours the hours already displayed change reactively with the input.
As my input-component also posts to a database with axios if I reload the page all is displayed correctly.
input-component
<template>
<div>
<div class="row">
<div class="col-2">
<input hidden="" v-model="hours.day">
</div>
<div class="col-2" >
<input type="time" v-model="hours.start">
</div>
<div class="col-2" >
<input type="time" v-model="hours.finish">
</div>
<div class="col-2" >
<input type="number" v-model="hours.job_number">
</div>
<div class="col-2" >
<select v-model="hours.climbing">
<option selected="selected" value="0">No</option>
<option value="1">Yes</option>
</select>
</div>
<div class="col-2" >
<button #click="onSave" class="btn-success btn-sm">Save</button>
</div>
</div>
<hr>
</div>
</template>
<script>
export default {
name: 'InputHoursComponent',
props: ['employeeId', 'dayCheck', 'weekEnding'],
data() {
return {
hours: {
start: "",
finish: "",
job_number: "",
climbing: 0,
day: this.dayCheck
},
climbing_select: ['No', 'Yes'],
}
},
methods: {
onSave()
{
axios.post('/payroll', {
employee_id: this.employeeId,
week_ending: this.weekEnding,
start: this.hours.start,
finish: this.hours.finish,
job_number: this.hours.job_number,
climbing: this.hours.climbing,
day: this.dayCheck
})
.then(response => {})
.catch(e => {this.errors.push(e)});
let data = this.normalizeProp(this.hours, s, true)
Event.$emit('onAddedEntry', data);
console.log("passed data:", this.hours);
}
}
}
</script>
display-component
<template>
<div>
<div v-for="item in todays_hours">
<div class="row">
<div class="col-2">
<div hidden="" ></div>
</div>
<div class="col-2" >
<div v-text="item.start"></div>
</div>
<div class="col-2" >
<div v-text="item.finish"></div>
</div>
<div class="col-2" >
<div v-text="item.job_number"></div>
</div>
<div class="col-2" >
<div v-text="(item.climbing)?'Yes':'No'"></div>
</div>
<div class="col-2" >
<button #click="onEdit" class="btn-warning btn-sm mb-1">Edit</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'DisplayHoursComponent',
props: ['dayCheck', 'hoursWorked'],
data() {
return {
hours_list: this.hoursWorked,
todays_hours: []
}
},
mounted() {
for (var i = 0; i < this.hours_list.length; i++) {
if (this.hours_list[i].day === this.dayCheck) {
this.todays_hours.push(this.hours_list[i])
}
}
Event.$on('onAddedEntry', (check) => {
if(check.day === this.dayCheck){
this.todays_hours.push(check);
}
})
},
methods: {
onEdit()
{
}
}
}
</script>
Can someone please help me?
Try pushing a copy of check instead of check itself.
Event.$on('onAddedEntry', (check) => {
if(check.day === this.dayCheck){
this.todays_hours.push({...check});
}
})
You could also make the copy when you emit the event instead.

To do List with Vue js 2 using component or v-model

Hello I have here one code with two "todo list" implementations in Vuejs but I have a problem.
1 Using a vue component i am getting a waring about how to use the parent variable.
2 Doing it on the main function I cannot keep the old value for the discard implementation.
please find the working code
Running! todo list in codepen
Vue.component('ntodo-item', {
template: '\
<transition name="fade">\
<div id="if" class="row" v-if="edit">\
<div class="col-md-7">\
<input class="form-control" v-model="title">\
</div>\
<div id="sssss" class="col-md-5">\
<button class="btn btn-danger roundButton" v-on:click="$emit(\'edit\')">Discard</button>\
<button class="btn btn-success roundButton" v-on:click="updateValue">Save</i></button>\
</div>\
</div>\
<div id="else" class="row" v-else>\
<div class="col-md-7">\
{{ title }}\
</div>\
<div id="ssaaas" class="col-md-5">\
<button class="btn btn-danger roundButton" v-on:click="$emit(\'remove\')">Remove</button>\
<button id="aaa" class="btn btn-default roundButton" v-on:click="$emit(\'edit\')">Edit</button>\
</div>\
</div>\
</transition>\
',
props: [
'title' ,
'edit'
],
methods: {
updateValue: function () {
this.$emit('input', this.title);
}
}
})
var app14 = new Vue({
el: '#app-14',
data: {
newTodoText: '',
newTodoText2: '',
todos: [
{
id: 1,
title: 'Do the dishes',
edit:0
},
{
id: 2,
title: 'Take out the trash',
edit:0
},
{
id: 3,
title: 'Mow the lawn',
edit:0
}
],
todos2: [
{
id: 1,
title: 'Do the dishes',
edit:0
},
{
id: 2,
title: 'Take out the trash',
edit:0
},
{
id: 3,
title: 'Mow the lawn',
edit:0
}
],
nextTodoId: 4,
nextTodoId2: 4
},
methods: {
addNewTodo: function () {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText,
edit:0
})
this.newTodoText = ''
this.todos = _.orderBy(this.todos, 'id', 'desc');
},
editTodo: function (item){
// console.log(item.title)
item.edit^= 1
},
updateValue: function (item, newValue){
item.title=newValue
item.edit^= 1
},
addNewTodo2: function () {
this.todos2.push({
id: this.nextTodoId2++,
title: this.newTodoText2,
edit:0
})
this.newTodoText2 = ''
this.todos2 = _.orderBy(this.todos2, 'id', 'desc');
},
editTodo2: function (item){
console.log(item.title)
item.edit^= 1
},
deleteTodo2: function (item){
this.todos2.splice(item.id, 1);
},
updateValue2: function(text){
console.log(text);
}
}
})
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s, transform 0.3s;
transform-origin: left center;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
transform: scale(0.5);
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<div class="col-md-12">
<div class="graybox">
<h5>app14</h5>
<div id="app-14">
`enter code here`<div class="row">
<div class="col-md-6">
<h5> todo list using "ntodo-item" component</h5>
<p>This one show me a warning because the child cannot edit the va passed by the parent but it is working and spected</p>
<input class="form-control"
v-model="newTodoText"
v-on:keyup.enter="addNewTodo"
placeholder="Add a todo"
>
<hr>
<ul>
<li
is="ntodo-item"
v-for="(todo, index) in todos"
v-bind:key="todo.id"
v-bind:title="todo.title"
v-bind:edit="todo.edit"
v-on:input="updateValue(todo, $event)"
v-on:remove="todos.splice(index, 1)"
v-on:edit="editTodo(todo)"
></li>
</ul>
</div>
<div class="col-md-6">
<h5> todo list update</h5>
<p> This one is working without any warn but I dont know how to discard changes. I dont want to create a temp var because I want to be able to edit all of them at the same time. </p>
<input v-model="newTodoText2"
v-on:keyup.enter="addNewTodo2"
placeholder="Add a todo"
class="form-control"
>
<hr>
<ul>
<transition-group name="fade" >
<li v-for="(todo2, index) in todos2":key="todo2.id">
<div id="if" class="row" v-if="todo2.edit">
<div class="col-md-7">
<input class="form-control" ref="todo2" v-model="todo2.title">
</div>
<div id="sssss" class="col-md-5">
<button class="btn btn-success roundButton" v-on:click="editTodo2(todo2)">ok</button>
</div>
</div>
<div id="else" class="row" v-else>
<div class="col-md-7">
{{todo2.title}}
</div>
<div id="ssaaas" class="col-md-5">
<button class="btn btn-danger roundButton" v-on:click="todos2.splice(index, 1)">Remove</button>
<button id="aaa" class="btn btn-default roundButton" v-on:click="editTodo2(todo2)">Edit</button>
</div>
</div>
</li>
</transition>
</ul>
</div>
</div>
</div>
</div>
</div>
.
Echoing my comment:
Create a local variable copy of your title prop and emit that variable's changes on edit. If they discard the edit just reset the local variable to the value of the title prop. Working example on CodeSandbox here.
Todo Item Component
<button class="btn btn-danger roundButton" #click="discardEdit">Discard</button>
...
data() {
return {
// our local copy
localTitle: null,
};
},
mounted() {
this.localTitle = this.title;
},
methods: {
updateValue: function() {
this.$emit("input", this.localTitle);
},
discardEdit: function() {
// just set local back to title prop value
this.localTitle = this.title;
this.$emit('edit');
},
}

Vue js event not picked up

I have just started experimenting with vue js and I am building a checkout form with it. I am also using Symfony 31 for the project. On the checkout/signup page I have an embedded collection of forms representing order items (each are subscription to a type of product). You can select multiple items by ticking a checkbox. You can also change the quantity. Unfortunately I cannot manage to pass the quantity update to the Vue instance. The entries are registered on render with the quantity 1, and if I change the quantity and then select the item, the price is calculated correctly, but the app registers this as a new entity. The binding with the quantity is not working. I will also need to add a similar field called frequency and I know I will have the same problem. Help?
Here is the js fiddle: https://jsfiddle.net/wavsu8xm/
Javascript:
var bus = new Vue();
var entriesComponent = Vue.component('entries', {
template: '#entries',
props: {
entries: [Array, Object],
selected: Array,
addons: Array,
frequencies: [Array, Object],
},
watch: {
selected: function(val, oldVal) {
bus.$emit('selected-changed', val);
},
}
});
new Vue({
el: '#app',
data: {
entries: [],
selected: [],
addons: [],
frequencies: [],
paymentConfig: {
advance: 25,
firstweek: 25,
ondelivery: 50,
},
weeks: 12,
},
components: {
'entriesComponent': entriesComponent,
},
created: function() {
// store this to use with Vue.set
var temp = this;
bus.$on('selected-changed', function(selected) {
// vm.$set deprecated
Vue.set(temp, 'selected', selected);
});
},
computed: {
totalAdvance: function() {
return (this.paymentConfig.advance * this.total) / 100;
},
totalFirstWeek: {
get: function() {
return (this.paymentConfig.firstweek * this.total) / 100;
},
},
onDeliveryPayment: {
get: function() {
return (this.paymentConfig.ondelivery * this.total) / (this.weeks * 100);
}
},
total: {
get: function() {
var sum = 0;
var weeks = this.weeks;
this.selected.forEach(function(item) {
sum += weeks * item.itemPrice * item.quantity;
});
console.log(sum);
return sum;
}
}
}
});
Template:
<section class="content">
<div class="row" id="app">
<div class="col-md-8">
<div class="box box-primary">
<div class="box-body">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label class="control-label required">Items</label>
<div class="col-md-12">
<entries :entries="{ 0 : { shareSize : 'Small', quantity : '1', itemPrice : '24', frequency : '' }, 1 : { shareSize : 'Medium', quantity : '1', itemPrice : '35', frequency : '' }, 2 : { shareSize : 'Large', quantity : '1', itemPrice : '46', frequency : '' } }"
:selected="selected"></entries>
<!-- component template -->
<template id="entries">
<div class="col-md-12">
<div class="form-group" v-for="(entry, key) in entries" v-bind:entry="entry">
<div class="form-group col-md-12">
<div class="col-md-12">
<div class="col-md-4">
<input type="checkbox" v-bind:value="entry" v-model="selected">
</div>
<div class="col-md-4">{{entry.shareSize}}</div>
<div class="col-md-4">{{'$ ' + Number(entry.itemPrice).toFixed(2) }}</div>
</div>
<div class="form-group col-md-12">
<div class="col-md-6">
<input type="number" v-model="entry.quantity" :value="entry.quantity" />
</div>
</div>
</div>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="box box-info">
<div class="box-body" style="padding:15px;">
<div class="container-fluid">
<div class="form-group">
<div class="control-label">
<label>Summary</label>
</div>
<div class="form-control" v-for="item in selected">
<span class="pull-left small-box-footer">{{ item.shareSize }}</span>
<span class="pull-right">{{ item.quantity + ' x $ ' + (item.itemPrice*item.quantity).toFixed(2)}}</span>
</div>
<div class="control-label">
<label>Payment plan</label>
</div>
<div class="col-md-12">
{{ '$ ' + totalAdvance.toFixed(2) }} - advance
</div>
<div class="col-md-12">
{{ '$ ' + totalFirstWeek.toFixed(2) }} - first week
</div>
<div class="col-md-12">
{{ '$ ' + onDeliveryPayment.toFixed(2) }}/ week on each of the {{ weeks }} weeks of the subscription
</div>
<div class="col-md-12 row">
<div class="control-label"><strong><span class="pull-left">Total</span><span class="pull-right">{{ '$ ' + total.toFixed(2) }}</span></strong></div>
</div>
<div class="col-md-12 row">
<div class="title"><strong><span class="pull-left">Total due now</span><span class="pull-right">{{ '$ ' + totalAdvance.toFixed(2) }}</span></strong></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
You are mutating your props directly which will get overwritten when the parent component re-renders, so you need to make a copy of them inside your component, which can be done inside the created method of your component:
created: function() {
// copy props to data
this.entriesCopy = this.entries;
this.selectedCopy = this.selected;
},
data: function() {
return{
entriesCopy: [],
selectedCopy: []
}
}
Now you just need to update your watcher:
watch: {
selectedCopy: function(val, oldVal) {
bus.$emit('selected-changed', val);
}
}
And your template:
//...
div class="form-group" v-for="(entry, key) in entriesCopy" v-bind:entry="entry">
//...
<input type="checkbox" v-bind:value="entry" v-model="selectedCopy">
to reflect the changes.
Here's the updated jsfiddle: https://jsfiddle.net/5pyw74h9/