Vuejs cannot push to nested array - vue.js

First question on stack overflow, I apologize if I am asking in the wrong place. New to Vue and been trying for several days to finish a small project. I've simplified it but here it is. I want to be able to enter as many customer names as I need, and then be able to add as many customer's childrens names as I need. So an array of customers with a nested array of children. I can get the customers to add, but I cannot figure out why I cannot push a new element into the children's array. Here is my code.
new Vue ({
el: '#app',
data: function () {
return {
formdata: [],
fields: [{
name: '',
childs: [{
cname: '',
}]
}],
};
},
methods: {
addRow: function() {
this.fields.push({
name: "",
childs: [{
cname:'',
}],
});
},
addChild: function() {
this.child.push({
});
},
},
});
And my html
<div id="app">
<button class="btn btn-success" #click="addRow">Add</button>
<div v-for="field in fields" >
<div class="input-group">
<input type="text" placeholder="Name" class="form-control" v-model="field.name">
</div>
<div v-for="child in field.childs" >
<div class="input-group">
<input type="text" placeholder="Child" class="form-control" v-model="child.cname">
<div class="input-group-btn">
<button class="btn btn-success" #click="addChild">Add</button>
</div>
</div>
</div>
</div>
</div>
Again, the problem is that i can push a new "name" to the fields array, but i cannot push a new child into the fields child array. I have been trying every permutation of child childs field fields etc, trying to find how to reference that array. Any help would be greatly appreciated!

In your addChild method, you need to be accessing the childs property of the relevant field. Currently, you are trying to access the childs property of the Vue instance, which doesn't exist.
You should pass the relevant field to the addChild method like so:
<button class="btn btn-success" #click="addChild(field)">Add</button>
And then update your addChild method to reference the childs array of the field passed in:
addChild: function(field) {
field.childs.push({});
}

Related

Use Vue.js Prop to populate vForm data?

I am still fairly new to using Laravel and Vue.js and have been having some problems when trying to submit data to be used in a modal form.
overview.vue is displaying some information and triggering the modal component
My modal element:
<EditTeamModal :existing="existing_team"/>
In my template I am triggering the following function
#click="getExistingTeam(membership.team)"
Which triggers the following method:
getExistingTeam: function (team) {
this.existing_team = team;
this.$bvModal.show('edit_team');
},
EditTeamModal.vue
In my modal component I have several inputs like the one following:
<div class="form-group row">
<label for="name" class="col-md-4 col-form-label text-md-right">{{ $t('team.name') }} <span class="text-danger">*</span></label>
<div class="col-md-8">
<input type="text" v-model="form.name" :class="{ 'is-invalid': form.errors.has('name') }" class="form-control" id="name" name="name" required/>
<has-error :form="form" field="name" />
<small id="nameHelp" class="form-text text-muted">Unique name of your team (155 character limit).</small>
</div>
</div>
and am setting my props and vForm data as follows:
props: {
existing: {
type: Object,
required: true,
default: {}
}
},
// middleware: 'auth',
data () {
return {
form: new Form({
id: this.existing.id,
name: this.existing.name,
region: this.existing.region,
website: this.existing.website,
about: this.existing.about,
membership_password: "",
membership_password_confirmation: "",
avatar: null,
}),
}
},
However, my form inputs are always blank...
I can access this.exists.name directly and get the proper expected data output, however it will not seem to populate into my form fields?!
Is there a proper way to pull data from a vue property into vForm form data?
Was able to get the modal form data to update by adding the following to my EditTeamModal.vue
watch: {
existing: function(val) {
this.form.fill(this.existing);
}
},

post indexed array Vue.js Axiom

