Props change parent if object Vue js - javascript

My question it's more a search for explanation.
I have two components, father and child.
Father:
<template>
<Child :data="testData" />
{{testData}} //for print data in father
</template>
<script>
export default {
name: "Father",
data() {
return {
testData: {name: "hello"}
}
}
}
</script>
And then My Child
<template>
<input v-model="data.name" />
</template>
<script>
export default {
name: "Child",
props: ["data"]
}
}
</script>
As documentation say in this section:
https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow
I expected that it will return error, but no, it's work, if I try to change input the father component show the correct new value, without console error.
So I have tested with simple string for data instead of object in father like:
Father :
<template>
<Child :data="testData" />
{{testData}} //for print data in father
</template>
<script>
export default {
name: "Father",
data() {
return {
testData: "hello"
}
}
}
</script>
Child :
<template>
<input v-model="data" />
</template>
<script>
export default {
name: "Child",
props: ["data"]
}
}
</script>
And in this case I will get the correct error:
[Vue warn]: Avoid mutating a prop directly since the value will be
overwritten whenever the parent component re-renders. Instead, use a
data or computed property based on the prop's value. Prop being
mutated: "data"
found in
Yes I know that in JS the object are different and "magic", but I expected that Vuejs not allowed this.
Is this desired/due(for JS specifics) behavior?
Thankyou.

In both exemples, your prop value is an object, or a link to an object (and its attributes/properties).
When you use v-model="data.name", you are not changing the prop, you are following the link, accessing the object, and only changing one of its attributes.
Can't say if this is bad practice, but it works great for me.
In the second exemple, the v-model="data" would actually change the link to another object, not only the prop internals.
This results in an error.
This thread can also help: https://forum.vuejs.org/t/pass-object-via-props-reference-vs-emit-value-best-practices/45683/5

Related

How to re-render a component (Vue) with its data (and state) intact?

Here's how my code is structured: parent component shuffles through child components via v-if directives, one of the child components is using a state to define its data. Everything works except when I switch between the child components. When I get back, no data can be shown because the state has become null.
Parent component:
<template>
<div>
<Welcome v-if="view==0" />
<Courses v-if="view==1" /> //the component that I'm working on
<Platforms v-if="view==2" />
</div>
</template>
Courses component:
<template>
<div>Content</div>
</template>
<script>
export default {
name: 'Courses',
computed: {
...mapState([
'courses'
])
},
data () {
return {
courseList: [],
len: Number,
}
},
created () {
console.log("state.courses:")
console.log(this.courses)
this.courseList = this.courses
this.len = this.courses.length
},
}
</script>
Let say the default value for "view" is 1, when I load the page, the "Courses" component will be shown (complete with the data). If I click a button to change the value of "view" to 0, the "Welcome" component is shown. However, when I tried to go back to the "Courses" component, the courses component is rendered but is missing all the data.
Upon inspection (via console logging), I found that when the "Courses" component was initially rendered, the state was mapped correctly and I could use it, but if I changed the "view" to another value to render another component and then changed it back to the original value, the "Courses" component still renders but the state became undefined or null.
EDIT: Clarification.
Set courseList to a component name and use <component>
and set some enum
eg.
<template>
<component :is="this.viewState" />
</template>
<script>
export default {
// your stuff
data() {
return {
viewState: 'Welcome',
}
},
methods: {
getComponent(stateNum) {
const States = { '1': 'Welcome', '2': 'Courses', '3': 'Platforms' }
Object.freeze(States)
return States[stateNum]
}
},
created() {
// do your stuff here
const view = someTask() // someTask because I don't get it from where you're getting data
this.viewState = this.getComponent(view)
}
}
</script>
I don't actually understood correctly but here I gave some idea for approaching your problem.

How to pass values between parent and child components in vue?

