Property mutation in Vuejs - javascript

So I have a multiple codes like this:
<input id="data" type="text" v-model="data">
<label for="data">Data</label>
And I tried to make a property out of it so that I won't repeat it every time:
Vue.component('textbox', {
template: `
<div>
<input :id="id" type="text" v-model="value">
<label :for="id">{{ label }}</label>
</div>
`,
props: [
"id", "value", "label", "for"],
watch: {
value: function(newVal){
this.$emit('input', newVal)
}
}
})
and access it in my html like this:
<textbox v-model="data" id="someID" label="Data"></textbox>
Everything works fine but every time I type in the textbox I get this warning in the console:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "value"
(found in component <textbox>)
Is there a way to remove that warning message? Or am I doing it the wrong way? Any help would be much appreciated.

You don't need to use watch at all. And instead of using v-model, try this:
<input :id="id" type="text" :value="value" #input="$emit('input', $event.target.value)">
working example: http://codepen.io/CodinCat/pen/dNQopR?editors=1010

Related

How to set x-model and name of Input field inside x-for in alpine js?

I am getting an issue saving the data with alpine js.
I have a form inside a for loop. but, i am unable to set the index for x-model and name of form inputs.
HTML:
<template x-for="(price, index) in prices">
<tr data-id="price.id">
<td class="text-right">
<input type="text" class="form-control" x-bind:name="record.items[${index}].batch" x-model="record.items[${index}].batch">
</td>
</tr>
</template>
Javscript
record: {
items: [],
}
I got this error.
Uncaught ReferenceError: index is not defined.
How to resolve this?
I created a CodePen illustrating how you can use the index inside an x-for loop and reference items.
The usage of ${index} in your code seems wrong to me. You should be able to use it just like [index]:
<div x-data="{ prices: myPrices, items: record.items }">
<template x-for="(price, index) in prices">
<div>
<span x-text="price.id"></span>
<input type="text" x-bind:name="items[index]" x-bind:placeholder="items[index]">
</div>
</template>
</div>
Please see the full CodePen for all details.

Why is my boolean always true when passing it as value to a radio component?