I can post an array of objects in Vue, but I'm having trouble posting a simple indexed array. I'm giving streamlined code because I think the answer lies in plain sight for the experienced.
Here's what I've got so far...
<section v-for="(item, index) in items">
<div>
<button #click.prevent="deleteItem(index)">delete</button>
<input type="text" v-model="item[index]" placeholder="enter your item here">
</div>
</section>
<div>
<button #click.prevent="addItem">add item</button>
</div>
The Vue instance data object so far:
data () {
return {
items: ['']
}
},
What works:
The user can add/delete rows on the form.
Vue DevTools shows me an indexed array with empty fields for each row.
Error messages in the JS console:
I would like to see this in the post...
{ "items": ["item1 input value", "item2 input value"] }
Instead I'm only able to get this because Vue won't react to the input changes...
{ "items": ["", ""] }
For comparison, posting an array of objects works like this:
<section v-for="(item, index) in items">
<div>
<button #click.prevent="deleteItem(index)">delete</button>
<input type="text" v-model="item.color" placeholder="enter color here">
<input type="text" v-model="item.price" placeholder="enter price here">
<input type="text" v-model="item.comment" placeholder="enter comment here">
</div>
</section>
<div>
<button #click.prevent="addItem">add item</button>
</div>
The Vue instance data object:
data () {
return {
items: [{ color: '', price: '', comment: '' }]
}
},
I just received the answer in another forum.
It was so silly... item[index] just needs to be items[index].

Binding to array fields in vue.js

I want to make multiple 'in-place' editable date fields in table rows.
An example for a single field below works.
I show the currentdate (oldDate) as a label. User clicks on 'Change', an input field appears, after editing the user can Accept or Cancel.
https://jsfiddle.net/asrajan55/qv6crg84/
<div id="root">
<label>Test Date:</label>
<span v-show="!makeEditable"> {{ oldDate }} </span>
<span v-show = "makeEditable">
<input type="date" v-model="newDate" required=""/>
<button #click="acceptClicked">Accept</button>
<button name="cancel" #click="makeEditable=false">Cancel</button>
</span>
<button v-show="!makeEditable" #click="makeEditable=true" >Change</button>
</div>
new Vue({
el: "#root",
data: {
oldDate: '2019-02-04',
newDate: '2019-02-04',
makeEditable: false,
},
methods: {
acceptClicked(){
if (this.newDate!='') {
this.oldDate=this.newDate;
this.makeEditable=false;
}
}
}
});
However if I try multiple(2) fields the click event fires (sometimes) but nothing seems to happen. No errors in console. Also the Vue debugger in the browser does not immediately update the changed fields. Please help. I am desperate and pulling my hair out!
https://jsfiddle.net/asrajan55/9uhkr4w0/3/
<div id="root">
<div v-for="(item,index) in oldDates">
<label for="">Test Date:</label>
<span v-show="!editables[index]">{{item}}</span>
<input v-show="editables[index]" type="date" v-model="oldDates[index]"/>
<button v-show="editables[index]">Accept</button>
<button v-show="editables[index]" #click="editables[index]=false">Cancel</button>
<button v-show="!editables[index]" #click="makeEditable(index)">Change</button>
<hr />
</div>
</div>
new Vue({
el: "#root",
data: {
oldDates: ['2019-01-04', '2019-02-04'],
newDates: ['2019-01-04', '2019-02-04'],
editables: [false, false]
},
methods: {
makeEditable(index) {
alert(index);
this.editables[index] = true;
}
}
});
The problem was that you were mutating the array in place,
creating a new array reference and passing it would solve the issue
fixed here : https://jsfiddle.net/e3L2zcna/
makeEditable(index) {
this.editables = this.editables.map((val,i) => i===index || val);
}
Try using this.$set(this.editables, index, true);
Vue can't detect changes to arrays if you directly access an element using []. Read about it here:
https://vuejs.org/2016/02/06/common-gotchas/#Why-isn%E2%80%99t-the-DOM-updating

How to combine v-for and v-model to edit a list of objects?

