What is a proper way to deal with Vue.js props that from the response of backend - vue.js

I am wondering that what is a proper way to deal with props in Vue.js if the prop is from the response of backend?
Ok, let's say the child component has a prop called person. a name is in person object.
<template>
{{ person.name }}
<template>
<script>
export default {
name: 'ChildComponent',
props:['person'],
created(){
this.getName();
},
data(){return{name:''}},
methods:{
getName(){
this.name = this.person.name;
}
}
</script>
The parent component is like this
<template>
<ChildComponent :person="person"></ChildComponent>
<template>
<script>
export default {
name: 'ParentComponent',
created(){
this.getPerson();
}
data(){
return {
person: null
}
},
methods:{getPerson(){
// send request to server or api then update name
sendrequest().then(person => { this.person = person});
}}
</script>
At first, before the get the response, there will be a warning can't get name from person.
I know 2 methods to handle this:
<ChildComponent :person="person" v-if="person"></ChildComponent>
watch person prop in, every time the person is changed, rerun the getName() method in childcomponent or set name as a computed attribute.
So here is the question again, are they the proper way to handle this? Is there still some other methods like using Vuex?
Thx

It really depends on your use case, if you don't want to show the person until it is ready then v-if seems like the right way to go.
If you want to display something until the person object is ready you have 2 options:
Give person a default name, or init it with an empty name
show a loading component until person is ready

This is not vue warning. Basically this is javascript error TypeError: Cannot read property 'name' of null, which vue may show you as warning. Your 1 variant is okey. Same as <div v-if="person">{{person.name}}</div> in your child. Also you may fill your person with some thing like
return {
person: {name: 'loading...'}
}
and in your parent
mounted: function () {
// your person will be loaded when component mounted
this.getPerson()
}

Related

Mutiple Data Source for a component

I am in a process to support multiple data source to show data in a component via props & store.
The idea is to build a list component, which will load the data from the store if no props present. Otherwise, show the data from props. Thus, I am ensuring reusability in the context of search functionality and normal listing view.
Here is the code looks like,
<template>
<div>
<li v-for="user in this.dataSource" :key="user.age">
{{ user.name }}
</li>
</div>
</template>
<script>
export default {
props: {
userData: {
type: Array,
default: null,
}
},
created() {
// dispatch action to get user data
this.$store.dispatch("GET_USER_DATA");
},
data() {
return {
dataSource: this.userData !== null ? this.userData: this.$store.state.users
};
}
};
As of now, the store holds just static data. But in the context of REST it will be async in nature. So sometimes, I see no data.
So my question is that is this logic of dataSource can be improved further?
Thanks
Robin.
You should change dataSource to computed property. It will automatically re-computed when this.userData is changed or this.$store.state.users is changed
computed: {
dataSource() {
return this.userData !== null ? this.userData: this.$store.state.users
}
}
For more information about computed in Vuejs, please check the document
This is a bad approach anyways.
Store should be used to contain the data and components should just "show" it and manipulate it through actions and mutations.
So in this case, I'd remove the props, created and data part and add a getter to the store which should be implemented in a computed in the component. Nothing fancy here.
You shouldn't need to manually load the data with a proper store setup. Always make a getter and "load" it into the component with a computed.

Component rendered before correct prop data is passed in from another component

I have a component called EditPost and that uses another component called PostForm. I am using vuex store to make an api call to retrieve the post object to be edited from the backend in the EditPost beforeCreate method and using a computed property to get the retrieved post from the store which I then pass as a prop to the PostForm component.
Since the object exists already, I want its data to be populated in the input fields of the PostForm. But the values of the object aren't there since the component is rendered before. How can I make sure the data is safely reached before the component gets rendered.
My EditPost component is basically like this:
<template>
<PostForm v-bind:key="fetchPost" />
</template>
<script>
beforeCreate() {
this.$store.dispatch('loadPost');
}
computed:
fetchPost() {
return this.$store.getters.getPost;
}
</script>
You can wait for the loadPost action to be completed in beforeCreated(), then the component won't be created before the API responds. But be aware that this is not the best practice since the user won't see anything before the API returns a response.
Example:
<template>
<PostForm v-bind:key="fetchPost" />
</template>
<script>
async beforeCreate() {
await this.$store.dispatch('loadPost');
}
computed:
fetchPost() {
return this.$store.getters.getPost;
}
</script>

Vue - instance data is not defined

I have a menu of topics (e.g. "About us", "Support") and I want to be able to tell what topic is active right now. When a topic is clicked it will become the active one. Think of it as a substitute for a router that will set an active route. This is not possible in my case since I'm working in SharePoint 2010... in a content editor. So I made the decision to have an activeTopic variable in my Vue instance.
The Vue instance
new Vue({
el: '#app',
data: {
activeTopic: '',
}
});
This is because the activeTopic has to update another component and show some data based on the active topic. Like a router showing a child route.
The topic component
Vue.component('topic', {
props: ["title"],
methods: {
setActiveTopic: function (title) {
activeTopic = title;
},
isActiveTopic: function (title) {
return activeTopic == title;
}
},
template: `
<div v-bind:class="{active: isActiveTopic(title)}" v-on:click="setActiveTopic(title)">
<p v-text="title"></p>
</div>
`
});
The setActiveTopic function works as it should when I click it; it updates the activeTopic. But the isActiveTopic function gives me this error:
Error in render: "ReferenceError: activeTopic is not defined"
What I don't understand is that I can edit the activeTopic variable but I can't make a comparison? I've tried setting a default value but it still says it is undefined.
activeTopic should be assigned to the Vue component by setting and reading this.activeTopic. As is, you have two independent activeTopic variables scoped within each of your component methods.
You'll also want to include activeTopic in the component's data block, rather than on the Vue object itself, since it's specific to the component.
(If there's a reason to put that variable on the Vue object you would either want to pass it down to the component as a prop, or else access it directly as Vue.activeTopic instead of this.activeTopic. I'm honestly not certain whether Vue would treat that last structure as a reactive value within components -- I've never had a reason to try that, and can't think of a situation where that'd be a reasonable architecture.)
Vue.component('topic', {
props: ["title"],
data() { return {
activeTopic: ''
}},
methods: {
setActiveTopic(title) {
this.activeTopic = title;
},
isActiveTopic(title) {
return this.activeTopic == title;
}
},
template: `
<div v-bind:class="{active: isActiveTopic(title)}" v-on:click="setActiveTopic(title)">
<p v-text="title"></p>
</div>
`
});
I am new to Vue, and have seen data being defined as an object in several examples.
But apparently, as per the documentation, you have to use data as a function that returns an object.
This is to ensure that all instances will have it's own version of the data object, and is not shared with all instances.
Instead of
data: {
count: 0
}
use
data: function () {
return {
count: 0
}
}
However I still don't know why in one of my component, data as an object worked, and in a child component with it's own data as an object, I got the error count is undefined;
I am assuming, the root element, (defined by new Vue({}) has to be a singleton, and is not intended to have several instances.
And since components, can have instances, the data in that needs to be defined as a function.

Vuejs - - Assigning prop value in child after ajax request in parent.

I am sure I am missing something simple here. I have created a reusable child component that includes a input like the following, and I am assigning the initialValue in the data object from the itemValue prop passed to it from the parent.
<template>
<label>{{itemLabel}}</label>
<input v-model="initialValue" type="text" >
</template>
<script>
export default {
props: ['itemValue'],
data(){
return {
initialValue: this.itemValue,
}
}
</script>
If in the parent component I assign the item-value property directly with a string it works fine.
The problem is I want to set the item-value after making an ajax call in the parent, so I am binding it to a data object property that is set by a method using beforeMount()
<v-child-component :item-value="theValue"></v-child-component>
And...
data(){
return {
theValue: null,
}
},
methods: {
setvalue(){
//make ajax axios get request here then set this.theValue
}
}
beforeMount(){
this.setValue();
}
When I do it this way the it seems the child's item-value is bound to the null value before ajax call completes and sets the actual value. How can I achieve my purpose here?
If you don't want the component to render until theValue is set, use the v-if directive:
<v-child-component v-if="theValue !== null" :item-value="theValue"></v-child-component>

Vue 2 pass props to child [old : "call child's method"]

ok so I've learned that I'm not supposed to call a child's method but pass it props instead.
I've got (parent) :
<template>
<div id="main">
<Header :title ="title"/>
<router-view/>
<LateralMenu/>
</div>
</template>
<script>
export default {
name: 'app'
data: function () {
return {
title: true
}
},
methods: {
hideTitle: function () {
this.title = false
console.log(this.title)
},
showTitle: function () {
this.title = true
console.log(this.title)
}
}
}
</script>
and (child) :
<script>
export default {
name: 'Header',
props: ['title'],
created () {
console.log(this.title)
},
methods: {
}
}
</script>
the first console logs (inside the parent) print correctly on each method but the second console log within the child stays true all the time. I got this from : Pass data from parent to child component in vue.js
inside what method does the console.log need to be to be printed everytime the methods in the parent are triggered?
(this is why I wanted to go for method-calling, originally, by going with variables instead, we're potentially omitting valuable parts of the process such as optimization and a "when" for the execution(s!!) of our code. pontetally being the key word here, don't blow up on me, keep in mind that I'm learning.)
OLD:
I've browsed the web and I know there a a million different answers
and my point is with the latest version of vue none of those millions
of answers work.
either everything is deprecated or it just doesn't apply but I need a
solution.
How do you call a child method?
I have a 1 component = 1 file setup.
DOM is declared inside a <template> tag javascript is written inside
a <script> tag. I'm going off of vue-cli scaffolding.
latest method I've tried is #emit (sometimes paired with an #on
sometimes not) doesn't work :
child :
<script>
export default {
name: 'Header',
created () {
this.$on('hideTitlefinal', this.hideTitlefinal)
},
methods: {
hideTitlefinal: function () {
console.log('hideeeee')
},
showTitlefinal: function () {
console.log('shwowwww')
}
}
}
</script>
parent :
<template>
<div id="main">
<Header v-on:hideTitle="hideTitlefinal" v-on:showTitle="showTitlefinal"/>
<router-view/>
<LateralMenu/>
</div>
</template>
<script>
export default {
methods: {
hideTitle: function () {
this.$emit('hideTitle')
},
showTitle: function () {
this.$emit('showTitle')
}
}
}
</script>
console :
Uncaught TypeError: this.$emit is not a function
at Object.showTitle (Main.vue?1785:74)
at VueComponent.showTitle (LateralMenu.vue?c2ae:113)
at boundFn (vue.esm.js?efeb:186)
at invoker (vue.esm.js?efeb:1943)
at HTMLDivElement.fn._withTask.fn._withTask (vue.esm.js?efeb:1778)
Please don't do this. You're thinking in terms of events. When x happens, do y. That's sooo jquery 2005 man. Vue still has all that stuff, but we're being invited to think in terms of a view model...
You want your state in a variable, in window scope, and you want reactive pipes linking your vue stuff to your state object. To toggle visibility, use a dynamic class binding, or v-if. Then think about how to represent your state. It could be as simple as having a property like store.titleVisible. But, you want to 'normalize' your store, and avoid relationships between items of state. So if title visibility really depends on something higher up, like an editMode or something, then just put the higher-up thing in the store, then create computed properties if you need them.
The goal is that you don't care when things happen. You just define the relationships between the markup and the store, then let Vue take care of it. The docs will tell you to use props for parent=>child and $emit for child=>parent communication. Truth is you don't need this until you have multiple instances of a component, or reusable components. Vue stuff talks to a store, not to other vue stuff. For single-use components, as for your root Vue, just use the data:.
Whenever you find yourself writing show/hide methods, you're doing it wrong. It's intuitive (because it's procedural), but you'll quickly appreciate how much better the MVVM approach is.