What is the right way to use :value and v-model together in a <input> - vue.js

this is my wrong code:
<input v-model="input.nameInput" type="text" :value="name" autocomplete="off" class="form-control">
<input v-model="input.posInput" type="text" :value="pos" autocomplete="off" class="form-control">
i can display the {{name}} and the {{pos}} outer of and its work. but if v-model and :value merged, error:
v-bind:value="name" conflicts with v-model on the same element because the latter already expands to a value binding internally
so what is the correct way? thanks

value is the same as v-model, v-model however updates on input where value does not.
There have been a few times I've used value over v-model but if you do this you would have to watch on input and change and update the value using a function instead.
do the following for this.
<input v-model="input.nameInput" type="text" autocomplete="off" class="form-control">
<input v-model="input.posInput" type="text" autocomplete="off" class="form-control">
Remove the value and keep the v-model.

You shouldn't use both v-bind and v-model on the same element. Consider the following code:
<label>
Input with v-bind
<input v-bind:value="message" />
</label>
<label>
Input with v-model
<input v-model="message" />
</label>
<p>{{ message }}</p>
The input with v-bind:value="message" (or it's shorthand, :value="message" will do the exact same), implements one-way binding. Any changes done to the "message" variable will be updated in this input, but changes done in the v-bind input will not change the message variable.
The input with v-model="message" has two-way binding. This means that changes to the input will reflect the message variable, but changes to the message variable will also reflect on the input. If you're building a form in vue, this type of binding is usually the way to go.
I made a JSFiddle where you can test both types of binding.
If your intention with both a v-bind and a v-model was to set a default value to your input field, you can set this value in the component's data.
data: {
message: 'My default value',
}
Or, if your fields are managed by a parent component (via props), you can set the initial value with default: "My default value".
props: {
message: {
type: String,
default: 'My default value',
},
}

Related

Validate vue-select with vee-validate

I'm new to VueJS.
I'm trying to validate vue-select using vee-validate.
I've tried to validate it manually but of course its not a good approach.
So, I tried to use vuelidate but couldn't get the desired result.
Now i'm trying to use vee-validate. Validation works fine as desired
but the issue is v-model.
I created a global variable product, to calculate the length of array, and passed it in v-model. So that when Its empty product's value will be zero and i can return desired result from vee-validation.
Here's the .vue html part.
<ValidationObserver>
<form #submit.prevent="add">
<div class="row row-xs mx-0">
<label class="col-sm-4 form-control-label">
<span class="tx-danger">*</span> Add product(s):
</label>
<div class="col-sm-8 mg-t-10 mg-sm-t-0">
<ValidationProvider rules="required" v-slot="{ errors }">
<v-select
name="product"
placeholder="Add product(s)"
:options="availableProducts" <-- here is the options array
:reduce="name => name"
label="name"
#input="setSelected"
v-model="product" <-- this calculates length and pass it to vee **extends**
>
</v-select>
<div v-for="error in errors" :key="error"> {{ error }} </div>
</ValidationProvider>
</div>
<!-- col-8 -->
</div>
</form>
</ValidationObserver>
Here's validation.js file
import { extend } from 'vee-validate';
extend('required', value => {
console.log(value);
return value > 0;
});
I don't want this product value there. I know its not a good approach as well. I can't pass whole array to v-model because then I can't push options in it. I can't pass a single option to v-model as well then I won't get desired result.
All I want to validate v-select when options array is empty. Any suggestions?
Veevalidate doesn't validate directly on select elements. This is my workaround.
You should create a v-field "hidden" input and a visible select v-model element. The veevalidate will take place on the v-field.
Here is an example.
<v-field type="text" class="form-control disabled" name="expirationMonth" v-model="expirationMonth" :rules="isRequired" style="display:none;"></v-field>
<select v-model="expirationMonthUI" class="form-control" #click="synchExpirationMonthUI">
<option value="January">January</option>
<option value="February">February</option>
<option value="March">March</option>
<option value="April">April</option>
<option value="May">May</option>
<option value="June">June</option>
<option value="July">July</option>
<option value="August">August</option>
<option value="September">September</option>
<option value="October">October</option>
<option value="November">November</option>
<option value="December">December</option>
</select>
<error-message name="expirationMonth"></error-message>
Then add this to your methods to synch both together.
synchExpirationMonthUI() {
this.expirationMonth = this.expirationMonthUI;
}
I have found a way of doing this, with the Rendering Complex Fields with Scoped Slots from the Vee-Validate documentation. And using the bindings from Vue Select, it looks something like this:
<Field name="supportType" v-slot="{ field }" v-model="supportType">
<v-select :options="mediaTypes" label="name" :reduce="mediaType => mediaType.id" v-bind="field">
</v-select>
</Field>
As you can see, I am using here the name, v-slot and v-model for the Field from Vee-Validate, as normal. But the v-slot is very important as it carries the information from Vue Select to Vee-Validate, or at least I think so.
On the other hand I use the options, label, reduce and v-bind from Vue Select, these I use to handle the information. So with the :options I select my dataset, with label I tell Vue Select which label to select and show from the dataset, with :reduce I tell Vue Select what will be the value of the select tag and finally use v-bind to bind the value of the select to the Vee-Validate field. So the information used on the :reduce property will be displayed on the v-model="supportType".
I tested it with a button and it worked. And I liked this way so it is not that messy and I can use the validation and other things as usual.
Hope this helps anyone.
PD: I am using Vue 3, and the latest package of both Vee-Validate and Vue Select, as of today.

What are inheritAttrs: false and $attrs used for in Vue?

As the question suggests, I can't figure out their meaning and why I should use it. It's said that it can be used so that when we have many components and we want to pass data from parent to the child's child's child's component, we don't have to use props. Is this true?
It'd be nice If you could provide an easier example. Vue.js docs don't mention much on it.
Have a look at the "Disabling Attribute Inheritance" section of the docs and the api description for the full details.
It's main usage is to define so-called "transparent" components that pass-through attributes. The example given in the doc is a component wrapping an input element:
// Component
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
`
})
// Usage
<base-input
v-model="username"
required
placeholder="Enter your username"
/>
The required and placeholder attributes are then set on the input instead of the wrapping label.
It doesn't really have anything to do with children of children of components but it can be used in such a hierarchy.
I hope that clears things up for you.
For me talking about $attrs and inheritAttrs together, makes it harder to understand. So I will show a bit more observations.
I will call my base-inp from HTML like below
<base-inp label="Username:" v-model="username"
required placeholder="Enter your username"></base-inp>
The HTML for base-inp is like below
<label>
{{ label }}
<input v-bind="$attrs" :value="value" #input="$emit('input', $event.target.value)" />
</label>
v-bind="$attrs" is used to bind attributes.
If you set v-bind="$attrs" and inheritAttrs: true (default) and render your base-inp component. It will render
So basically we are passing the attributes entered into base-inp component in the HTML template. And also into 'input' inside the base-inp. You can see 'placeholder' in both 'label' and 'input'.
If we delete v-bind="$attrs", the placeholder won't be an attribute in inner 'input'
If you set v-bind="$attrs" and inheritAttrs: false, you will see attributes are not on the root element (label) but on inner input.

Trying to assign min value in vee-validate dynamically

Vue.js component is like below.
<template>
<div class="container">
<input type="text" name="first_name" v-validate data-vv-rules="min:12">
</div>
</template>
<script>
export default {
props: ['messages']
}
</script>
In the above component, messages property is an object with one property with value like this:
this.messages.Min_Length_First_Name: 3
I am trying to assign it like below.
<input type="text"
name="first_name"
v-validate
data-vv-rules="min:this.messages.Min_Length_First_Name">
But, the input tag renders the min value = this.messages.Min_Length_First_Name instead of numeric value
Am I missing anything?
You should not use this in the template. It's automatically inferred
:data-vv-rules="`required|alpha|min:${messages.Min_Length_First_Name}|max:15`

v-bind object's properties in radio buttons can't render correctly

The radio buttons' behavior is not as I expected when I am being lazy to use v-bind to dynamically bind the properties of the options to the template.
I am trying to dynamically render the options by an javascript object. However, it behaves differently when I am binding the properties differently. Even after checking the output HTML, I am not sure why the problem exists.
This is the Vue instance for backend data.
const app = new Vue({
el: '#app',
data: {
inputs: {
radioDynamic: '',
radioDynamicOptions: [
{
id: 'Light',
label: 'Blue',
value: 'Light',
},
{
id: 'Dark',
label: 'Red',
value: 'Dark',
},
],
},
},
template: `
<div>
<h4>Radios Dynamic Options</h4>
<!-- case 1: It works fine when I bind properties individually>
<template v-for="(option, index) in inputs.radioDynamicOptions">
<input v-model="inputs.radioDynamic" type="radio" :value="option.value" :id="option.value">
<label :for="option.value">{{ option.label }}</label>
<br v-if="index < inputs.radioDynamicOptions.length">
</template>
-->
<!-- case 2: the options are rendered as a single radio button which is not able to function correctly when I bind the object directly with v-bind="object".
<template v-for="(option, index) in inputs.radioDynamicOptions">
<input v-model="inputs.radioDynamic" type="radio" v-bind="option">
<label :for="option.value">{{ option.label }}</label>
<br v-if="index < inputs.radioDynamicOptions.length">
</template>
-->
<p><strong>Radios:</strong>{{ inputs.radioDynamic }}</p>
</div>
`,
})
<case 1 HTML output>
<h4>Radios Dynamic Options</h4>
<input type="radio" id="Light" value="Light">
<label for="Light">Blue</label>
<br>
<input type="radio" id="Dark" value="Dark">
<label for="Dark">Red</label>
<br>
<p><strong>Radios:</strong>Dark</p>
<case 2 HTML output>
<h4>Radios Dynamic Options</h4>
<input type="radio" id="Light" label="Blue" value="Light">
<label for="Light">Blue</label>
<br>
<input type="radio" id="Dark" label="Red" value="Dark">
<label for="Dark">Red</label>
<br>
<p><strong>Radios:</strong></p>
I expect the case 2 method, which is using v-bind="object", should generate the same result like v-bind:id="object.id" v-bind:value="object.value"
But it turns out that I can't select the radio button individually and the selected value isn't pushed into the array.
This is my very first question here. Please forgive if my expression or format isn't good or qualified enough. Thanks and have a great day~
not sure what you're trying to achieve here, but i would recommend going over the functionality of v-bind and v-model. v-bind enables passing dynamic data to html attributes. v-on allows listening to DOM events, and v-model results in whats called - two way data binding, which is basically a combination of both v-bind and v-on. with that said, using both v-model and v-bind on the same element feel's a bit odd.
you might achieve what you desire in the 2nd case following way:
<template v-for="(option, index) in inputs.radioDynamicOptions">
<input v-model="inputs.radioDynamic" type="radio" v-bind:value="option.id">
<label :for="option.value">{{ option.label }}</label>
<br v-if="index < inputs.radioDynamicOptions.length">
</template>
UPDATE:
i believe that the issue your'e experiencing is a result of one main difference between object (which you can learn more about here) and primitive types.
long story short, in JavaScript primitive types like the strings you pass in case 1 are being passed by value and so behave as expected. while passing object's to v-bind you actually pass a pointer to that object and so when you click a radio button, you manipulate the same place in memory in charge of both radio buttons, which results with the unexpected behavior you experience.
Why do you expect that v-bind="options" will work on <input/>? This form is used for custom components only.
From documentation:
Components In-Depth → Props → Passing the Properties of an Object
If you want to pass all the properties of an object as props, you can use v-bind without an argument (v-bind instead of v-bind:prop-name). For example, given a post object:
post: {
id: 1,
title: 'My Journey with Vue'
}
The following template:
<blog-post v-bind="post"></blog-post>
Will be equivalent to:
<blog-post
v-bind:id="post.id"
v-bind:title="post.title">
</blog-post>
If you want to bind dynamic/javascript values to an usual element, you should use :value="option.value" :id="option.id" as shown in your first example.

vuejs - dynamic input 'types'

I would like to be able to dynamically change the type of an input field between text and password. I tried doing the following:
<input :type="dynamicInputType">
data() {
return {
dynamicInputType: 'password'
}
}
But apparently this doesn't work; vuejs displays the error: v-model does not support dynamic input types. Use v-if branches instead.
It's not clear to me how I can fix this with v-if.
This kind of thing is what's being suggested.
<input v-if="'text' === dynamicInputType" type="text">
<input v-else type="password">