Is modifying v-model indirectly an anti pattern? - vue.js

In Vue 3 the following line of code produces an error:
<input v-model="modelValue[idx].name" v-for="(item, idx) in modelValue" :key="idx">
The error is the following:
error Unexpected mutation of "modelValue" prop vue/no-mutating-props
This makes sense, since modifying property values is considered an anti-pattern:
https://v2.vuejs.org/v2/guide/migration.html#Prop-Mutation-deprecated
Yet the following line of code compiles and runs happily:
<input v-model="item.name" v-for="(item, idx) in modelValue" :key="idx">
I still modify the very same object, the only difference is that I refer to it indirectly. Is this OK, or is this an anti pattern that the linter fails to detect?

I have found the answer in the documentation:
Warning
Note that objects and arrays in JavaScript are passed by reference, so if the prop is an array or object, mutating the object or array itself inside the child component will affect the parent state and Vue is unable to warn you against this. As a general rule, you should avoid mutating any prop, including objects and arrays as doing so ignores one-way data binding and may cause undesired results.
https://v3.vuejs.org/guide/component-props.html#one-way-data-flow

Related

Expects an Object or Array value in Vue [ Warn ]

I've been running into a warning that I want to hide/fix. I read you can hide warnings using Vue.config.ignoredElements, and have added the code below in my main.js file:
Vue.config.ignoredElements = [
'slot v-bind without argument expects an Object',
'Expected Object, got Array',
'v-bind without argument expects an Object or Array value'
]
Is there a specific option I need to add? or a better way to fix this problem?
This issue might be related: https://github.com/vuejs/vue/issues/6677
I think you've misunderstood. ignoredElements solves a very specific problem.
Typically when Vue is rendering it comes across 3 types of element:
HTML elements, such as <div>.
Special Vue elements, such as <template> or <slot>.
Components, such as <v-select>.
Vue has a list of HTML elements hard-coded so that it can identify the first group. See:
https://github.com/vuejs/vue/blob/399b53661b167e678e1c740ce788ff6699096734/src/platforms/web/util/element.js#L11
If it comes across an element with a name it doesn't recognise it will log an error. Most of the time that's fine but occasionally you might need Vue to treat an unknown element just like a plain HTML element. That can be achieved by adding it to ignoredElements. See https://v2.vuejs.org/v2/api/#ignoredElements for more details.
It is not used to suppress warning messages more generally.
You mentioned three messages:
slot v-bind without argument expects an Object
Expected Object, got Array
v-bind without argument expects an Object or Array value
In all cases these mean that there's a bug in your code. You shouldn't be trying to suppress these warnings, you should be fixing the bugs.

Vue/vuetify v-switch: what is input-value?

Can someone explain to me exactly what the input-value attribute does on the v-switch component?
I think it has something to due with using the component with vuex, when you cannot use v-model directly.
It seems to be working for me, but I don't understand it exactly.
You can see the attribute here: https://vuetifyjs.com/en/components/selection-controls#api
Where it is described as: "The v-model bound value".
(I originally found the attribute in an example somehere.)
input-value behaves like a default value attribute that you would expect in other components.
Normally v-model is syntax sugar for :value="value" :input="$emit('input', $event.target.value)", but we can change it.
from selectable.js:
model: {
prop: 'inputValue',
event: 'change'
},
So the above lines (see vue docs) make your v-model bind to input-value instead of value likely because some components i.e. checkbox (which v-switch uses) have value attribute reserved for something else.
So value attribute is then used to set the value which will be represented when the component is checked.
And in v-switch case v-model is syntax sugar for something like :input-value="value" #change="value = $event"
Codepen

Vue $refs and kebab case

