can not assign props to data - vue.js

This is a simple component. I'm trying to assign props to data as docs said so. (the initialData comes from vuex and database)
<template>
<section>
{{ initialData }}
{{ privateData }}
</section>
</template>
<script>
export default {
name: 'someName',
props: [
'initialData'
],
data() {
return {
privateData: this.initialData
};
}
};
But, the problem is initialData is OK, but privateData is just an empty object {}.
Weirdest thing is, if I save my file again, so webpack hot reloads stuff, privateData also gets the proper data I need.
Here is the parent:
<template>
<section v-if="initialData">
<child :initial-data="initialData"></micro-movies>
</section>
</template>
<script>
export default {
name: 'parentName',
data() {
return {};
},
computed: {
initialData() {
return this.$store.state.initialData;
}
},
components: {
child
}
};
</script>
I know that it's about getting data dynamically . because if I change initialData in parent to some object manually, it works fine.

The data function is only ever called once at component creation. If initialData is not populated at that point in time, then privateData will always be null. That is why you probably want to use a computed property, or watch the property.

Related

Vue: Updating a data property triggers re-evaluation of entire data?

I've been working on Vue project for almost a year, and I've just observed unexpected behavior below for the first time today...
Here is a link to code sandbox:
https://codesandbox.io/s/2bnem?file=/src/App.vue
And a code snippet from above link:
<template>
<div>
<div>{{a}}</div>
<div>{{translator(b)}}</div>
<input v-model="a" />
</div>
</template>
<script>
export default {
data() {
return {
a: 'a',
b: 'b',
}
},
computed: {
translator: function() {
return function(value) {
console.log(`translated...: ${value}`)
return value
}
}
}
}
</script>
Now every time I hit the key on input, the translator is triggered.
Is this a correct behavior?
If so, what is the cause of this problem or a background reasoning of this behavior?
Note that my vue version is 2.6.14(latest).
Your original issue is that you were attempting to use a method to render parts of your template. Methods used like this will execute for every update cycle, regardless of what changed.
The better solution is to use a computed property. Here's a somewhat dynamic example that wraps each of your data properties with a computed translator_x property
<template>
<div>
<div>{{ a }}</div>
<div>{{ translator_b }}</div>
<input v-model="a" />
</div>
</template>
<script>
const defaultData = {
a: "a",
b: "b"
}
export default {
data: () => ({ ...defaultData }),
computed: Object.fromEntries(Object.keys(defaultData).map(k => [
`translator_${k}`,
vm => {
console.log("translated...", k)
return vm[k]
}
]))
};
</script>
Each translator_x property will only be evaluated if the underlying data property is changed.

How to wrap the slot in a scoped slot at runtime

I have an very unusual scenario.
<WrapperComponent>
<InnerComponent propA="Static"></InnerComponent>
</WrapperComponent>
The WrapperComponent should manage all instances of the InnerComponent. However I don't know what will be compiled into the wrapper component.
Usually I would do something like this:
<WrapperComponent>
<template scoped-slot="{data}">
<InnerComponent v-for="(propB) in data" prop-b="Static" :prop-b="propB"></InnerComponent>
</template>
</WrapperComponent>
But I cannot do this for reasons!
I need to be able to create multiple instances of the slot content at runtime. I have created a code sandbox with what I got so far.
https://codesandbox.io/s/stupefied-framework-f3z5g?file=/src/App.vue:697-774
<template>
<div id="app">
<WrapperComponent>
<InnerComponent propA="Static"></InnerComponent>
</WrapperComponent>
</div>
</template>
<script>
import Vue from "vue";
const WrapperComponent = Vue.extend({
name: "WrapperComponent",
data() {
return {
valueMap: ["Child 1", "Child 2"]
}
},
render(h) {
return h("div", {}, [
this.valueMap.map(val => {
console.log(val);
for (const slot of this.$slots.default) {
// this is a read only slot. I can not change this.
// However, I want multiple instances of the slot
// => Inject a scoped-slot
slot.componentOptions.propsData.propB = val;
}
return h("div", {}, [this.$slots.default]);
})
])
}
});
const InnerComponent = Vue.extend({
name: "InnerComponent",
props: {
// This is a static configuration value.
propA: String,
// This is a runtime value. The parent component manages this component
propB: String
},
template: "<div>A: {{propA}} B: {{propB}}</div>"
});
export default {
name: "App",
components: {
WrapperComponent,
InnerComponent
}
};
</script>
This works perfectly fine with static information only, but I also have some data that differs per slot instance.
Because VNodes are readonly I cannot modify this.$slot.default.componentOptions.propsData. If I ignore the warning the result will just be the content that was passed to last instance of the slot.
<WrapperComponent>
<WrapperSubComponent v-for="(propB) in data" key="probB" :prop-b="prop-b">
<InnerComponent propA="Static"></InnerComponent>
</WrapperSubComponent>
</WrapperComponent>
Works after wrapping the component in another component and only executing the logic once.

