Passing entire data object as props - vue.js

Is it possible in Vue to pass the whole data object as props?
For example
Vue.component('comp', {
props: ['allData'],
template: '<div>{{allData.msg}}</div>'
})
new Vue({
el: "#test",
data: {
msg: "Hello"
}
})
In my view:
<div id="test">
<comp :allData="data"></comp>
</div>

It's possible like this:
<div id="test">
<comp :allData="$data"></comp>
</div>
However, mutating allData in the component will affect the parent's state since it's an object. See the warning from Vue 1.0 docs below:
Note that if the prop being passed down is an Object or an Array, it is passed by reference. Mutating the Object or Array itself inside the child will affect parent state, regardless of the binding type you are using.
and Vue 2.0 docs
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 will affect parent state.

You can access the whole object via $data, and pass it.
But it's usually not the best idea to mess with it.

Related

reactivity disappears after the object was in the array

Why, after passing through the reactive array, did the element's reactive property turn into a non-reactive one? Can you explain please.
<script setup lang="ts">
import { ref, Ref } from 'vue'
interface Obj {
a: number;
r: Ref<{s:string}>;
}
const obj: Obj = { a: 1, r: ref<string>({s:"one"}) }; //create an instance
const valBeforeArr=obj.r.value //the variable is displayed as it should be
//put an instance in a reactive array, and get from it
const arr = ref<Obj[]>([]);
arr.value.push(obj)
const valAfterArrRef=arr.value[0].r.value //reference value missing
const valAfterArrNonRef=arr.value[0].r //and the original value is displayed normally
</script>
<template>
<h2>valBeforeArr: {{ valBeforeArr }}</h2>
<h2>valAfterArrRef: {{ valAfterArrRef ?? 'underfined' }}</h2>
<h2>valAfterArrNonRef: {{ valAfterArrNonRef ?? 'underfined' }}</h2>
</template>
playground
As explained to me
Estus Flask, reactivity has not disappeared. You can verify this with the following code:
<template>
<h2>valBeforeArr: {{ valBeforeArr }}</h2>
<h2>valAfterArrRef: {{ valAfterArrRef ?? 'underfined' }}</h2>
<h2>valAfterArrNonRef: {{ valAfterArrNonRef ?? 'underfined' }}</h2>
<button #click="obj.r.value.s='seven'">updateBeforeArr</button>
<button #click="arr[0].r.s='five'">updateAfterArr</button>
</template>
But in one case you have to access using ".value", and in the other, without it. This is inconvenient, given that the object, in accordance with the business logic, can either pass through the array or not. How to access a property universally?
The documentation explains this:
When a ref is accessed or mutated as a property of a reactive object,
it automatically unwraps to the inner value so it behaves like a
normal property
<...>
Ref unwrapping only happens when nested inside a reactive Object.
There is no unwrapping performed when the ref is accessed from an
Array or a native collection type like Map
Since ref is deeply reactive, arr.value elements are reactive objects, and refs in obj properties are unwrapped when it's added to the array and becomes reactive.
shallowRef can be used in order to avoid unwrapping in nested refs.

Vue: How to perform reactive object change detection in v-for?

