Reputation: 1668
I set up my code as follows, and I was able to update checkout_info
in App.vue from the setter in SomeComponent.vue, but the getter in SomeComponent.vue is not reactive.
// App.vue
export default {
provide() {
return {
checkout_info: this.checkout_info,
updateCheckoutInfo: this.updateCheckoutInfo
}
},
data() {
return {
checkout_info: {},
}
},
methods: {
updateCheckoutInfo(key, value) {
this.checkout_info[key] = value
}
}
}
// SomeComponent.vue
export default {
inject: ['checkout_info', 'updateCheckoutInfo']
computed: {
deliveryAddress: {
get() { return this.checkout_info.delivery_address }, // <---- Not reactive??
set(value) { return this.updateCheckoutInfo('delivery_address', value) }
}
}
}
Upvotes: 15
Views: 24153
Reputation: 6501
As others mentioned, you'll need to provide a reactive object instead of a value.
You can pass a Proxy
that wraps around the component's $data object, and exposing only selected properties.
for example:
// App.vue
export default {
data() {
return {
checkout_info: {},
}
},
provide() {
const exposedProps = ['checkout_info']; // <--- notice this list of properties to be exposed
const providedState = new Proxy(this.$data, {
get(target, prop, receiver) {
return exposedProps.includes(prop) ? Reflect.get(...arguments) : undefined;
},
ownKeys(target) {
return Reflect.ownKeys(target).filter(key => exposedProps.includes(key));
}
});
return {
providedState
}
}
}
// SomeComponent.vue
export default {
inject: ['providedState']
computed: {
deliveryAddress: {
get() { return this.providedState.checkout_info.delivery_address },
set(value) { return this.providedState.checkout_info.delivery_address = value; }
}
}
}
To expose multiple data properties, you only need to add their keys to the exposedProps
array.
I usually use a tweaked version of the proxy to make the props read-only.
Upvotes: 0
Reputation: 275
If you are using Vue.js 2.7 or later, which has Composition API built-in, you can import and use 'computed' to make provided data reactive:
Parent Component:
import { computed } from 'vue'
export default {
provide() {
return {
foo: computed(() => this.bar)
}
},
data() {
return {
bar: 'Hello World'
}
}
}
Child Component:
export default {
inject: ['foo']
}
Upvotes: 0
Reputation: 551
I would put the values in an object:
var Provider = {
provide() {
return {
my_data: this.my_data
};
},
data(){
const my_data = {
foo: '1',
fuu: '2'
};
return {
my_data;
}
}
}
var Child = {
inject: ['my_data'],
data(){
console.log(my_data.foo);
return {};
},
}
When are object properties they are reactive. I don't know if this is the correct solution but it works in my case.
Upvotes: 4
Reputation: 61
This note from official documentation.
Note: the provide and inject bindings are NOT reactive. This is intentional. However, if you pass down an observed object, properties on that object do remain reactive.
I think this is the answer to your question.
source: https://v2.vuejs.org/v2/api/#provide-inject
Upvotes: 6
Reputation: 1668
I found the answer after many hours of searching. You have to use Object.defineProperty
to make it reactive. I'm not sure if this is the best approach, but this is a working example.
export default {
data() {
return {
checkout_info: {},
}
},
provide() {
const appData = {}
Object.defineProperty(appData, "checkout_info", {
enumerable: true,
get: () => this.checkout_info,
})
return {
updateCheckoutInfo: this.updateCheckoutInfo,
appData,
}
}
}
You can later access it via this.appData.checkout_info
Upvotes: 23