bootstrap-vue table, Formatter callback error: You may have an infinite update loop in a component render function - vue.js

I'm using bootstrap-vue table, according to documentation regarding formatter callback: https://bootstrap-vue.org/docs/components/table .
A variable is defined in data(), I will need this variable as a flag to control the cell content.
data() {
return {
aFlag: 0,
}
}
Then in the fields I use the formatter call back:
{ key: 'value', label: 'Value', formatter: this.updateValue},
In the methods area I use updateValue to update the flag:
methods: {
updateValue(value) {
..
aFlag = value
..
}
}
Then error "You may have an infinite update loop in a component render function." happened here.
If I want to do such a thing, is there any best practice? The cell content may cause other cell's change, so currently I use a variable to control the behavior as a flag. Thanks in adavance.

The formatter callback is only intended to change how the value is displayed to the user, not change its value.
If you want to be able to change the value, I'd suggest using a slot for the value property and use v-model on a form component within the slot:
<template v-slot:cell(value)="data">
<input type="text" v-model="data.value"/>
</template>

Related

Vue v-model issue when using a computed setter

I want to create an input field that the user can fill out. The catch is that I don't want them to fill this field in with special characters. Currently, I have this html setup:
<input class="rc-input-editing" id="bioInput" type="text" v-model="wrappedBioName">
And this Vue.js setup (As you can see, I'm trying to approach this problem using a computed setter) :
data () {
return {
newBioName: '',
}
},
computed: {
wrappedBioName: {
get () {
alert('getting new name!')
return this.newBioName
},
set: function (newValue) {
const restrictedChars = new RegExp('[.*\\W.*]')
if (!restrictedChars.test(newValue)) {
this.newBioName = newValue
}
}
}
Currently, my issue is that the client is able to continue filling out the text input field, even when this.newBioName isn't updating. In other words, they are able to enter special characters into the input field, even though the this.newBioName isn't being updated with these special characters.
This behavior is different than what I'm expecting, given my current understanding of v-model. Based on what I've read until now, v-model binds the input element to some vue instance data, and that vue instance data to the input element (two way binding). Therefore, I'm expecting that the text in the text input field will directly match the value of this.newBioName.
Clearly, I'm missing something, and would appreciate a second pair of eyes!
Vue.js two way binding system doesn't work as you expected. Each binding process works one way each time. So, the thing you should do is not to let the input text change.
Try keypress event instead of computed property like this:
<input class="rc-input-editing" id="bioInput" type="text" v-model="newBioName" #keypress="nameKeyPressAction">
data() {
return {
newBioName: ""
};
},
methods: {
nameKeyPressAction(event) {
const restrictedChars = new RegExp("[.*\\W.*]");
const newValue = this.newBioName + event.key;
if (!restrictedChars.test(newValue))
this.newBioName = newValue;
return event.preventDefault();
}
}
Edit:
When you set a data property or a computed property as v-model of an input, vue associates them and yet, if user updates dom object via the input, property's setter is triggered and the process ends here. On the other hand, when you change the value of the property on javascript side, vue updates the dom object and this process also ends here.
In your sample code, it seems like you expect that the computed property's getter to set the dom again but it can't. The property is already updated via dom change and it can't also update it. Othervise, there might occur infinite loop.

Quasar: Is there any way to get the value typed into a QSelect with use-input before it gets removed on blur?

Per the docs you can add the attribute use-input to a QSelect component to introduce filtering and things of that nature: https://quasar.dev/vue-components/select#Native-attributes-with-use-input.
However, if you type something into one of these fields and click outside of it, the text gets removed.
Is there any way to grab that text in Vue before it gets removed and do something with it?
Since v1.9.9 there is also a #input-value event described in the q-select api.
As the api says it's emitted when the value in the text input changes. The new value is passed as parameter.
In the examples there's a filter function, so there you can save it in a data variable:
methods: {
filterFn (val, update, abort) {
update(() => {
this.myDataVariable = val;
})
}
}

VueJs + Element-ui: how to get native event from input

I am trying to get the event from the #input.native attribute of a el-input tag.
Here the template code:
<el-input :value="filter.name" #input.native="updateFilter"></el-input>
And the script code:
updateFilter (e) {
console.log(e.target.value)
}
My filter.name has been initialized with value "aaa", then I type "b" in the field. For some reason, the output on the log is "aaa" but I need the "aaab" value instead.
Also I can't use #input because it return only the value, I need other attributes too.
Are there anyway to get the valid native input event?
#Update: I am using Vuex so v-model is not an option
let's just do v-model with Vuex, and it is very simple :
export default : {
...
computed : {
filter : {
get () { return this.$store.state.filter; };
set (val) { this.$store.commit("setFilter", val);
}
}
...
}
And then v-model onto filter will be magical.
You can use computed method. Take one temporary variable and add that variable as v-model to your input. Whenever value is changing assign that variable to vuex store variable(nothing but string concatenation). You can use setters and getters in computed.
Following link might help.
assigning value to vuex store variable using computed setters and getters
I believe you are doing everything right. However, the value can't get updated unless you bind the model (using v-model="filter.name") instead of doing :value.
Here is what I did:
HTML
<el-input
class="small"
controls-position="right"
:value="someValue"
#input.native="someFunction">
</el-input>
Script
<script>
export default {
name: "CustomizeSmtCampaign",
data: function () {
return {
someValue: 'test'
}
},
methods: {
someFunction: function (val = '1') {
console.log('Event Value', val.target.value, ' some value: ', this.blankValue);
}
}
}
</script>
Output
This is the output I got on console as I typed
Event Value teste some value: test
Event Value tester some value: test
Event Value testere some value: test
Event Value testerer some value: test
Event Value testerere some value: test
So your code must be working.
What is wrong, then?
What's wrong is that you are binding to the value, not to the model.
When I changed the :value="someValue" to v-model="someValue", the following was the output:
v-model Output
Event Value teste some value: teste
Event Value tester some value: tester
Event Value testere some value: testere
Event Value testeree some value: testeree
Event Value testereer some value: testereer
Event Value testereere some value: testereere
Summary
Always bind the value using v-model (not using :value). That's how Vue achieves the reactiveness!
Hope that helped.

Vuetify Select Retaining old value

I've got a Vuetify select, with the following syntax.
<v-select label="..." autocomplete
append-icon="search" :items="plots" item-value="id" item-text="plotHeader"
v-model="selectedPlot" v-on:change="loadPlotInformation();">
</v-select>
So when the page loads, the dropdown initializes with an Ajax request. But when the user changes the value, the model reflects the old value, not the current selection.
Inside the function.
loadPlotInformation() {
console.log(this.selectedPlot);
}
Update:
I was able to fix the issue by transitioning to blur event. But why would change event not resolve?
If you want to take new value instead of the old value, you should use nextTick method.
eg:
loadPlotInformation() {
this.$nextTick(() => {
console.log(this.selectedPlot);
})
};
Try to change your function to read the parameter of your function.
loadPlotInformation(e) {
console.log(e);
}
But this way you need to check if you want to update your model variable

In vue.js is it possible to notify "observers" to refetch the value from the observed data, without changing the value of the observed data

Suppose that I have an input element bound like this:
<input :value="somedata">
The user types something in the input, and since I am not using v-model or altering somedata through a handler, the value of the element is now different from somedata. This is what I want, but I would also like to have the following capability:
Without changing the value of somedata I would like to be able to notify the element so that it sets its value equal to somedata again. Something like knockout's notifySubscribers() or valueHasMutated()
Is that possible in vue.js?
UPDATE: A clear illustration of the issue here: https://jsfiddle.net/gtezer5c/3/
It's a little difficult interpreting what exactly the requirements and acceptance criteria might be to suit your needs, and I thought Bill's solution was what you were after, but after all the updates and clarifications, I think I understand a little more what you're trying to accomplish: in short, I think you need a generic way to have an input that can hold a value but that can be independently reverted to some other value.
Please have a look at this CodePen. I believe it's providing what you're trying to do. It allows you to create an input element as a revertable component, which can optionally be passed a default value. Any changes to that input are maintained by the component with its value data property. It will not be observing/pulling in any lastKnownGood type of value because any reversion will be pushed into the component from outside.
Externally to the revertable component, you can $emit a revert event with a new value that will cause either all revertable components or a single revertable component (with a matching ID) to be reverted.
I feel like it's mostly a clean solution (assuming I'm understanding the requirements correctly), except that in VueJS 2 we have to use a standalone, shared Vue object to pass the events when there is no parent-child relationship. So you'll see:
const revertBus = new Vue()
defined in global scope in the demo. And the revertable component will use it to receive incoming messages like so:
revertBus.$on('revert', (value, id) => { ... }
and the controlling Vue object that is triggering the messages will use it like this:
revertBus.$emit('revert', this.reversionValue, targetId)
You can also emit the event with a null value to cause the revertable component to reset its value to its initial default value:
revertBus.$emit('revert', null, targetId)
Again, it's a mostly clean solution, and though it might not fit perfectly inline with what you're trying to accomplish, I'm hoping it might at least help you get closer.
I'm not sure I'm following properly but I'll give it a shot.
What I think you want is to only update some values when their "temporary" values meet some type of condition. Here's how I was thinking of it.
<div id="app">
<input v-model="tempValues.one">
<input v-model="tempValues.two">
<input v-model="tempValues.three">
<pre>{{ values }}</pre>
<pre>{{ tempValues }}</pre>
</div>
Then, in my component, I watch tempValues and only update values when a condition is met.
new Vue({
el: '#app',
data: {
values: {
one: '',
two: '',
three: '',
},
tempValues: {},
},
created () {
// Create the tempValues based on the real values...
this.tempValues = Object.assign({}, this.values)
},
methods: {
updateValues (tempValues) {
// Only updating the values if all the tempValues are longer than 3 characters...
var noneEmpty = Object.values(tempValues).every((value) => value.length > 3)
if (noneEmpty) {
this.values = Object.assign({}, tempValues)
}
},
},
watch: {
// Watch tempValues deeply...
tempValues: {
handler (tempValues) {
this.updateValues(tempValues)
},
deep: true,
},
},
})
Here's a quick demo: https://jsfiddle.net/crswll/ja50tenf/
yourvar.__ob__.dep.notify()
works on objects and arrays
Yes, You should be able to do this with help of v-on:input. You can call a function on input and put your logic of checking and updating in this function:
<input :value="somedata" v-on:input="yourMethod">
In fact if you look at the documentation, <input v-model="something"> is syntactic sugar on:
<input v-bind:value="something" v-on:input="something = $event.target.value">
so instead of assigning variable something to value inputted, you can put your logic in that place.