I read this documentation but cannot use the proposed solution.
I have a v-for loop over objects. These objects are changed dynamically over time and I need that change to show reactively in the v-for loop.
<b-row lg="12" v-for="data in objects" :key="data.id">
<div v-if="data.loading">
loading...
{{data.loading}}
</div>
<div v-else>
loaded, done
{{data.loading}}
</div>
</b-row>
In my methods, I have a for loop that downloads data for each object and changes the object value like this:
for(var i = 0; i<response.ids.length; i++){
var newId = response.ids[i].id
this.objects.newId = {"loading":true, "id": newId}
downloadSomething(newId).then(res => {
this.objects.newId = res[0] //<-- this change needs to be shown reactively.
})
}
According to Vue documentation, Object changes are not reactive:
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` is now reactive
vm.b = 2
// `vm.b` is NOT reactive
Vue propses some workaround like this:
Vue.set(vm.userProfile, 'age', 27)
UPDATE
But for my case, this just creates a new parameter in the object with the same ID and creates a duplicate key warning and other problems.
I also tried Vue.delete just before Vue.set but it is not actually deleting.
Is there a way to not replace the key/value pair but add more/change parameters to the first child of the root with the ID of newID
Thanks!
Solution: Replace this.objects.newId = res[0] with this.$set(this.objects, 'newId', res[0]) and it should work.
Explanation: this.$set is just an alias to Vue.set, available within any Vue instance. Bear in mind that vm (stands for view model) in the example is the same as this and equals to Vue component instance. Also due to ES5 JS restrictions, you have to set objects' properties explicitly via Vue.set/this.$set to manually trigger re-render. This problem will be resolved when Vue 3.0 is released.
Hope this helps, if you need any clarifications - feel free to ask.
Try that:
downloadSomething(newId).then(res => {
this.$set(this.objects, 'newId', res[0])
})
you need Vue.set() when you want to define a new property to an existing object (not directly to the data), and this function will assign the new property to the built-in watcher, and it will become reactive as well.
its all being explained in the docs: https://v2.vuejs.org/v2/guide/reactivity.html
in your case, it seems to be all you need to make it work. in my example:https://jsfiddle.net/efrat19/eywraw8t/484131/ an async function fetches the data and define a new property to contain te response.
new Vue({
el: "#app",
data: {
objects:{
property1:12
}
},
methods: {
fetchDataAndAddProperty(response){
fetch('https://free.currencyconverterapi.com/api/v6/countries').then(res =>
res.json()).then(data => Vue.set(this.objects,'property2',data))
}
}
})
and the template:
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app">
<div lg="12" v-for="(val,key,index) in objects" :key="index">
{{key}}:{{val}}
</div>
<button #click="fetchDataAndAddProperty()">fetch</button>
</div>
as you can see in the fiddle, the new property becomes reactive and being displayed as well.

How to pass bound variables to a subcomponent? [duplicate]

I'm trying to pass php/laravel data to my component from a custom global variable. I've seen examples of this going into the new Vue({}) area directly but I haven't seen any way to pass this by going into right into the component
<script>
var itemData = //json object
</script>
<custom-component item-data="ITEMDATAVAR"></custom-component>
I should specify that I do have item-data in my component props. The issue is that I'm not sure how to tell my component's html that I'm passing the value of the variable itemData and not the string "itemData"
I think you are referring to dynamic props
<custom-component v-bind:item-data="ITEMDATAVAR"></custom-component>
or use the shorthand syntax
<custom-component :item-data="ITEMDATAVAR"></custom-component>
You should add the item-data to the props array like this:
Vue.component('custom-component', {
props: ['item-data'],
...
}
You can research this Vue.js example
Create a variable
new Vue({
el: '#el',
data: yourJsonObject
})
In you component you have to write about props
Vue.component('custom-component', {
props: ['item-data']
...
}
Pass the data to the component the same way
<custom-component item-data="ITEMDATAVAR"></custom-component>
I have not tested how it will work, guided by the documentation.

How to pass props without creating setters and getters?

In the code below I pass an object to a child component. Vue creates a pair of setter/getter for each property in this object. In other words, it binds each property to make the component reactive. Is there a way to pass an object, like I'm doing here, but without binding? In a real life application I pass an object with ten's of properties and a setter/getter pair is created for each also. This impacts performance a bit. What would you recommend?
Vue.component('child', {
template: '<div>Child!</div>',
props: ['params'],
created () {
console.log(this.params)
}
})
var app = new Vue({
el: '#app',
data () {
return {params: {a: 1, b: 2}}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<div id="app"><child :params="params"></child></div>
No.
I would be highly surprised if converting your properties into getters/setters, which is at the core of Vue's reactivity, is the cause of a performance issue.
The only way to pass a property would be to expose it to Vue at some point, which means that it will be converted to getter/setters when you expose it. In order pass the object without them, you would need to do something like JSON.stringify the object and JSON.parse it on the other side. Then, as soon as you try to use in in your child (by adding it as a data property for example) it's going to be converted into a reactive object again.

In Vue 2 after passing props v-for does not get updated after items are removed

If I pass an array of objects using props in Vue 2 and on this array I use the v-for directive, view does not get updated if one of the array elements get removed.
This seems to work only if the v-for elements are declared as data, but my component needs to receive props...
In the example below you can see that the elements in the services array are indeed removed, but the v-for isn't triggered.
I'm pretty sure I'm doing here something wrong...
Vue.component('location-service-list', {
props: ['services'],
template: '<div>{{ services }}<div v-for="(service, index) in services">{{ service.id }} - {{ service.name }} <a #click.prevent="remove(index)">remove</a></div></div>',
methods: {
remove(index) {
this.services.splice(index, 1);
console.log(this.services);
},
}
});
const app = window.app = new Vue({
el: '#admin-app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.8/vue.js"></script>
<div id="admin-app">
<location-service-list :services='[{"id":1,"name":"Test 1"},{"id":2,"name":"Test 2"}]'></location-service-list>
</div>
Try defining your servicesList inside the root component as follows:
const app = window.app = new Vue({
el: '#admin-app',
data: {
servicesList: [{"id":1,"name":"Test 1"},{"id":2,"name":"Test 2"}]
}
});
And your template as:
<div id="admin-app">
<location-service-list :services='servicesList'></location-service-list>
</div>
Now it will work alright without any issues. It was not working earlier because you passed it as a constant / immutable object (JSON string in the parent template which always evaluates to the same value whenever the parent template re-renders).
Technically you are not supposed to change objects passed via props in the child component. If you do the same to a string value that is passed via props, you will get an error message like:
[Vue warn]: Avoid mutating a prop directly...
To process this remove action from parent component, you may refer to the answer under this question: Delete a Vue child component
The jsFiddle in that answer provides a way to send an event from child component to parent component, so that the appropriate child component can be deleted.