Computed property not disabling input - vue.js

I am trying to set the disabled property on a text field via checkbox. I'm referencing this item, but none of the solutions are working for me.
My text field and checkbox are as follows:
<input
type="checkbox"
class="form-check-input"
v-model="formData.useSystemSetting"
>
<input
type="text"
class="form-control"
:class="hasError('maxCount') ? 'is-invalid' : ''"
placeholder="Enter the Maximum Count"
v-model="formData.maxCount"
:disabled = "isDisabled"
>
My computed property is:
computed:{
isDisabled: function() {
this.useSystemSetting = this.useSystemSetting == true?false:true
return this.useSystemSetting;
},
I'm also setting useSystemSettings in the data section to true because if I don't it doesn't get populated. When I add a breakpoint to the computed property, it's only getting hit on the page load, but not after.

It's not allowed to mutate other properties inside the computed property, you should only do some process and return a value:
computed:{
isDisabled: function() {
return this.useSystemSetting ? false : true;
}
}

You shouldn't mutate data inside a computed property.
A computed property is used whenever you need to use logic for getting a value which includes reactive data, as you'd find here.
You could try something like:
computed:{
isDisabled: function() {
return !this.formData.useSystemSetting;
}
}
Presuming that the formData object will not be null at this point.
To prevent that, you could also use something like:
return ((this.formData || {}).useSystemSetting || true) ? false : true;

Related

In Vue, how to get the content of a textarea?

I want to keep the value of a variable identical with the content of a textarea.
I don't want to use v-bind or v-model, because I have already bound the textarea with another value.
This is a notebook app, and the textarea is used to display the content of a note, so it has been bound using v-bind with a note object, like
<textarea cols="30" rows="3" v-bind:value="note"></textarea>
Now, I want to add the "edit note" functionality. So when the content of the textarea changes, I want to store its value into a variable, and when the "submit" button is clicked, I pass the value of the variable, which contains the new content of the note, to backend to update the note.
My question is, how to store the textarea's content into the variable after each time the content changes?
I think I cannot use v-model because this way the note will be changed right after the content of the textarea is modified (though not sent to backend), but this is not what I want. What I want is the note to be changed only after the "submit" button is clicked. Thus, I cannot use v-model
Should I use v-on:change? If so, how to get the content of the textarea?
Like,
<textarea v-on:change="updateTheVariable(I need to get the content of the textarea here)"> ... </textarea>
methods: {
updateTheVariable(content of the textarea) {
this.variable = content of the textarea
}
}
Thanks
I'm assuming this thing only shows up when you click some kind of edit button which is why you don't want to alter note so try something like this instead
<button type="button" v-if="!editMode" #click="editNote">Edit</button>
<form v-if="editMode" #submit="handleSubmit">
<fieldset :disabled="saving">
<textarea v-model="editingNote"></textarea>
<button type="submit">Edit</button>
</fieldset>
</form>
export default {
data: () => ({
note: 'whatever', // maybe it's a prop, maybe assigned later, doesn't matter
editMode: false,
editingNote: null, // this will be used to bind the edited value
saving: false
}),
methods: {
editNote () {
this.editingNote = this.note
this.editMode = true
this.saving = false
},
async handleSubmit () {
this.saving = true // disables form inputs and buttons
await axios.post('/notes/update', { note: this.editingNote}) // just an example
this.note = this.editingNote // or maybe use data from the response ¯\_(ツ)_/¯
// or if it's a prop, this.$emit('updated', this.editingNote)
this.editMode = false
}
}
}
As #Phil indicated in a deleted post, the right way to do it is
<textarea #input="updateTheVariable($event.target.value)"></textarea>
.
.
.
methods:{
updateTheVariable(value){
this.variable = value
}
}

How to prevent reactivity in form placeholder

I have an input field set up as
<input #input="e => machineCIDR = e.target.value" type="text" name="machine" class="form-control form-control-lg" :placeholder="machineCIDR">
The problem is, whenever someone fills out the form and then deletes it all, the placeholder is left with the last character that was filled out.
How can I setup :placeholder="machineCIDR" so that it shows the initial value, and then never gets updated again??
Assuming machineCIDR is set up already (as a prop or in data), you could create an object for storing all of your initial values:
data() {
return {
...
initial: {}
}
}
And set up the values in created:
created() {
this.initial['machineCIDR'] = this.machineCIDR;
}
Then bind to that instead in the placeholder:
:placeholder="initial['machineCIDR']"

I need help in calling watcher in Vuejs2 when looping the objects in HTML

I am adding one object when clicking on button and displaying the same in HTML. User can able to select the drop down values in options (string or number). Based on the input, need to disable or enable the next text input field. Here is my HTML code,
<table>
<tr><button #click="add_new_input()">Add </button></tr>
<tr v-for="(key, index) in NewArr" v-bind:key=value>
<td>
<multiselect
v-model="key.name"
:options="NameList"
selectLabel='select'
#input="userInput(value)"
></multiselect>
</td>
<td class="modify-td-padding__multi">
<input type="text"
v-model="key.value"
:disabled="isNumber"
class="input-increase-height">
</td>
</tr>
</table>
if we change the key.name dropdown, it will call one function userInput() using #input. passing value will be either "string" or "number". Vue Mehods is below,
userInput: function (value) {
this.getInputType(value);
},
getInputType: function (value) {
if(value === "string") {
this.isNumber = false;
} else {
this.isNumber = true;
}
},
add_new_input: function () {
let vm = this;
vm.NewArr.push({
name: '',
value: '',
});
vm.$set(vm.NewArr, vm.name, vm.value);
}
add_new_input will add new object to NewArr, getInputType function will check the value is "string" or "number". If it is "string", text field should be disabled else enabled.
My issue is, if there are two rows, and if i am selecting key.name for 2nd row, it is affecting the first row input field also(key.name for both rows getting enabled or disabled). I need to make change only the specific text field. So, all the text fields becoming disabled even it is "number".
This is my first project in VueJS. Thanks a lot if anyone helps me on this. Thanks in advance.
You need to manage isNumber per key, so not just
data() {return {isNumber: false}}
But:
#input="userInput(key.name, value)"
:disabled="isNumber[key.name]"
data(){ return { isNumber: {} }}
...
onUserInput: function (key, value) {
this.setIsNumber(key, value);
},
setIsNumber: function (key, value) {
this.$set(this.isNumber, key, value !== "string");
},

Vue + Vue-Paginate: Array will not refresh once empty

I am using vue-paginate in my app and I've noticed that once my array is empty, refreshing its value to an array with contents does not display.
<paginate
name="recipes"
:list="recipes"
:per="16"
class="p-0"
>
<transition-group name="zoom">
<div v-for="recipe in paginated('recipes')" :key="recipe.id">
<recipe class=""
:recipe="recipe"
:ref="recipe.id"
></recipe>
</div>
</transition-group>
</paginate>
This is how things get displayed, and my recipe array changes depending on a search. If I type in "b" into my search, results for banana, and bbq would show. If I typed "ba" the result for bbq is removed, and once I backspace the search to "b" it would re-appear as expected.
If I type "bx" every result is removed and when I backspace the search to "b", no results re-appear.
Any idea why this might happen?
UPDATE
When I inspect the component in chrome I see:
currentPage:-1
pageItemsCount:"-15-0 of 222"
Even though the list prop is:
list:Array[222]
Paginate needs a key in order to know when to re-render after the collection it's looking at reaches a length of zero. If you add a key to the paginate element, things should function as expected.
<paginate
name="recipes"
:list="recipes"
:per="16"
class="p-0"
:key="recipes ? recipes.length : 0" // You need some key that will update when the filtered result updates
>
See "Filtering the paginated list" is not working on vue-paginate node for a slightly more in depth answer.
I found a hacky workaround that fixed it for my app. First, I added a ref to my <paginate></paginate> component ref="paginator". Then I created a computed property:
emptyArray () {
return store.state.recipes.length == 0
}
then I created a watcher that looks for a change from length == 0 to length != 0:
watch: {
emptyArray: function(newVal, oldVal) {
if ( newVal === false && oldVal === true ) {
setTimeout(() => {
if (this.$refs.paginator) {
this.$refs.paginator.goToPage(page)
}
}, 100)
}
}
}
The timeout was necessary otherwise the component thought there was no page 1.
Using :key in the element has certain bugs. It will not work properly if you have multiple search on the table. In that case input will lose focus by typing single character. Here is the better alternative:
computed:{
searchFilter() {
if(this.search){
//Your Search condition
}
}
},
watch:{
searchFilter(newVal,oldVal){
if ( newVal.length !==0 && oldVal.length ===0 ) {
setTimeout(() => {
if (this.$refs.paginator) {
this.$refs.paginator[0].goToPage(1)
}
}, 50)
}
}
},

Observe Change in Vue js model when input updated by javascript function

I am using Date Range Picker to select to dates now when I select the dates, I update the inputs with dates value respectively.
The inputs I have binded with v-model and created a function in watch attribute of component to observe the change in model.
But when the inputs are updated with the javascript function no change can be observed in the model but the value of my input fields are updated.
// My Input Fields
<input type="text" name="updateStartDate" v-model="updateDateRange.start">
<input type="text" name="updateEndDate" v-model="updateDateRange.end">
//My javascript Function
$('input[rel=dateRangePickerX]').daterangepicker({
'autoApply': true,
'drops': 'up',
'startDate': moment().add(90, 'days').calendar(),
'endDate': moment().add(97, 'days').calendar(),
locale: { cancelLabel: 'Clear' }
},
function (start, end, label) {
$('input[name="updateStartDate"]').val(start.format('MM/DD/YYYY'));
$('input[name="updateEndDate"]').val(end.format('MM/DD/YYYY'));
});
// My watch attribute in Component
watch : {
'updateDateRange.end' : function (val) {
console.log('In Watch Function');
console.log(this.dateRanges);
if(val != '' && this.updateDateRange.start != '' && this.updateDateRangeIndex != ''){
console.log(val);
console.log(this.updateDateRange.start);
console.log(this.updateDateRangeIndex);
this.dateRanges[this.updateDateRangeIndex] = this.updateDateRange;
this.updateDateRangeIndex = '';
this.updateDateRange.start = '';
this.updateDateRange.end = '';
console.log(this.dateRanges);
}
}
}
I don't like to mix jQuery and Vue because jQuery messes up the DOM. Even more, I find it completely unnecessary.
Simple only with native Vue you can do it like this:
<input type="text" name="updateStartDate" v-model="startDate" #input="onInput()">
<input type="text" name="updateStartDate" v-model="endDate" #input="onInput()">
methods: {
onInput(e): function () {
// this will be called on change of value
}
}
Further to set the value and update the DOM simply update startDate and/or endDate variables and DOM will update accordingly.
You need to work with your model and not fiddle with the bound DOM element. You have bound the elements to viewmodel items:
<input type="text" name="updateStartDate" v-model="updateDateRange.start">
<input type="text" name="updateEndDate" v-model="updateDateRange.end">
then you use jQuery to set the field values
$('input[name="updateStartDate"]').val(start.format('MM/DD/YYYY'));
$('input[name="updateEndDate"]').val(end.format('MM/DD/YYYY'));
but you should be setting the bound values instead:
updateDateRange.start = start.format('MM/DD/YYYY');
updateDateRange.end = end.format('MM/DD/YYYY');