VueJS: How to create v-model value inside object array - vue.js

I have several input fields like so:
<input type=text" v-model="InputVModel1">
<input type=text" v-model="InputVModel2">
and I want to place these v-model values inside an array of objects, like so:
array = [{id:1,value:InputVModel1},{id:2,value:InputVModel2},..]
What's the best way to achieve this?
Is it to use $set in a computed value to 'push' these into the array like:
computed: {
computed_array: function(){
object= {"id":1,"value":InputVModel1}
this.$set(this.array, object) //push them with a for loop
[..]
}
}
Or is there a more elegant way to do this?
Background: I want to use the final array for 'vuedraggable' to change the order of the objects while maintaining other important meta infos for each value.

new Vue({
el: '#app',
data: {
list: [
{ id: 1, value: 'foo' },
{ id: 2, value: 'bar' },
{ id: 3, value: 'baz' }
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div
v-for="(record, i) in list"
:key="i"
>
{{ record.id }}
<input v-model="record.value"/>
{{ record.value }} <!-- To check if value key is updating in the list -->
</div>
</div>
PS: Use list (final array) with for vue-draggable.

Related

Vue2 JS - v-for component in a form

i'm facing this problem.
I have a form where i get a component with a v-for cycle.
In this component I have a multiple select.
I cannot understand how to gather data from the components when submitting the form
I modified the question thanks to #Reinier68 tip on Components, bu still not getting
This is my component
<template>
<div>
<select
v-bind:value="selectedValue"
v-on:select="$emit('select', $event.target.selectedValue)"
:options="options"></select>
</div>
</template>
<script>
export default {
name: 'categorySelect',
data: function(){
return {
selected: []
}
},
props: {
options: Array,
selectedValue: null
},
}
</script>
Then, in the parent, i use this component in a v-for
<div v-for="cu in s.cus" :key="cu.cuId">
<category-select :options="options" v-model="selected[cu.cuId]" :selectedValue="value"></category-select>
</div>
The problem is that when submitting the for i still get nothing, my selected[] is null
props are for input, $emit is for output.
You pass in the value into the component with the prop, then on change, you use $emit to send the value out, then listen for the value on the component.
To assign back the selected value it's better to use a method but can be achived inline #select="v => $set(values, index, v)" and because its an array you need to use $set else it wont be reactive like #select="v => values[index] = v"
Below is an example, not using an object as you have not provided it, but; it would be the same prop in, emit out.
const categorySelect = {
template: '#category-select',
props: ['selected', 'options'],
data() {
return {
value: this.selected
}
},
methods: {
onChange(event) {
this.$emit('select', event.target.value)
}
}
}
//
new Vue({
el: '#app',
components: {
categorySelect
},
data() {
return {
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
values: [1, 0, 5, 0]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.14/vue.min.js"></script>
<div id="app">
<!-- -->
<div v-for="(value, index) in values" :key="index">
<category-select
:options="options"
:selected="value"
#select="v => $set(values, index, parseInt(v, 10))"
/>
</div>
<!-- -->
<pre>{{ values }}</pre>
</div>
<template id="category-select">
<select
v-model="value"
#change="onChange">
<option v-for="option in options">{{option}}</option>
</select>
</template>

vuejs auto-filter out an array marked "Bar"

I am new vuejs but learning a lot. I Have an array of items that renders to a list perfectly fine. I do want to not display anything marked Bar? I have tried !Bar but it does not work. Whats the correct way to do this?
var app = new Vue({
el: "#demo",
data: {
items: [{
childMsg: 'Foo'
}, {
childMsg: 'Bar'
}]
}
});
<script src="https://unpkg.com/vue"></script>
<div id="demo">
<ul v-for="item in items">
<li>{{item.childMsg}}</li>
</ul>
</div>
As usual, there are several approaches. One most straightforward is to exclude the item directly within v-for element template, like this:
<li v-if="item.childMsg !== 'Bar'">{{item.childMsg}}</li>
An alternative would be creating a computed property: array of items that do not match the pattern. Then you can rebase your v-for onto that property. Here's how it can be done:
var app = new Vue({
el: "#demo",
data: {
exclude: '',
items: [{
childMsg: 'Foo'
}, {
childMsg: 'Bar'
}]
},
computed: {
filteredItems() {
return this.items.filter(x => x.childMsg !== this.exclude);
}
}
});
<script src="https://unpkg.com/vue"></script>
<div id="demo">
<label>Exclude word... <input type="text" v-model="exclude" /></label>
<ul v-for="item in filteredItems">
<li>{{item.childMsg}}</li>
</ul>
</div>

Vue slot-scope with v-for, data changed but not re-rendered

I have a question while I'm studying vuejs2
I made an example with slot-scope && v-for, but it has an error which I can't understand.
Here is example code
https://jsfiddle.net/eywraw8t/6839/
app.vue
<template id="list-template">
<div>
<ul>
<slot name="row" v-for="item in list" v-bind=item />
</ul>
</div>
</template>
<div id="app">
<list-component :list="list">
<li slot="row" slot-scope="item">
{{item.name}}
</li>
</list-component>
</div>
Vue.component('list-component', {
template: '#list-template',
props: {
list: {
type: Array
}
}
});
new Vue({
el: '#app',
data () {
return {
list: [
{id: 1, name: 'Apple'},
{id: 2, name: 'Banana'},
{id: 3, name: 'Cherry'},
{id: 4, name: 'Durian'},
{id: 5, name: 'Eggplant'}
]
}
},
methods: {
h (item) {
item.name = item.name.toUpperCase()
console.log('Changed!')
console.log(item)
}
}
});
Strange thing is, the method 'h' is triggered and then, the console said 'Changed!' and data also changed but, the view is not re-rendered.
What am I missing? I think slot-scoped object data is not referencing the original object data.
What should I do to modify the original data?
Thanks for reading this question.
You are directly trying to modify the value of an item in an array. Due to some limitations vue cannot detect such array modifications.
So update your code to use vm.$set() to make chamges to the array item.
methods: {
h (item) {
let i = this.items.findIndex((it) => it.id === item.id);
this.$set(this.list, i, {...item, name: item.name.toUpperCase()});
console.log(item.id)
}
Here is the updated fiddle

JSON and v-if troubles with Vue.js

In the following example neither of the v-if related divs seem to get rendered before or after clicking the Add button. It seems like Vue.js isn't running any updates when the pizzas JSON object is updated.
Is there a solution to this problem without resorting to changing the pizzas variable into being an array?
<div id="app">
<div v-for="pizza in pizzas">
{{ pizza }}
</div>
<div v-if="totalPizzas === 0">
No pizza. :(
</div>
<div v-if="totalPizzas > 0">
Finally, some pizza! :D
</div>
<button #click="add">Add</button>
</div>
var app = new Vue({
el: '#app',
data: {
pizzas: {}
},
methods: {
add: function() {
this.pizzas['pepperoni'] = { size: 16, toppings: [ 'pepperoni', 'cheese' ] };
this.pizzas['meaty madness'] = { size: 14, toppings: [ 'meatballs', 'sausage', 'cajun chicken', 'pepperoni' ] };
},
totalPizzas: function() {
return Object.keys(this.pizzas).length;
}
}
});
There are several things to be improved in your code. Most of them are about syntax. For example, methods should be called, but computed properties can be queried directly: that's why it's #click="add()", but totalPizzas === 0 makes sense only if it's a computed property.
The crucial thing to understand, however, is how reactivity works in VueJS. See, while you change your object innards, adding new properties to it, this change is not detected by VueJS. Quoting the docs:
Vue does not allow dynamically adding new root-level reactive
properties to an already created instance. However, it’s possible to
add reactive properties to a nested object using the Vue.set(object, key, value) method:
Vue.set(vm.someObject, 'b', 2)
You can also use the vm.$set instance method, which is an alias to the
global Vue.set:
this.$set(this.someObject, 'b', 2)
Sometimes you may want to assign a number of properties to an existing
object, for example using Object.assign() or _.extend(). However, new
properties added to the object will not trigger changes. In such
cases, create a fresh object with properties from both the original
object and the mixin object:
// instead of `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
And this is how it might work:
var app = new Vue({
el: '#app',
data: {
pizzas: {}
},
computed: {
totalPizzas: function() {
return Object.keys(this.pizzas).length;
}
},
methods: {
add: function() {
this.pizzas = Object.assign({}, this.pizzas, {
pepperoni: { size: 16, toppings: [ 'pepperoni', 'cheese' ] },
['meaty madness']: { size: 14, toppings: [ 'meatballs', 'sausage', 'cajun chicken', 'pepperoni' ] }
});
},
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<div id="app">
<div v-for="pizza in pizzas">
Size: {{ pizza.size }} inches
Toppings: {{ pizza.toppings.join(' and ') }}
</div>
<div v-if="totalPizzas === 0">
No pizza. :(
</div>
<div v-if="totalPizzas > 0">
Finally, some pizza! :D
</div>
<button #click="add()">Add</button>
</div>

How can I add operator ternary in input type text on vue.js 2?

My vue component like this :
<template>
<div>
...
<li v-for="category in categories">
...
<input type="radio" class="category-radio" :value="category.id" (category.id == categoryId) ? 'checked data-waschecked=true' : ''>
...
</li>
...
</div>
</template>
<script>
export default {
props: ['categories', 'categoryId'],
}
</script>
I want to add condition in input type text. I use operator ternary like above code
If the code executed, it does not work
There is no error. So i'm confused to solve it
Maybe my code is still not correct
How can I solve it?
The issue is you're trying to use JavaScript expression inside plain HTML. This won't work.
You can either bind each attribute manually like this:
:checked="(expression) ? true : false"
or bind to a computed property which depends on your expression and returns your calculated property. Alternatively, you can bind an object with one to many properties, and bind the whole object at once (this is possible also):
new Vue({
el: '#app',
data: {
categories: [
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
{ id: 3, name: 'three' }
],
selectedId: 2 // for simplicity
},
computed: {
attrs: function() {
return function(id) { // computed can also return a function, so you can use args
return (id === this.selectedId) ? { checked: true, 'data-waschecked': true } : {}
}
}
},
mounted() { // log your element
console.log(document.querySelector('input[data-waschecked=true]'))
}
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<ul>
<li v-for="category in categories">
<input type="checkbox" v-bind="attrs(category.id)">
</li>
</ul>
</div>