I defined two different components:
'permissionTitle':customTitle,
'permissionItem':customItem,
in main template they are organized like this:
<permissionTitle content="品牌商管理">
<permissionItem>查看列表</permissionItem>
<permissionItem>添加</permissionItem>
<permissionItem>修改</permissionItem>
<permissionItem>删除</permissionItem>
</permissionTitle>
Now I want to pass values from permissionItem to permissionTitle and vice versa.
How to can I do this?
In permissionTitle.vue:
<template>
<div id="root">
<Checkbox>
<span>{{content}}</span>
</Checkbox>
<slot></slot>
</div>
</template>
In permissionItem.vue:
<template>
<Checkbox #on-change="change">
<slot></slot>
</Checkbox>
</template>
You can do this with v-model.
Add a model and prop to your child component, something like this:
Vue.component('permissionItem', {
model: {
prop: 'value',
event: 'change'
},
props: {
value: String
},
methods: {
// or however this value changes in your component
changeValue(newValue) {
this.value = value;
$emit('change', this.value;
....
And instantiate it like this:
<permissionTitle content="品牌商管理">
<permissionItem :v-model="value1">查看列表</permissionItem>
<permissionItem :v-model="value2">添加</permissionItem>
<permissionItem :v-model="value3">修改</permissionItem>
<permissionItem :v-model="value4">删除</permissionItem>
</permissionTitle>
variables 'valueN' are now available in your parent component.
Vue's documentation on this topic is a little lacking, but its basically here: https://v2.vuejs.org/v2/guide/components-custom-events.html

Vuejs moving data between multiple components

I have a parent component with two children components that I need data to move between. I can get emitted data from one child (multiselect.vue) to the parent, but cannot get it to then move into the other child component (render.vue). I suspect it has something to with v-model. Below is a simplifed version.
Parent component
<template>
<div>
<multiSelect v-model="content" />
<renderSplashPage :content="render" />
</div>
</template>
<script>
import multiSelect from '#/utils/multiSelect'
import render from '#/components/Render'
export default {
components: {
multiSelect,
render,
},
name: 'somename',
data(){
return{
content: {},
render: {}
}
},
watch: {
'content': function(val) {
this.render = val;
},
},
}
</script>
How can I get the emitted data to be 'watched' and 'picked up' by the 'render' component?
render is a reserved name (i.e., the internal render function cannot be overwritten), and for some reason, Vue silently ignores attempts to modify it.
Rename your property (e.g., to "myRender"), and you should see reactivity. If myRender is always equal to content, you could actually just replace it with content in your binding:
<multiselect v-model="content" :options="options" />
<render :content="content" />
demo
I used a variation of this here to solve my issue:
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
Taken from here:
https://v2.vuejs.org/v2/guide/reactivity.html

Changes to object properties not being reflected in Vue component

I have a Vue component with a single prop, which is an object. When a property in this object is changed, it doesn't seem to be updating in the Vue template.
Here is a simplified version of the component:
<template>
<p>{{ item.quantity }}</p>
</template>
<script>
export default {
props: {
item: {
required: true,
type: Object
}
}
}
</script>
Using vue-devtools in Google Chrome, I can see the quantity property in my item object is being updated, but the template still only renders the default value (1).
Is there something I need to do to make Vue watch nested properties or something?
The issue is that the component is not aware that it should re-render when there is a change in the props. You could use a computed property which returns the props value and use the computed property value.
<template>
<p>{{ quantity }}</p>
</template>
<script>
export default {
props: {
item: {
required: true,
type: Object
}
}
computed: {
quantity: function() {
return this.item.quantity
}
}
}
</script>

Use prop value as a variable in VueJS

I have a problem about using prop value as a variable in VueJS. I have a component which I tranmit prop:
This is parent component:
<template>
<div class="a">
<UploadAvatarModal
apiurl="upload_avatar"
id="UploadAvatarModal"
/>
</div>
</template>
This is script of UploadAvatarModal component:
<template>
<div class="a">
...
</div>
</template>
<script>
export default {
props: {
id: String,
apiurl: String
},
methods: {
def: function () {
this.$refs.id.hide()
}
}
}
</script>
In this line: this.$refs.id.hide() How can I call methods according to prop id. Example: this.$refs.UploadAvatarModal.hide() or this.$refs.UploadAvatarModal2.hide() changed by props value??
You can access props doing :
this.propName
To access id prop you need to do :
this.id
So the line you wrote this.$refs.id.hide() should be written :
this.$refs[this.id].hide()
But it will probably do nothing as .hide() is a jquery function.
In plain javascript you would need to do :
this.$refs[this.id].style.display = 'none'
That said, it's might not be a good idea to do so.
Using vue, the best way to show/hide a component is probably to use v-if or v-show

Categories