Vue stored valued through props not being reactive

So I pass value using [props] and stored it in child component's data. However, when passing [props] value changes from parent, it's not updating in child component's data. Is there a fix for this..?
Here is the link to w3 test (I tried to clarify the problem as much as possible here)
<div id='app'>
<div id='parent'>
<button #click='current_value()'>Click to see parent value</button>
<br><br>
<button #click='change_value($event)'>{{ txt }}</button>
<br><br>
<child-comp :test-prop='passing_data'></child-comp>
</div>
<br><br>
<center><code>As you can see, this methods is <b>NOT</b> reactive!</code></center>
</div>
<script>
new Vue({
el: "#parent",
data: {
passing_data: 'Value',
txt: 'Click to change value'
},
methods: {
current_value(){
alert(this.passing_data);
},
change_value(e){
this.passing_data = 'New Vaule!!';
this.txt = 'Now click above button again to see new value';
e.target.style.backgroundColor = 'red';
e.target.style.color = 'white';
}
},
components: {
"child-comp": {
template: `
<button #click='test()'>Click here to see child (stored) value</button>
`,
props: ['test-prop'],
data(){
return {
stored_data: this.testProp
}
},
methods: {
test(){
alert(this.stored_data);
}
},
watch: {
stored_data(){
this.stored_data = this.testProp;
}
}
}
}
});
Props have one way data flow, that's why it doesn't react when you update it from the parent component. Define a clone of your prop at data to make it reactive, and then you can change the value within the child component.
Short answer: you don't need stored_data. Use alert(this.testProp) directly.
Long answer: when child component is created, stored_data get it's value from this.testProp. But data is local, it won't change automatically. That's why you need to watch testProp and set it again. But is not working because of a simple mistake, your watch should be:
watch: {
testProp(){ // here was the mistake
this.stored_data = this.testProp;
}
}

How to bind a local component's data object to an external component

how do you use a local component's data attriutes to bind an external component's v-model
for example i have this component
<publish-blog>
<VueTrix v-model="form.editorContent">
</publish-blog>
so the form.editorContent there refers to the publish-blog component's form.editorContent inside data, how do I do that ?
You can pass a prop to the publish-blog component.
This would be what ever page or component you are using the publish blog on, though to be honest I'm not sure why you would not just put the VueTrix component inside of the publish-blog component.
This would be on what ever page/component you are wanting it on.
<template>
<PublishBlog :trix="trix">
<VueTrix v-model="trix" />
</PublishBlog>
</template>
<script>
import PublishBlog from './PublishBlog.vue';
export default {
components: {
PublishBlog,
},
data() {
return {
trix: '',
};
},
};
</script>
and inside of the publish blog component make the form.editorContent the prop passed or a default value.
But without a global store/state you are stuck with props.
UPDATE: Showing what a publish blog component might look like
PublishBlog.vue
<template>
<section>
what ever goes here.
<slot />
</section>
</template>
<script>
export default {
name: 'PublishBlog',
props: {
trix: {
type: String,
default: '',
},
},
data() {
return {
form: {
editorContent: this.trix
},
};
},
};
</script>

VueJS Passing computed data as a prop returns undefined

I've tried and tried, but i can't figure it out the problem. From what I could read elsewhere, the variable passed to the child component gets sent as undefined before the data is available in the parent.
Please see here for reference:
the code in codesandbox
<template>
<div id="app">
<child :parentData="data.message"/>
</div>
</template>
<script>
import Child from "./components/Child";
export default {
name: "App",
components: {
Child
},
computed: {
quote() { return 'Better late than never' }
},
data() {
return {
data: { message: this.quote } ,
thisWorks: { message: "You can see this message if you replace what is passed to the child" }
};
}
};
</script>
Then in the child:
<template>
<div>
<h1>I am the Child Component</h1>
<h2> {{ parentData }}</h2>
</div>
</template>
<script>
export default {
name: "Child",
props: {
parentData: { type: String, default: "I don't have parent data" },
},
};
</script>
The answer is, you cannot access the value of this.quote because at the moment the data objectis creating, the computed object actually does not exist.
This is an alternative, we will use the created() lifecycle hook to update the value of data object:
created(){
this.data = {
message: this.quote
}
},
You don't need to change any things, just adding those line of codes is enough.
I've already tested those codes in your CodeSandbox project and it works like a charm.
Hopefully it helps!