Vue.js: How to concatenate v-model value - javascript

How to make this work:
<div v-for="index in 4">
<input v-model="'invoice_' + index" >
</div>
just like this:
<div v-for="index in 4">
<input v-model="invoice_1" >
</div>

It could be better if you put all the invoices_s variables in a single object and then refer to them by key, something like follows:
new Vue({
el: '#app',
data: {
invoices: {
invoice_1: '1',
invoice_2: '2',
invoice_3: '3',
invoice_4: '4'
}
}
})
<script src="https://unpkg.com/vue#2.5.3/dist/vue.js"></script>
<div id="app">
<div v-for="index in 4">
<input v-model="invoices['invoice_' + index]">
</div>
</div>

If you are not able to change how your data is structured, you can access the data object via $data. So you could bind to your invoice properties via $data['invoice_' + index]:
new Vue({
el: '#app',
data() {
return {
invoice_1: 'a',
invoice_2: 'b',
invoice_3: 'c',
invoice_4: 'd'
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
<div v-for="index in 4">
<input v-model="$data['invoice_' + index]" >
</div>
</div>
However, I agree with Psidom that it would make more sense to change your data structure if you are able to. Although, I would make invoices an array and just access each invoice by the index. This way you don't need to explicitly name each invoice with an index:
new Vue({
el: '#app',
data() {
return {
invoices: ['a', 'b', 'c', 'd' ]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
<div v-for="index in 4">
<input v-model="invoices[index-1]">
</div>
</div>
Even better would be to make invoices an array of objects, each with its own id and value. This way you can render the entire array of invoices (via v-for="invoice in invoices") without having to keep track of any indexes. This also allows you to provide a unique key attribute for each rendered element:
new Vue({
el: '#app',
data() {
return {
invoices: [
{ id: 1, value: 'a' },
{ id: 2, value: 'b' },
{ id: 3, value: 'c' },
{ id: 4, value: 'd' }
]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
<div v-for="invoice in invoices" :key="invoice.id">
<input v-model="invoice.value">
</div>
</div>

You could try the following code
Define 'invoices' as an empty object in the data state
new Vue({
el: '#app',
data() {
return {
invoices: {}
}
}
})
In the template, you can pass in the custom string with an index value
<script src="https://unpkg.com/vue#2.5.3/dist/vue.js"></script>
<div id="app">
<div v-for="index in 4">
<input v-model="invoices['invoice__' + index]">
</div>
</div>
I used the above code to implement custom checkbox value of items.
At the end, the 'invoices' object will output as 'invoice__0: true', 'invoice__1:false' and so on.

Related

How to bind input of type checkbox to v-model of child component in `vuejs` to a parent component?

How can input of type checkbox present in child component be bonded to v-model on the parent component?
Required Component Way -
Parent-
<button v-if="checkedNames">Confirm</button> //show this button if 1 checkbox is selected
<div v-model="checkedNames" >
<child-component v-for="person in collection" :key="person.id"
:person="person"><child-component>
</div>
new Vue({
el: '...',
data: {
checkedNames: [],
collection: [
{id: 1, name: 'Jack', items:[{id:100,name:"Pen",quantity:5}]},
{id: 2, name: 'John', items:[{id:200,name:"Pen",quantity:10},
{id:201,name:"Paper",quantity:100,}]},
{id: 3, name: 'Mike', items:[{id:300,name:"Pen",quantity:20}]},
]
}
})
Child-
<div v-for="(item, i) in person.items" :key="i">
<input type="checkbox" :id="item.id" :value="item.name" >
<label :for="item.id">{{item.name}}</label>
<input type="number" :id="i" :value="item.quantity" >
<label :for="i">{{item.quantity}}</label>
</div>
new Vue({
el: '...',
props:{person:Object}
})
How to send the item id, quantity and main id if the checkbox is selected to the parent?
Using v-model on Components
new Vue() is to use only to create top-level Vue instance (usually called vm or app). Components used as a child elements must be registered instead - Component Registration
Always use :key with v-for - Maintaining State
Vue.component('checkboxes', {
props: ['value', 'options'],
template: `
<div>
<template v-for="item in optionsWithKey">
<input type="checkbox" :id="item.key" :value="item.name" v-model="model" :key="item.key">
<label :for="item.key">{{item.name}}</label>
</template>
</div>
`,
computed: {
model: {
get() { return this.value },
set(newValue) { this.$emit('input', newValue) }
},
optionsWithKey() {
return this.options.map(item => ({
...item,
key: "checkbox_"+ item.id
}))
}
}
})
new Vue({
el: '#app',
data: {
checkedNames: [],
options:[
{id: 1, name: 'Jack'},
{id: 2, name: 'John'},
{id: 3, name: 'Mike'},
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.js"></script>
<div id='app'>
<checkboxes v-model="checkedNames" :options="options">
</checkboxes>
<pre>{{ checkedNames }}</pre>
</div>
I'm not sure but try it...
child:
<input type="checkbox" :id="item.id" :value="item.name" :change="$emit('valueChecked',item.name)">
And in parent:
<child-component #valuChecked="getValue"><child-component>
methods:{
getValue(value){
this.checkedNames.push(value);
}
}
See if this helps
// parent template
<child-component #update:checked-names="updateCheckedNames">
<child-component>
// parent
data() {
return {
checkedNames: []
};
},
methods: {
updateCheckedNames(name) {
if (this.checkedNames.includes(name)) {
const index = this.checkedNames.indexOf(name);
this.checkedNames.splice(index, 1);
} else {
this.checkedNames.push(name);
}
}
}
// child template
<div v-for="item in collection">
<input type="checkbox"
:id="item.id"
:value="item.name"
#click="$emit('update:checked-names', item.name)"> // emit here
<label :for="item.id">
{{item.name}}</label>
</div>

Filtering a List Using Vue and Checkboxes?

I am trying to filter a list of posts using the userId in checkboxes. I am pulling the data from: https://jsonplaceholder.typicode.com/posts and want to then add checkboxes that when checked filter the list by userId. This is what I have currently:
var app = new Vue({
el: '#app',
data() {
return {
jobs: [],
userIds: ['1', '2', '3'],
checkedUserIds: []
}
},
created() {
axios
.get('https://jsonplaceholder.typicode.com/posts')
.then(response => (this.jobs = response.data))
},
computed: {
filteredJobs() {
if (!this.checkedUserIds.length)
return this.jobs
return this.jobs.filter(job => this.checkedUserIds.includes(job.userId))
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<div v-for="userId in userIds">
<input type="checkbox" v-model="checkedUserIds" v-bind:value="userId" /> {{userId}}
</div>
<div v-for="job in filteredJobs">
<h2>{{ job.title }}</h2>
<div>{{ job.body }}</div>
</div>
</div>
I am stumped on why when I click a checkbox the whole list disappears instead of filtering by the userId
Your userIds: ['1', '2', '3'], is an array of strings not integers. Try userIds: [1, 2, 3],
The json file has userId as integers. Types to need to be the same for this function to work.
var app = new Vue({
el: '#app',
data() {
return {
jobs: [],
userIds: [1,2,3],
checkedUserIds: []
}
},
created() {
axios
.get('https://jsonplaceholder.typicode.com/posts')
.then(response => (this.jobs = response.data))
},
computed: {
filteredJobs() {
if (!this.checkedUserIds.length)
return this.jobs
return this.jobs.filter(job => this.checkedUserIds.includes(job.userId))
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<div v-for="userId in userIds">
<input type="checkbox" v-model="checkedUserIds" v-bind:value="userId" /> {{userId}}
</div>
<div v-for="job in filteredJobs">
<h2>{{ job.title }}</h2>
<div>{{ job.body }}</div>
</div>
</div>

Dynamic number of input fields

I'm working in a installment payment Component, and I need a dynamic list of Form Inputs, according the number of installment payments the User selects (from 1 to 12).
I don't know how I could get the values of the future Form Inputs.
When I have just one Form Input, I use 'v-model' to capture the value of the input field that already exists.
But with multiple instances of a component inside a loop, I can't figure it out.
// The User choose 6
// The value 6 (Number) goes to a property inside data() with v-model
data () {
return {
numberOfFields: 6
}
}
Then to a v-for
<template v-for="n in numberOfFields">
<input type="text" v-model="????">
</template>
I don't want to create every possibility like:
data(){
return {
inputField1: '',
inputField2: '',
inputField3: '',
// up to inputField12
}
}
I want to capture the value of the input field only if the input exists, but without creating every possible option beforehand.
You can use:
<template v-for="n in numberOfFields">
<input type="text" v-model="$data['inputField' + n]">
</template>
Demo:
new Vue({
el: '#app',
data() {
return {
numberOfFields: 6,
inputField1: '11',
inputField2: '22',
inputField3: '33',
inputField4: '44',
inputField5: '55',
inputField6: '66',
// up to inputField12
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<template v-for="n in numberOfFields">
<input type="text" v-model="$data['inputField' + n]"> {{ $data['inputField' + n] }}<br>
</template>
</div>
But this is unusual. Generally, we create another object and use it (instead of the data root).
Example using another object, called fields:
new Vue({
el: '#app',
data() {
return {
numberOfFields: 6,
fields: {
inputField1: '11',
inputField2: '22',
inputField3: '33',
inputField4: '44',
inputField5: '55',
inputField6: '66',
// up to inputField12
}
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<b>Using numberOfFields:</b><br>
<template v-for="n in numberOfFields">
<input type="text" v-model="fields['inputField' + n]"> {{ fields['inputField' + n] }}
</template>
<br><hr><br>
<b>Using (val, key):</b><br>
<template v-for="(val, key) in fields">
<input type="text" v-model="fields[key]"> {{ fields[key] }}
</template>
</div>

Binding property model value to array with the value from checkbox and inputbox

I am implementing the v-for as follows:
new Vue({
el: '#app',
data: {
PaymentType:[
{Id: 1, Text:"Cash"},
{Id: 2, Text:"Check"},
{Id: 3, Text:"Paypal"}
],
SelectedType:[]
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p> SelectedType {{ SelectedType }}</p>
<div v-for="(pay, index) in PaymentType">
<input type="checkbox" v-model="SelectedType" :value="{PayTypeId: pay.Id}" />
{{pay.Text}}
<input type="input" v-model="SelectedType" />
<!-- What goes here on :value attributes? -->
</div>
</div>
<div id="app">
<p> SelectedType {{ SelectedType }}</p>
<div v-for="(pay, index) in PaymentType">
<input type="checkbox" v-model="SelectedType" :value="{PayTypeId: pay.Id}"/> />
{{pay.Text}}
<input type="input" v-model="SelectedType" :value=""/>
<!-- What goes here on :value attributes? -->
</div>
</div>
Initially SelectedType is empty array object.
Is there any way in Vue to push the Id's to the PayTypeId and the value from the input to Remarks, on same array objects?
new Vue({
el: '#app',
data: {
PaymentType:[
{Id: 1, Text:"Cash"},
{Id: 2, Text:"Check"},
{Id: 3, Text:"Paypal"}
],
SelectedType:[]
}
})
SelectedType is an array of object, it signature looks like SelectedType:[{PayTypeId:0, Remarks:''}].
Is it possible to map the value from checkbox and input box to a Array of Object?
What you need is a bit complicated. The cleanest solution I see is to:
track the checked checkboxes and typed remarks separately, and
turning SelectedType into a computed property that merges those values.
In the example below the checked checkboxes are in selectePayTypeIds and the typed remarks are in typedRemarks.
Notice SelectedType is no longer in data but now in computed.
new Vue({
el: '#app',
data: {
PaymentType:[
{Id: 1, Text:"Cash"},
{Id: 2, Text:"Check"},
{Id: 3, Text:"Paypal"}
],
selectePayTypeIds: {},
typedRemarks: {}
},
computed: {
SelectedType: function () {
var vm = this;
return Object.keys(vm.selectePayTypeIds).filter(function (selectedPayTypeId) {
return vm.selectePayTypeIds[selectedPayTypeId];
}).map(function (selectedPayTypeId) {
return {PayTypeId: selectedPayTypeId, Remarks: vm.typedRemarks[selectedPayTypeId] || ''}
});
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p> SelectedType {{ SelectedType }}</p>
<div v-for="(pay, index) in PaymentType">
<input type="checkbox" v-model="selectePayTypeIds[pay.Id]" />
{{pay.Text}}
<input type="input" v-model="typedRemarks[pay.Id]">
<!-- What goes here on :value attributes? -->
</div>
</div>
Making the SelectedType computed property editable/assignable again
Since you actually want to also be able to assign values to the SelectedType property, we need to define a set function in the SelectedType computed.
To show an example of value being assigned to the SelectedType, have a look at the created function below.
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
PaymentType:[
{Id: 1, Text:"Cash"},
{Id: 2, Text:"Check"},
{Id: 3, Text:"Paypal"}
],
selectePayTypeIds: {},
typedRemarks: {}
},
created: function () {
this.SelectedType = [{PayTypeId: 2, Remarks: 'remarks about two'}];
},
computed: {
SelectedType: {
get: function () {
var vm = this;
return Object.keys(vm.selectePayTypeIds).filter(function (selectedPayTypeId) {
return vm.selectePayTypeIds[selectedPayTypeId];
}).map(function (selectedPayTypeId) {
return {PayTypeId: selectedPayTypeId, Remarks: vm.typedRemarks[selectedPayTypeId] || ''}
});
},
set: function (newSelectedType) {
var vm = this;
newSelectedType.forEach(function(sType) {
Vue.set(vm.selectePayTypeIds, sType.PayTypeId, true);
Vue.set(vm.typedRemarks, sType.PayTypeId, sType.Remarks);
});
}
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p> SelectedType {{ SelectedType }}</p>
<div v-for="(pay, index) in PaymentType">
<input type="checkbox" v-model="selectePayTypeIds[pay.Id]" />
{{pay.Text}}
<input type="input" v-model="typedRemarks[pay.Id]">
<!-- What goes here on :value attributes? -->
</div>
</div>

Is it possible to append a component on a button press?

Here's a problem that I want to solve:
I have a button that when is pressed must append my-component to a dom.
If it is pressed 2 times there must be 2 <p> tegs. How can I achieve this?
js:
<script>
Vue.component('my-component', {
template: "<p>hello</p>",
})
var vue = new Vue({
el: "#App",
data: {},
methods: {
append: function() {
// unknown code here
}
}
})
</script>
html:
<div id = "App">
<button #click="append" class="btn btn-primary">Spawn stuff!</button>
</div>
Here is one way you could do that. This code iterates over a counter using v-for to iterate over a range.
Vue.component('my-component', {
template: "<p>hello</p>",
})
var vue = new Vue({
el: "#App",
data: {
hellocount: 0
},
methods: {
append: function() {
// unknown code here
this.hellocount++
}
}
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="App">
<my-component v-for="n in hellocount" :key="n"></my-component>
<button #click="append" class="btn btn-primary">Spawn stuff!</button>
</div>
This is a little atypical; normally you will drive the components rendered from actual data, as #RoyJ suggests in your comments.
From your comment below, you could build a form something like this.
Vue.component('my-input', {
props:["value", "name"],
data(){
return {
internalValue: this.value
}
},
methods:{
onInput(){
this.$emit('input', this.internalValue)
}
},
template: `
<div>
{{name}}:<input type="text" v-model="internalValue" #input="onInput">
</div>
`,
})
var vue = new Vue({
el: "#App",
data: {
form:{
name: null,
email: null,
phone: null
}
},
methods:{
append(){
const el = prompt("What is the name of the new element?")
this.$set(this.form, el, null)
}
}
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="App">
<my-input v-for="(value, prop) in form"
:key="prop"
v-model="form[prop]"
:name="prop">
</my-input>
<button #click="append">Add New Form Element</button>
<div>
Form Values: {{form}}
</div>
</div>
The code defines a form object and iterates over the properties of the form to render inputs for each property.
This is obviously extremely naive, handles only input texts, etc. But hopefully you get the idea.

Categories

Resources