so I am learning vue and have spent some time going through the documents and haven't seen the answer that solves my question. A lot of this is due to the nomenclature between using the CLI(which I am) and not.
I am trying to make it so that when one radio button is clicked it shows a div and when the other one is clicked it shows the other. Here is what I have.
Template:
<div id="daySelection">
<div class="o-m-day">
<div id="oneDay">
<p>One day?</p><input v-model="selected" type="radio" name="oneDay" id="" class="r-button" value="true">
</div>
<div id="multipleDays">
<p>Multiple days?</p> <input v-model="selected" type="radio" name="multDays" id="" class="r-button" value="false">
</div>
</div>
<!-- the div where the conditional render will be rendered -->
<div>
<!-- multiple days -->
<div v-show="selected" id="ta-multDays">
<textarea rows="10" cols="80" name="multDays" type="text" />
</div>
<!-- one day -->
<div v-show="!selected" id="i-oneDay">
<input type="text" name="r-oneDay">
</div>
</div>
</div>
Here is the script:
export default {
name: 'CreateTournamentForm',
data: function(e) {
return {
selected: Boolean,
}
},
}
above I was getting an error in the console that was saying that data needs to be a function that returns a new instance. I see many people and examples using vue instances differently where it is:
const app = new Vue({
el: '#app',
data: {
selected: true,
}
});
However whenever trying this Vue sends me a warning saying that it needs to be a function.
[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.
I am also aware that v-show toggles the display so I have tried both setting the display of the divs to:
display: none;
as well as not.
The problem is that the value of selected is a string, whereas you expect it to be a boolean.
The following watcher:
watch: {
selected(newValue) {
console.log("selected changed to", newValue, "which is a", typeof newValue);
}
}
Will tell you this:
selected changed to true which is a string
selected changed to false which is a string
The reason is that you give the fields value a string instead of a boolean. To fix this, instead of writing value="true", write :value="true".
You can play with a live example here.
There are two problems as far as I can see here:
In a component, the data key must be a function and the value for the selected key in the object returned by the data function must be an actual boolean value true or false (which will be initial value)
export default {
name: 'CreateTournamentForm',
data: function(e) {
return {
selected: true,
}
},
}
By default, v-model values are treated as strings so your true and false values are actually the strings "true" and "false" which are both truthy. Changing your template code to the below (or alternatively using a number or string value instead) should fix it
<div v-show="selected === 'true'" id="ta-multDays">
<textarea rows="10" cols="80" name="multDays" type="text" />
</div>
I solved it by changing it from a 'v-show' to 'v-if'
<div>
<p>One day?</p>
<input
v-model="selected"
type="radio"
name="oneDay"
id="oneDay"
class="r-button"
value="onlyOneDay" />
</div>
<div id="multipleDays">
<p>Multiple days?</p>
<input
v-model="selected"
type="radio"
name="multDays"
id="multDays"
class="r-button"
value="multipleDays" />
</div>
then the div to be shown as follows:
<div v-if="selected === 'multipleDays'" id="ta-multDays">
<textarea rows="10" cols="80" name="" type="text" />
</div>
<div v-if="selected === 'onlyOneDay'" id="i-oneDay">
<input type="text" name="">
</div>

Value change for Ng Date Time Picker doesn't work

Nothing of events are not working for ng-pick-datetime
<label class="fieldlabel">{{ attribute.attribute.displayName }}: </label>
<label>
<mat-form-field class="field-full-width">
<input [owlDateTimeTrigger]="datePicker" placeholder="Date" [owlDateTime]="datePicker"
[dateTimeInput]="emitValue()" [(ngModel)]="value.value">
<owl-date-time #datePicker></owl-date-time>
</mat-form-field>
</label>
Also I import needed modules:
import { OwlDateTimeModule, OwlNativeDateTimeModule } from 'ng-pick-datetime';
https://www.npmjs.com/package/ng-pick-datetime
I have the same errors for other events: afterPickerOpen,
yearSelected, monthSelected, dateTimeChange
dateTimeInput is an output (event) not an input. Hence, you need to use () instead of [].
(dateTimeInput)="emitValue()"
For further information on outputs, read the official Angular docs.

Vue.js: Calling function on change

I'm building a component in Vue.js. I have an input on the page where the user can request a certain credit amount. Currently, I'm trying to make a function that will log the input amount to the console, as I type it in. (Eventually, I'm going to show/hide the requested documents based on the user input. I don't want them to have to click a submit button.)
The code below logs it when I tab/click out of the input field. Here's my component.vue:
<template>
<div class="col s12 m4">
<div class="card large">
<div class="card-content">
<span class="card-title">Credit Limit Request</span>
<form action="">
<div class="input-field">
<input v-model="creditLimit" v-on:change="logCreditLimit" id="credit-limit-input" type="text">
<label for="credit-limit-input">Credit Limit Amount</label>
</div>
</form>
<p>1. If requesting $50,000 or more, please attach Current Balance Sheet (less than 1 yr old).</p>
<p>2. If requesting $250,000 or more, also attach Current Income Statement and Latest Income Tax Return.</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'licenserow',
data: () => ({
creditLimit: ""
}),
methods: {
logCreditLimit: function (){
console.log(this.creditLimit)
}
}
}
</script>
If I change methods to computed, it works - but I get an error saying Invalid handler for event: change every time it logs the value.
Use the input event.
<input v-model="creditLimit" v-on:input="logCreditLimit" id="credit-limit-input" type="text">
change only fires when the element loses focus for input elements. input fires each time the text changes.
Binding #input event alongside with v-model is unnecessary. Just bind v-model and thats all. Model is automatically updated on input event.
new Vue({
el: '#app',
data: {
message: ''
}
})
<script src="https://unpkg.com/vue#2.4.4/dist/vue.min.js"></script>
<div id="app">
<input type="text" v-model="message"><br>
Output: <span>{{ message }}</span>
</div>
And if you need log it on change to console, create particular watcher:
new Vue({
el: '#app',
data: {
message: ''
},
watch: {
message: function (value) {
console.log(value) // log it BEFORE model is changed
}
}
})
<script src="https://unpkg.com/vue#2.4.4/dist/vue.min.js"></script>
<div id="app">
<input type="text" v-model="message"><br>
Output: <span>{{ message }}</span>
</div>
<input v-model="yourVariableName" #input="yourFunctinNameToBeCall" id="test" type="text">
You can use #change to it trigger the function when is done
Ex- Select a value from drop down
But here you need to call the function when pressing keys (enter values). So use #click in such cases

