How to set default value on dynamic input fields in Vue.js? - javascript

I have created a dynamic input fields but the problem is that I don't know how to set default values on that dynamic fields for example number "1". Can anyone help me with that? Thanks
Here is my example
new Vue({
el: '#app',
data: {
result: [],
array: [{
id: 1
}, {
id: 2
}, {
id: 3
}]
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div v-for="(item, index) in array" :key="item.id">
<input type="number" :id="item.id" v-model="result[index]">
<br>
</div>
<span>Result: {{ result }}</span>
</div>

As Vue js is reactive you can simply set initial value in result
new Vue({
el: '#app',
data: {
result: ["1","1","1"],
array: [{
id: 1
}, {
id: 2
}, {
id: 3
}]
}
})

According to the official documentation,
v-model will ignore the initial value, checked or selected attributes
found on any form elements. It will always treat the Vue instance data
as the source of truth. You should declare the initial value on the
JavaScript side, inside the data option of your component.
I think it is a design decision of Vue to use the benefit of Vue instance’s reactive system rather than listening to DOM updates when such attributes are updated.
So you can put directly your default values into the result array, here's the updated working jsfiddle.

Related

Why is v-model inside a component inside a v-for (using a computed list) changing weirdly when the list changes?

I'm using a computed list to display several forms for changing comments in a database. (backend Symfony / api requests via axios, but unrelated)
The form for the comments itself is in a Vue component.
The computed list is based on a list that gets loaded (and set as data property) when the page is mounted which is then filtered by an input search box in the computed property.
Now when i type different things in the input box and the comment component gets updated the v-model and labels are messing up.
I've tested in several browsers and the behaviour is the same in the major browsers.
I've also searched the docs and haven't found a solution.
Example to reproduce behaviour:
<!DOCTYPE html>
<html>
<div id="app"></app>
</html>
const ChangeCommentForm = {
name: 'ChangeCommentForm',
props: ['comment', 'id'],
data() {
return {
c: this.comment,
disabled: false
};
},
template: `
<form>
<div>{{ comment }}</div>
<input :disabled="disabled" type="text" v-model="c">
<button type="submit" #click.prevent="changeComment">
Change my comment
</button>
</form>
`,
methods: {
changeComment() {
this.disabled = true;
// do the actual api request (should be unrelated)
// await api.changeCommentOfFruit(this.id, this.c),
// replacing this with a timeout for this example
window.setTimeout(() => this.disabled = false, 1000);
}
}
};
const App = {
components: {ChangeCommentForm},
data() {
return {
fruits: [
{id: 1, text: "apple"},
{id: 2, text: "banana"},
{id: 3, text: "peach"},
{id: 4, text: "blueberry"},
{id: 5, text: "blackberry"},
{id: 6, text: "mango"},
{id: 7, text: "watermelon"},
],
search: ''
}
},
computed: {
fruitsFiltered() {
if (!this.search || this.search === "")
return this.fruits;
const r = [];
for (const v of this.fruits)
if (v.text.includes(this.search))
r.push(v);
return r;
}
},
template: `
<div>
<form><input type="search" v-model="search"></form>
<div v-for="s in fruitsFiltered">
<ChangeCommentForm :id="s.id" :comment="s.text"/>
</div>
</div>
`
};
const vue = new Vue({
el: '#app',
components: {App},
template: '<app/>'
});
Just type some letters in the search box
Example on codepen: https://codepen.io/anon/pen/KLLYmq
Now as shown in the example the div in CommentChangeForm gets updated correctly, but the v-model is broken.
I am wondering if i miss something or this is a bug in Vue?
In order to preserve state of DOM elements between renderings, it's important that v-for elements also have a key attribute. This key should remain consistent between renderings.
Here it looks like the following might do the trick:
<div v-for="s in fruitsFiltered" :key="s.id">
<ChangeCommentForm :id="s.id" :comment="s.text"/>
</div>
See:
https://v2.vuejs.org/v2/guide/list.html#Maintaining-State
https://v2.vuejs.org/v2/api/#key

How to use Vue.js variable/parameter inside v-if condition

I need to pass a dynamic variable into v-if attribute.
I tried multiple ways but it doesn't produce the expected result.
So far, I have this: v-if="customDropdown === {{row.name}}"
How can I conditionally and dynamically render the element, please?
Thank you in advance.
You cannot use interpolation in Vue directives/attributes.
To bind to v-if or v-for use variables directly:
<div v-if="value.someProperty"></div>
To bind to other attributes/properties use v-bind: or shorthand : as follows:
<div :directive="value"></div>
Template Syntax documentation
You need binding the row.name to data object in vue:
export default {
data() {
return {
customDropdown: 'something',
row: {name: 'something'},
}
}
}
and then use it in the v-if statement:
<div v-if="customDropdown == row.name">You see me if customDropdown is equal to row.name</div>
First, you cannot interpolate directives/attributes and second, it's not clear, but I think you want to check the condition while iterating by a array.
You can use the template structure below:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div
v-for="row in arrayList"
v-if="customDropdown === row.name"
>
Show row content: {{ row }}
</div>
</div>
With the data structure below to achieve you goal:
new Vue({
el: '#app',
data: {
customDropdown: 'first',
arrayList: [
{
name: 'first',
},
{
name: 'second',
},
],
},
})
Just use the properties name,
For example
export default {
data() {
return {
var: true
}
}
}
html
<span v-if="var">I only appear when the condition is true</span>

proper use of Vue $refs

Im attempting to recreate this exact inline editing functionality in on of my vue components. However, and I may be wrong, I see some of the syntax is outdated Vue, in particular the v-el directive being used. I've attempted to update the syntax like so:
new Vue({
el: '#app',
data: {
numbers: [{
val: 'one',
edit: false
},
{
val: 'two',
edit: false
},
{
val: 'three',
edit: false
}
]
},
methods: {
toggleEdit: function(ev, number) {
number.edit = !number.edit
// Focus input field
if (number.edit) {
Vue.nextTick(function() {
ev.$refs.input.focus(); // error occurs here
})
}
},
saveEdit: function(ev, number) {
//save your changes
this.toggleEdit(ev, number);
}
}
})
<div id="app">
<template v-for="number in numbers">
<span v-show="!number.edit"
v-on:click="toggleEdit(this, number)">{{number.val}}</span>
<input type="text"
ref="input"
v-model="number.val"
v-show="number.edit"
v-on:blur="saveEdit(ev, number)"> <br>
</template>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
However I get a range of errors... any suggestions on how to properly execute this?
Here is the Error:
[Vue warn]: Error in nextTick: "TypeError: undefined is not an object
(evaluating 'ev.$refs.input')"
Many things changed from Vue.js 1.x to 2.x. I will walk you through the changes necessary in that snippet of yours:
v-repeat should be v-for
Replace v-el="input" with ref="input"
Since you are using ref="input" inside a v-for, then this.$refs.input will be an array of elements, not a single element.
To access each single element, you will need an index (for the array), that's why you should include the index variable in the v-for: v-for="(number, index) in numbers"
Pass the index instead of the ev to the functions, so you can get the<input>s later using vm.$refs.input[index].focus();
And that's pretty much it. After changes you'll get:
new Vue({
el: '#app',
data: {
numbers: [
{
val: 'one',
edit: false
},
{ val: 'two',
edit: false
},
{
val: 'three',
edit: false
}
]
},
methods: {
toggleEdit: function(index, number){
number.edit = !number.edit;
// Focus input field
var vm = this;
if (number.edit){
Vue.nextTick(function() {
vm.$refs.input[index].focus();
})
}
},
saveEdit: function(index, number){
//save your changes
this.toggleEdit(index, number);
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<div id="app">
<template v-for="(number, index) in numbers">
<span v-show="!number.edit"
v-on:click="toggleEdit(index, number)">{{number.val}}</span>
<input type="text"
ref="input"
v-model="number.val"
v-show="number.edit"
v-on:blur="saveEdit(index, number)"> <br>
</template>
</div>
If you want the functionality and not the code design, I'd recommend you redesign it. I think you want to edit data, and the data shouldn't have to know whether it's being edited. That is the role of a component.
So let's make a component that lets you v-model data. The component itself has a span and an input. If you're editing, it shows the input, otherwise, the span. Click starts editing, blur stops editing. When editing starts, set focus on the input.
It takes a value prop. Its input element emits an input event to signal changes (per component v-model spec.
new Vue({
el: '#app',
data: {
stuff: ['one', 'two', 'three']
},
components: {
inlineEditor: {
template: '#inline-editor-template',
props: ['value'],
data() {
return {
editing: false
}
},
methods: {
startEditing() {
this.editing = true;
this.$nextTick(() => this.$refs.input.focus());
},
stopEditing() {
this.editing = false;
}
}
}
}
});
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
<inline-editor v-for="item, index in stuff" v-model="stuff[index]"></inline-editor>
</div>
<template id="inline-editor-template">
<div>
<span #click="startEditing" v-show="!editing">{{value}}</span>
<input ref="input" :value="value" #input="e => $emit('input', e.target.value)" #blur="stopEditing" v-show="editing">
</div>
</template>

Input-fields as components with updating data on parent

I'm trying to make a set of components for repetitive use. The components I'm looking to create are various form fields like text, checkbox and so on.
I have all the data in data on my parent vue object, and want that to be the one truth also after the user changes values in those fields.
I know how to use props to pass the data to the component, and emits to pass them back up again. However I want to avoid having to write a new "method" in my parent object for every component I add.
<div class="vue-parent">
<vuefield-checkbox :vmodel="someObject.active" label="Some object active" #value-changed="valueChanged"></vuefield-checkbox>
</div>
My component is something like:
Vue.component('vuefield-checkbox',{
props: ['vmodel', 'label'],
data(){
return {
value: this.vmodel
}
},
template:`<div class="form-field form-field-checkbox">
<div class="form-group">
<label>
<input type="checkbox" v-model="value" #change="$emit('value-changed', value)">
{{label}}
</label>
</div>
</div>`
});
I have this Vue object:
var vueObject= new Vue({
el: '.vue-parent',
data:{
someNumber:0,
someBoolean:false,
anotherBoolean: true,
someObject:{
name:'My object',
active:false
},
imageAd: {
}
},
methods: {
valueChange: function (newVal) {
this.carouselAd.autoOrder = newVal;
}
}
});
See this jsfiddle to see example: JsFiddle
The jsfiddle is a working example using a hard-coded method to set one specific value. I'd like to eighter write everything inline where i use the component, or write a generic method to update the parents data. Is this possible?
Minde
You can use v-model on your component.
When using v-model on a component, it will bind to the property value and it will update on input event.
HTML
<div class="vue-parent">
<vuefield-checkbox v-model="someObject.active" label="Some object active"></vuefield-checkbox>
<p>Parents someObject.active: {{someObject.active}}</p>
</div>
Javascript
Vue.component('vuefield-checkbox',{
props: ['value', 'label'],
data(){
return {
innerValue: this.value
}
},
template:`<div class="form-field form-field-checkbox">
<div class="form-group">
<label>
<input type="checkbox" v-model="innerValue" #change="$emit('input', innerValue)">
{{label}}
</label>
</div>
</div>`
});
var vueObject= new Vue({
el: '.vue-parent',
data:{
someNumber:0,
someBoolean:false,
anotherBoolean: true,
someObject:{
name:'My object',
active:false
},
imageAd: {
}
}
});
Example fiddle: https://jsfiddle.net/hqb6ufwr/2/
As an addition to Gudradain answer - v-model field and event can be customized:
From here: https://v2.vuejs.org/v2/guide/components.html#Customizing-Component-v-model
By default, v-model on a component uses value as the prop and input as
the event, but some input types such as checkboxes and radio buttons
may want to use the value prop for a different purpose. Using the
model option can avoid the conflict in such cases:
Vue.component('my-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean,
// this allows using the `value` prop for a different purpose
value: String
},
// ...
})
<my-checkbox v-model="foo" value="some value"></my-checkbox>
The above will be equivalent to:
<my-checkbox
:checked="foo"
#change="val => { foo = val }"
value="some value">
</my-checkbox>

Vue JS, checkboxes and computed properties

I am having some problems using vue, checkboxes and computed properties.
I made a very small example showing my problem: https://jsfiddle.net/El_Matella/s2u8syb3/1/
Here is the HTML code:
<div id="general">
Variable:
<input type="checkbox" v-model="variable">
Computed:
<input type="checkbox" v-model="computed()">
</div>
And the Vue code:
new Vue({
el: '#general',
data: {
variable: true
},
compute: {
computed: function() {
return true;
}
}
})
The problem is, I can't make the v-model="computed" work, it seems that Vue doesn't allow such things.
So my question is, how could I use the benefits of the computed data and apply it to checkboxes?
Here is an other jsfiddle showing the same problem, but with more code, I was trying to use computed properties to build a "selected" products array variable: https://jsfiddle.net/El_Matella/s2u8syb3/
Thank you for your answers and have a nice day!
Computed properties are basically JavaScript getters and setters, they are used like regular properties.
You can use a computed setter to set the value (currently, you only have a getter). You will need to have a data or props property in which you can save the changes of the model though, because getters and setters don't have an inherent state.
new Vue({
el: '#general',
data: {
variable: true,
cmpVariable: true,
},
computed: { // "computed" instead of "compute"
cmp: {
get: function() {
return this.$data.cmpVariable;
},
set: function(val) {
this.$data.cmpVariable = val;
},
}
}
});
Also, you don't need to call the computed with brackets (as it behaves like a regular property):
<div id="general">
Variable:
<input type="checkbox" v-model="variable">
Computed:
<input type="checkbox" v-model="cmp">
</div>
You miss-spelled computed. Here Computed Properties
I guess you want to check an item in Product list,
So it can be displayed in the selected list.
And you also want to check it off from both the lists.
Thus you don't need a computed property.
For check boxes, you can easily change the selected set by referring to it with v-model and set value for what you want to put in the set.
In your case, that's the product.id.
You may want to save the object itself in the selectedProducts list,
but I highly recommend you not to do that.
In some case, it will cause unexpected results as objects are mutable.
So it will work if it is written this way.
new Vue({
el: '#general',
data: {
products: [{
id: 1
}, {
id: 2
}],
selectedProducts: []
}
})
<script src="//cdn.bootcss.com/vue/1.0.13/vue.min.js"></script>
<h1>Hello</h1>
<div id="general">
<h2>Products</h2>
<ul>
<li v-for="product in products">
<input v-model="selectedProducts" value="{{product.id}}" type="checkbox">{{ product.id }}
</li>
</ul>
<h2>Selected Products</h2>
<ul>
<li v-for="p in selectedProducts">
<input v-model="selectedProducts" value="{{p}}" type="checkbox">{{ p }}
</li>
</ul>
</div>

Categories

Resources