In vue 1 it was possible to do this:
<app v-ref:test-app></app>
and then reference it like so:
vm.$refs.testApp;
however in vue 2 the syntax for the ref has changed to:
<app ref="test-app"></app>
but this no longer can be referenced by
vm.$refs.testApp
instead it only works if:
<app ref="testApp"></app>
which in standard DOM stuff isn't allowed. Is it a bug? can kebab case no longer be used?
Since the syntax has been changed from that of a namespaced element attribute (i.e., v-ref:foo-bar) to a normal key-value-pair attribute (i.e., ref="fooBar"), the implicit kebab-case-to-camel-case conversion is no longer applicable because the reference name is a plain string and is not constrained by having to conform to the requisite lowercase-kebab-case HTML syntax.
In other words, you can identify a ref with any string, so it wouldn't make sense for Vue to manipulate it. Have a look at this CodePen for an example in action of what I mean.
But, basically, a plain string ref value means you can define a reference like this:
<div id="app" ref="test ** app!"></div>
and reference it from Vue like this:
this.$refs['test ** app!']
In short, no, it's not a bug but no, automatic kebab-case conversion no longer takes place.

Difference between v-bind and {{}}?

I have an input field with the value field being passed a string stored in Vuex.
The input fields changes are debounced and the new string synced to Vuex.
When bound like this :value="vuexState.myString, when typing, the cursor jumps to the end of the line.
When bound like this value={{vuexState.myString}}, the cursor stays where it is.
According to the guide: http://vuejs.org/guide/syntax.html#Arguments
These two should be the same, with the {{ }} style being internally converted to :bind. Could this be a bug?
My theory is that the cursor jumping occurs because the vuex state change re-renders the input and that the {{ }} style is interpolated only once while the binding syntax re-renders the input every change.
I am currently using value={{vuexState.myString}} but I'd like to know what is happening or if there is a better way to do this.
It's in the documentation about Interpolation and has been deprecated (see. Migration guit from 1.x)
Deprecated
This is the old way
<div class="btn btn-primary hint--top {{class}}"></div>
Solution
Use Javascript expression instead:
<div v-bind:class="'btn btn-success hint--top '+ class "></div>
Take a look at the console, it seems like it has been deprecated in favour of the colon syntax or v-bind:
vue.js:2237 [Vue warn]: foo="{{foo}}": Interpolation inside attributes has been deprecated. Use v-bind or the colon shorthand instead.
v-text:'something' === {{something}}

The data-source property isn't propagated from reagent to the render method of the React Native ListView component

I'm trying to render a React Native ListView in reagent.
I have the following snippet:
(def data-source
(React.ListView.DataSource. #js{:rowHasChanged (fn [a b] false)}))
(defn render-row []
[ui/view])
(def rows
(clj->js ["whoa", "hey"]))
(defn main-scene []
(fn []
[ui/list-view {:render-row render-row
:data-source (.cloneWithRows data-source rows)}]))
The above leads to "Failed propType: Required prop dataSource was not specified in ListView. Check the render method of app.ios.ui.main_scene." Which is followed by "Cannot read property 'rowIdentities' of undefined" as the data source is undefined inside the render method of the ListView.
My first guess was there was some special treatment of the "data-" attributes somewhere in the internals of either reagent/hiccup or whatever, but I couldn’t find a single clue to why the property is not propagated properly.
And, yep, (.cloneWithRows data-source rows) actually returns a valid ListViewDataSource object instance.
And then if I pass :data-source as :dataSource all I get is a puzzling "StaticRenderer.render(): A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object."
I’m using react-native 0.18.1 and reagent 0.5.1 with re-frame 0.6.0. I have checked this with reagent 0.6.0-alpha and re-frame 0.7.0-alpha and got the same errors.
I've been digging this for several hours and I guess I need some help. Any hints/ideas to try to fix this, any references in the code to look at? Thanks a ton in advance.
Well, the first thing is the data source should be passed to the view as :dataSource and not as :data-source. The latter doesn’t work for whatever reason. As a side note, the render row method can both be passed as :render-row or :renderRow and it works both ways. Heh.
Secondly, the render-row function should return a React component and not an array. The latter would be okay if the array was later rendered by reagent, but the listview doesn’t do any kind of post-processing on the data it gets from render-row and merely tries to return that to React, which bails if it’s a plain clojure array.
So the above render-row function should be written as:
(defn render-row []
(r/as-element [ui/view]))
And then everything works fine. :)