I'm trying to iterate through a JSON object in Vue. The panelfields variable will be assigned to a JSON object, and will change between different JSON objects with varying numbers of key/value pairs.
I can't figure out how to get the value inserted into each input, as it is created. The code is similar to the following:
data() {
return {
panelfields:
[
{
fname: "fname1",
lname: "lname1",
email: "email1"
},
{
fname: "fname2",
lname: "lname2",
email: "email2"
},
{
fname: "fname3",
lname: "lname3",
email: "email3"
}
]
}
}
<div :key="index" v-for="(value, name, index) in panelfields">
<span>{{ name }}: </span>
<input type="text" v-model="panelfields" :value="value" />
</div>
Note I am trying to avoid the following pattern, where the field props are explicitly bound by name in the template:
<input type="text" v-model="value.fname" :value="value" />
<input type="text" v-model="value.lname" :value="value" />
<input type="text" v-model="value.email" :value="value" />
...which would force the JSON object pattern to fit that model, requiring separate panels to be created for each JSON object that panelfields is set to.
Panel fields is for a popup panel that will display data for different database queries, and the JSON object will be different depending on what is queried; it may be "fname", "lname", "email" or it may be something else entirely. So the UI needs to be flexible in how it handles the data.
Use a nested v-for to iterate each field's props by key (obtained by Object.keys(field)), allowing you to dynamically bind the props with v-model="field[key]":
<div :key="index" v-for="(field, name, index) in panelfields">
<input v-for="key in Object.keys(field)" v-model="field[key]" />
</div>
new Vue({
el: '#app',
data() {
return {
panelfields:
[
{
fname: "fname1",
lname: "lname1",
email: "email1"
},
{
fname: "fname2",
lname: "lname2",
email: "email2"
},
{
fname: "fname3",
lname: "lname3",
email: "email3"
}
]
}
},
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.min.js"></script>
<div id="app">
<div :key="index" v-for="(value, name, index) in panelfields">
<span>{{name}}: </span>
<input type="text" v-for="x in Object.keys(value)" v-model="value[x]" />
</div>
<div>
<pre>panelfields = {{panelfields}}</pre>
</div>
</div>
Change your template to this
<div v-for="(value, index) in panelfields" :key="index">
<span>{{ value.fname }}: </span>
<input type="text" v-model="value.fname" :value="value" />
<span>{{ value.lname }}: </span>
<input type="text" v-model="value.lname" :value="value" />
<span>{{ value.email }}: </span>
<input type="text" v-model="value.email" :value="value" />
<hr>
</div>
You can't set an entire array of objects as a v-model. Just make the separate inputs for each v-model.
<div v-for="(item, index) of panelfields" :key="index">
<span>{{item.fname}}</span><input v-model="panelfieds[index].fname"/>
<span>{{item.lname}}</span><input v-model="panelfieds[index].lname"/>
<span>{{item.email}}</span><input v-model="panelfieds[index].email"/>
</div>
Related
I have users with their own checkboxes, I need to do so that when you select a specific user by clicking on the checkbox, then when you click on the "Get user id" button, get the user ID in the getUserId method.
Here is the code in CodeSandbox
<template>
<div>
<div class="user" v-for="(user, i) in items" :key="i">
<p>{{ user.name }}</p>
<p>{{ user.age }}</p>
<input type="checkbox" />
</div>
<button #click="getUserId()">Get user id</button>
</div>
</template>
<script>
export default {
methods: {
getUserId() {},
},
data() {
return {
items: [
{
id: 1,
name: "Alex",
age: 23,
},
{
id: 2,
name: "Robert",
age: 33,
},
{
id: 3,
name: "Jacob",
age: 55,
},
],
};
},
};
</script>
Seems you want to collect the checked user IDs. To do so, you should bind your checkbox models to an array and set the value to the user.id
new Vue({
el: "#app",
methods: {
getUserId() {
console.log("userIds", this.userIds)
},
},
data() {
return {
items: [{"id":1,"name":"Alex","age":23},{"id":2,"name":"Robert","age":33},{"id":3,"name":"Jacob","age":55}],
userIds: [] // start with an empty array
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<!-- Note: the `user.id` makes for a much better `key` -->
<div class="user" v-for="user in items" :key="user.id">
<p>{{ user.name }}</p>
<p>{{ user.age }}</p>
<!-- Set the checkbox value to `user.id` and bind the model to your array -->
<input type="checkbox" :value="user.id" v-model="userIds" />
</div>
<button #click="getUserId()">Get user id</button>
</div>
See https://v2.vuejs.org/v2/guide/forms.html#Checkbox
I'm working on a little project using VueJS, and I would like to know how can I print my props (label) that I'm receiving
this is my code :
<div class="form-group">
<auto-complete :path="'/api/accounts/autoComplete/'" :label="'item.account_name'"></auto-complete>
</div>
this is my component HTML :
<template>
<div class="autocomplete">
<input autocomplete="off" type="text" class="form-control form-control-border" placeholder="query" v-model="query" #input="autoComplete" #focus="suggestion = true" />
<div v-if="data && suggestion">
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
<li #click="selected(item.account_name)" v-for="item in data" :key="item.id">
<a class="dropdown-item" href="#">{{ label }}</a>
</li>
</ul>
</div>
</div>
</template>
<script>
import {getAPI} from "../axios-api"
export default {
props: {
label: Object,
path: String
},
data() {
return {
suggestion: false,
query: '',
data: [],
}
},
methods: {
autoComplete(){
if(/\S/.test(this.query) && this.query.length >= 3){
getAPI.get(this.path + '?query=' + this.query).then((response) => {
this.data = response.data
})
}
},
selected(account){
this.query = account
this.suggestion = false
}
}
}
</script>
as you can see I'm sending an object in :label that i want to print here ( since i have this object in my component data )
Assuming that you want a specific key of item as the label, you can just pass that key string then use it in your dropdown item. Make sure that the label prop accepts only String.
<div class="form-group">
<auto-complete path="/api/accounts/autoComplete/" label="account_name" placeholder="My PlaceHolder"></auto-complete>
</div>
<template>
<input autocomplete="off" type="text" class="form-control form-control-border" :placeholder="placeholder" v-model="query" #input="autoComplete" #focus="suggestion = true" />
<li #click="selected(item.account_name)" v-for="item in data" :key="item.id">
<a class="dropdown-item" href="#">{{ item[label] }}</a>
</li>
</template>
<script>
export default {
props: {
label: String,
placeholder: String
}
}
</script>
You are sending string, not object.
You should do:
:label="item.account_name"
Instead of
:label="'item.account_name'"
I'm trying to append new input fields based on a condition, I will describe the workflow to help you understand
First stage is to press this button to implement 2 functions(1 is to move to other fieldset in the staged form, second function is to append the inputs:
<input type="button" name="secondBtn" class="next action-button" value="Next" id="secondBtn" #click="nextPlusappend"/>
nextPlusappend:
nextPlusappend() {
this.isNextClicked();
this.appendFields();
}
appendFields:
//this.platform initllized as 'one' so the condition is true.
if(this.platform === 'one'){
this.inputFields.push({
Username: '',
Password: '',
AffiliateID: '',
CI: '',
GI: '',
})
}
And I want to append all the fields from the function to this fieldset:
<div v-if="currentPage === 2">
<fieldset id="fieldset3" v-for="(param, index) in inputFields" :key="index">
<h2 class="fs-title">API Credentials</h2>
<h3 class="fs-subtitle">Step 3- Add any parameter for the integration</h3>
<input v-model="param.Username" type="text" name="`inputFields[${index}[Username]]`" placeholder="userName">
<input type="button" name="previous" class="previous action-button" value="Previous" #click="isPreviousClicked"/>
<input type="submit" name="submit" class="submit action-button" value="Create a Ticket" id="excel"/>
</fieldset>
</div>
How can I append this without hard code all the input fields as I did here:?
<input v-model="param.Username" type="text" name="`inputFields[${index}[Username]]`" placeholder="userName">
This is designated to be dynamic, what do i mean?
I mean that if the this.platform is equal to "one" there will be a unique fields, and if this.platform equal to "two" for example there will be other unique fields.
Don't think like "pushing a form field", rather think like "adding a new item to the dataset" (and of course, its displayed UI is a form field).
Let me give an example:
Vue.component("FormField", {
props: ["label", "value"],
computed: {
val: {
get() {
return this.value
},
set(val) {
this.$emit("update:value", val)
}
},
},
methods: {
handleClickAdd() {
this.$emit("click-add-field")
}
},
template: `
<div>
<label>
{{ label }}: <input type="text" v-model="val" />
</label>
<button
#click="handleClickAdd"
>
+ ADD
</button>
</div>
`,
})
new Vue({
el: "#app",
data() {
return {
formFields: [{
label: "Field 1",
value: null,
}],
}
},
methods: {
handleClickAddField() {
const item = {
label: `Field ${ this.formFields.length + 1 }`,
value: null,
}
this.formFields = [...this.formFields, item]
},
},
template: `
<div
class="container"
>
<div
class="col"
>
<h4>FIELDS:</h4>
<hr>
<form-field
v-for="(field, i) in formFields"
:key="i"
:label="field.label"
:value.sync="field.value"
#click-add-field="handleClickAddField"
/>
</div>
<div
class="col"
>
<h4>FIELD VALUES:</h4>
<hr>
<div
v-for="(field, i) in formFields"
:key="i"
>{{ field.label }}: {{ field.value }}</div>
</div>
</div>
`,
})
.container {
display: flex;
}
.col {
padding: 0 8px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
You can see, that on ADD I just added a new item in the formFields - the values are bound in the template to a child-component, that handles the actual representation of the fields.
On the right side of the snippet, you can see another benefit of decoupling data from UI: I created another representation of the same data source - that immediately reacts to any changes!
I have a list of properties in my database. I want the user to select the type of houses in the property.
Property
Selected property types
Property 1
['studio_apartment', 'one_bedroom', 'two_bedroom']
Property 2
['studio_apartment', 'one_bedroom']
To achieve this I am using the Vue v-for loop as follows:
<template>
<div v-for="(name, index) in propertyNames" :key="index">
NAME: {{ name }}<br />
<input
type="checkbox"
value="studio_apartment"
v-model="form.property_type"
/>Studio apartment<br />
<input
type="checkbox"
value="one_bedroom"
v-model="form.property_type"
/>One bedroom<br />
<input
type="checkbox"
value="two_bedroom"
v-model="form.property_type"
/>Two bedroom<br />
SELECTED: {{}}
</div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
data() {
return {
form: {
property_type: [],
},
};
},
</script>
The problem is when I select for example studio_apartment in property 1, then all the properties studio_apartment is checked. How do I only get the selected checkboxes for each property individually. Thanks.
This happens because there's only one form instance being shared in each loop iteration.
You could change form into an object array based on propertyNames, so that they have the same array length; then index into form with the iteration index of v-for:
<template>
<div>
<div v-for="(name, index) in propertyNames" :key="index">
NAME: {{ name }}<br>
<input type="checkbox" value="studio_apartment" v-model="form[index].property_type">Studio apartment<br>
<input type="checkbox" value="one_bedroom" v-model="form[index].property_type">One bedroom<br>
<input type="checkbox" value="two_bedroom" v-model="form[index].property_type">Two bedroom<br>
</div>
</div>
</template>
<script>
export default {
data() {
const propertyNames = ['Property 1', 'Property 2']
return {
propertyNames,
form: propertyNames.map(name => ({ property_type: [] }))
}
}
}
</script>
demo
How can I have different V-MODEL on every object that i generate
I am trying to make an sample cupcake website that can generate multiple forms in one submit.
But when I generate 2 field, the inputs of the 2 generated field bound by each other inputs.
This is the code I am trying to generate:
<template>
<div>
<button #click="cupcakes.push(def)">Add Cup Cake</button>
<div v-for="(cupcake, index) in cupcakes" :key="index">
<input type="text" v-model="cupcakes[index].name">
<input type="text" v-model="cupcakes[index].description">
<input type="text" v-model="cupcakes[index].type">
<input type="text" v-model="cupcakes[index].prize">
<input type="text" v-model="cupcakes[index].color">
</div>
<button #click="onSubmit">Create Cupcate</button>
</div>
</template>
<script>
export default {
data() {
return {
cupcakes: [],
def: {
name: '',
description: 'Originals',
type: 'small',
prize: 500,
color: 'color'
}
}
},
methods: {
onSubmit() {
console.log(this.cupcakes);
}
}
}
</script>
I tried to do other things but it doesn't work.
How can I dis bind the 2 field and when I submit it will take the inputs that I type or input.
You are pushing n times the same objet (def) into your cupcakes Array. def is a reference to an object. So when you update cupcakes[n], you are just updating the def values.
What you need to do is send a copy of that object into the cupcakes object:
<button #click="cupcakes.push(JSON.parse(JSON.stringify(def)))">Add Cup Cake</button>
I think a better pattern would be to make a method that returns you a new cupcake:
<template>
<div>
<button #click="cupcakes.push(getNewCupcake())">Add Cup Cake</button>
<div v-for="(cupcake, index) in cupcakes" :key="index">
<input type="text" v-model="cupcakes[index].name">
<input type="text" v-model="cupcakes[index].description">
<input type="text" v-model="cupcakes[index].type">
<input type="text" v-model="cupcakes[index].prize">
<input type="text" v-model="cupcakes[index].color">
</div>
<button #click="onSubmit">Create Cupcate</button>
</div>
</template>
<script>
export default {
data() {
return {
cupcakes: []
};
},
methods: {
onSubmit() {
console.log(this.cupcakes);
},
getNewCupcake() {
return {
name: "",
description: "Originals",
type: "small",
prize: 500,
color: "color"
}
}
}
};
</script>