I would like to edit a list of users using Vue.js. Each user has a name and an age. It seems that v-for is the right directive to work with lists and v-model is the right directive to bind the contents of an input to a particular element in the list.
So I tried to implement it like this:
new Vue({
el: '#exercise',
data: {
users: [{
name: "martin",
age: 32
}]
},
methods: {
add_user: function() {
this.users.push({
name: "",
age: ""
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="exercise">
<form id="list">
<div></div>
<div v-for="user in users">
<input v-model="user.name">
<input v-model="user.age">
<button #click="add_user">+</button>
</div>
</form>
</div>
However, once I click the button to add a new user, a new line with input fields is displayed only for a fraction of a second and then disappears, leaving the list of users containing only one element.
Please, could you tell me what am I doing wrong?
The reason is <button> with <form> when you're click button it does the request automatically so its refresh or disappear because it fails to try to use <a> or using <button #click.prevent="add_user" />
<div id="exercise">
<form id="list">
<div></div>
<div v-for="user in users">
<input v-model="user.name">
<input v-model="user.age">
<a #click="add_user">+</a> //solution
<button #click.prevent="add_user">+</button> //another solution
</div>
</form>
</div>
The button is submitting the form, add .prevent to stop the action:
new Vue({
el: '#exercise',
data: {
users: [{
name: "martin",
age: 32
}]
},
methods: {
add_user () {
this.users = [ ...this.users, {
name: "",
age: ""
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="exercise">
<form id="list">
<div v-for="(user, index) in users" :key="index">
<input v-model="user.name" :key="`name-${index}`">
<input v-model="user.age" :key="`age-${index}`">
</div>
<button #click.prevent="add_user">+</button>
</form>
</div>
You need to set tye type of the button, since it's inside a form it fallbacks to submit button.
Also, it's good to define a :key to help vue to tell the difference between one line to another on v-for.
new Vue({
el: '#exercise',
data: {
users: [{
id:new Date().getTime(),
name: "martin",
age: 32
}]
},
methods: {
add_user: function() {
this.users.push({
id:new Date().getTime(),
name: "",
age: ""
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="exercise">
<form id="list">
<div v-for="user in users" :key="user.id">
<input v-model="user.name">
<input v-model="user.age">
<button type="button" #click="add_user">+</button>
</div>
</form>
</div>

Vuejs - Proper way to clone an element, and append to DOM

I have a HTML input field to enter some information:
<div id="fruitForm">
<div class="inputArea">
<label for="fruit0">Enter Fruit Name</label>
<input id="fruit0"></input>
</div>
</div>
<button #click="newInputField">Add More Fruit Input Fields</button>
<button #click="submit">Submit Fruit</button>
And then I handle that click event:
export default {
data() {
return {
}
},
methods: {
newInputField() {
//Create another input area for users to input fruits
},
submit() {
//Do something here
}
}
}
When a user selects the Add More Fruit Input Fields button, it should create a new input area so that the HTML looks like this:
<div id="fruitForm">
<div class="inputArea">
<label for="fruit0">Enter Fruit Name</label>
<input id="fruit0"></input>
</div>
<div class="inputArea">
<label for="fruit1">Enter Fruit Name</label>
<input id="fruit1"></input>
</div>
</div>
<button #click="newInputField">Add More Fruit Input Fields</button>
<button #click="submit">Submit Fruit</button>
Now, I've been using traditional DOM manipulation methods via vanilla Javascript to accomplish this... stuff like this:
const inputArea = document.getElementsByClassName('inputArea');
And then I change the id's of the input field, and then I use appendChild to add the new input field to the DOM.
So my question is: how should I be cloning this element with vuejs? I feel I'm not approaching this in the vuejs way. Should I be approaching this like a list and using v-for? Or something else?
Avoid direct DOM manipulations with Vue. You can use data property as a model for your template. The answer would be yes, you can and probably should use v-for for what you call cloning:
var demo = new Vue({
el: '#demo',
data: {
counter: 0,
inputs: [{
id: 'fruit0',
label: 'Enter Fruit Name',
value: '',
}],
},
methods: {
addInput() {
this.inputs.push({
id: `fruit${++this.counter}`,
label: 'Enter Fruit Name',
value: '',
});
}
}
});
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="demo">
<div class="inputArea" v-for="input in inputs" :key="input.id">
<label :for="input.id">{{input.label}}</label>
<input :id="input.id" v-model="input.value"></input>
</div>
<button #click="addInput">Add input</button>
</div>