Disable input conditionally (Vue.js)

I have an input:
<input
type="text"
id="name"
class="form-control"
name="name"
v-model="form.name"
:disabled="validated ? '' : disabled"
/>
and in my Vue.js component, I have:
..
..
ready() {
this.form.name = this.store.name;
this.form.validated = this.store.validated;
},
..
validated being a boolean, it can be either 0 or 1, but no matter what value is stored in the database, my input is always disabled.
I need the input to be disabled if false, otherwise it should be enabled and editable.
Update:
Doing this always enables the input (no matter I have 0 or 1 in the database):
<input
type="text"
id="name"
class="form-control"
name="name"
v-model="form.name"
:disabled="validated ? '' : disabled"
/>
Doing this always disabled the input (no matter I have 0 or 1 in the database):
<input
type="text"
id="name"
class="form-control"
name="name"
v-model="form.name"
:disabled="validated ? disabled : ''"
/>
To remove the disabled prop, you should set its value to false. This needs to be the boolean value for false, not the string 'false'.
So, if the value for validated is either a 1 or a 0, then conditionally set the disabled prop based off that value. E.g.:
<input type="text" :disabled="validated == 1">
Here is an example.
var app = new Vue({
el: '#app',
data: {
disabled: 0
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="disabled = (disabled + 1) % 2">Toggle Enable</button>
<input type="text" :disabled="disabled == 1">
<pre>{{ $data }}</pre>
</div>
you could have a computed property that returns a boolean dependent on whatever criteria you need.
<input type="text" :disabled=isDisabled>
then put your logic in a computed property...
computed: {
isDisabled() {
// evaluate whatever you need to determine disabled here...
return this.form.validated;
}
}
Not difficult, check this.
<button #click="disabled = !disabled">Toggle Enable</button>
<input type="text" id="name" class="form-control" name="name" v-model="form.name" :disabled="disabled">
jsfiddle
You can manipulate :disabled attribute in vue.js.
It will accept a boolean, if it's true, then the input gets disabled, otherwise it will be enabled...
Something like structured like below in your case for example:
<input type="text" id="name" class="form-control" name="name" v-model="form.name" :disabled="validated ? false : true">
Also read this below:
Conditionally Disabling Input Elements via JavaScript
Expression You can conditionally disable input elements inline
with a JavaScript expression. This compact approach provides a quick
way to apply simple conditional logic. For example, if you only needed
to check the length of the password, you may consider doing something
like this.
<h3>Change Your Password</h3>
<div class="form-group">
<label for="newPassword">Please choose a new password</label>
<input type="password" class="form-control" id="newPassword" placeholder="Password" v-model="newPassword">
</div>
<div class="form-group">
<label for="confirmPassword">Please confirm your new password</label>
<input type="password" class="form-control" id="confirmPassword" placeholder="Password" v-model="confirmPassword" v-bind:disabled="newPassword.length === 0 ? true : false">
</div>
Your disabled attribute requires a boolean value:
<input :disabled="validated" />
Notice how i've only checked if validated - This should work as 0 is falsey ...e.g
0 is considered to be false in JS (like undefined or null)
1 is in fact considered to be true
To be extra careful try:
<input :disabled="!!validated" />
This double negation turns the falsey or truthy value of 0 or 1 to false or true
don't believe me? go into your console and type !!0 or !!1 :-)
Also, to make sure your number 1 or 0 are definitely coming through as a Number and not the String '1' or '0' pre-pend the value you are checking with a + e.g <input :disabled="!!+validated"/> this turns a string of a number into a Number e.g
+1 = 1
+'1' = 1
Like David Morrow said above you could put your conditional logic into a method - this gives you more readable code - just return out of the method the condition you wish to check.
You may make a computed property and enable/disable any form type according to its value.
<template>
<button class="btn btn-default" :disabled="clickable">Click me</button>
</template>
<script>
export default{
computed: {
clickable() {
// if something
return true;
}
}
}
</script>
Try this
<div id="app">
<p>
<label for='terms'>
<input id='terms' type='checkbox' v-model='terms' /> Click me to enable
</label>
</p>
<input :disabled='isDisabled'></input>
</div>
vue js
new Vue({
el: '#app',
data: {
terms: false
},
computed: {
isDisabled: function(){
return !this.terms;
}
}
})
To toggle the input's disabled attribute was surprisingly complex. The issue for me was twofold:
(1) Remember: the input's "disabled" attribute is NOT a Boolean attribute.
The mere presence of the attribute means that the input is disabled.
However, the Vue.js creators have prepared this...
https://v2.vuejs.org/v2/guide/syntax.html#Attributes
(Thanks to #connexo for this... How to add disabled attribute in input text in vuejs?)
(2) In addition, there was a DOM timing re-rendering issue that I was having. The DOM was not updating when I tried to toggle back.
Upon certain situations, "the component will not re-render immediately. It will update in the next 'tick.'"
From Vue.js docs: https://v2.vuejs.org/v2/guide/reactivity.html
The solution was to use:
this.$nextTick(()=>{
this.disableInputBool = true
})
Fuller example workflow:
<div #click="allowInputOverwrite">
<input
type="text"
:disabled="disableInputBool">
</div>
<button #click="disallowInputOverwrite">
press me (do stuff in method, then disable input bool again)
</button>
<script>
export default {
data() {
return {
disableInputBool: true
}
},
methods: {
allowInputOverwrite(){
this.disableInputBool = false
},
disallowInputOverwrite(){
// accomplish other stuff here.
this.$nextTick(()=>{
this.disableInputBool = true
})
}
}
}
</script>
Can use this add condition.
<el-form-item :label="Amount ($)" style="width:100%" >
<template slot-scope="scoped">
<el-input-number v-model="listQuery.refAmount" :disabled="(rowData.status !== 1 ) === true" ></el-input-number>
</template>
</el-form-item>
If you use SFC and want a minimal example for this case, this would be how you can use it:
export default {
data() {
return {
disableInput: false
}
},
methods: {
toggleInput() {
this.disableInput = !this.disableInput
}
}
}
<template>
<div>
<input type="text" :disabled="disableInput">
<button #click="toggleInput">Toggle Input</button>
</div>
</template>
Clicking the button triggers the toggleInput function and simply switches the state of disableInput with this.disableInput = !this.disableInput.
This will also work
<input type="text" id="name" class="form-control" name="name" v-model="form.name" :disabled="!validated">
My Solution:
// App.vue Template:
<button
type="submit"
class="disabled:opacity-50 w-full px-3 py-4 text-white bg-indigo-500 rounded-md focus:bg-indigo-600 focus:outline-none"
:disabled="isButtonDisabled()"
#click="sendIdentity()"
>
<span v-if="MYVARIABLE > 0"> Add {{ MYVARIABLE }}</span>
<span v-else-if="MYVARIABLE == 0">Alternative text if you like</span>
<span v-else>Alternative text if you like</span>
</button>
Styles based on Tailwind
// App.vue Script:
(...)
methods: {
isButtonDisabled(){
return this.MYVARIABLE >= 0 ? undefined: 'disabled';
}
}
Manual:
vue v2
vue v3
If isButtonDisabled has the value of null, undefined, or false, the
disabled attribute will not even be included in the rendered
element.
Bear in mind that ES6 Sets/Maps don't appear to be reactive as far as i can tell, at time of writing.
We can disable inputs conditionally with Vue 3 by setting the disabled prop to the condition when we want to disable the input
For instance, we can write:
<template>
<input :disabled="disabled" />
<button #click="disabled = !disabled">toggle disable</button>
</template>
<script>
export default {
name: "App",
data() {
return {
disabled: false,
};
},
};
</script>
There is something newly released called inert, which is literally making it ignored by the browser.
<template>
<input
type="text"
id="name"
class="form-control"
name="name"
:inert="isItInert"
/>
</template>
<script setup>
const isItInert = true
</script>
Here is the playground for testing purposes.
Vue 3
<input
type="text"
id="name"
class="form-control"
name="name"
v-model="form.name"
:disabled="VALIDATOR == '0'"
/>

Categories

Resources