Relationship between props and data (vue) - javascript

In
https://codesandbox.io/s/v9pp6
the ChromePage component passes a prop to InventorySectionC:
<inventory-section-component :itemSectionProps="getItemSection">
</inventory-section-component>
InventorySectionC:
<template>
<div class="inventory-section-component">
<draggable v-model="itemSectionProps.itemSectionCategory">
<transition-group>
<div
v-for="category in itemSectionProps.itemSectionCategory"
:key="category.itemSectionCategoryId"
>
<!-- <p>{{ category.itemSectionCategoryName }}</p> -->
<inventory-section-group-component :itemSectionGroupData="category">
</inventory-section-group-component>
</div>
</transition-group>
</draggable>
</div>
</template>
<script>
import InventorySectionGroupComponent from "./InventorySectionGroupC";
import draggable from "vuedraggable";
export default {
name: "InventorySectionComponent",
components: {
InventorySectionGroupComponent,
draggable,
// GridLayout: VueGridLayout.GridLayout,
// GridItem: VueGridLayout.GridItem,
},
props: {
itemSectionProps: {
type: Object,
},
},
data() {
let itemSectionData = itemSectionProps;
return {
itemSectionData
};
},
};
</script>
<style scoped>
</style>
gives a warning at line:
<draggable v-model="itemSectionProps.itemSectionCategory">
:
Unexpected mutation of "itemSectionProps" prop. (vue/no-mutating-props)eslint
Why (how?) is itemSectionProps mutable?
Can a binding be created between props and data (all draggable samples use a data object:
https://sortablejs.github.io/Vue.Draggable/#/nested-example
https://github.com/SortableJS/Vue.Draggable/blob/master/example/components/nested-example.vue
)?
The idea is to have auto updating, nested, draggable components.
The code as is "works" but there are warnings/errs:
data() can't seem to see props:
And one more thing, which comes "first"? Data or props? can't seem to figure it out from the docs:
https://v2.vuejs.org/v2/guide/instance.html
Setting the props to a predefined value:
props: {
itemSectionProps: {
type: Object,
default: { itemSectionCategory: '' }
},
},
gives:
Type of the default value for 'itemSectionProps' prop must be a function. (vue/require-valid-default-prop).
I'm not sure why vue expects props to return a function.
After adding a default() onto props, props are empty when passed on to components:
https://codesandbox.io/s/sjm0x

(this grew too long for a comment, but probably already answers what you need)
itemSectionProps:
Your props are defined as:
props: {
itemSectionProps: {
type: Object,
},
},
You reference a prop of that object in your template
<draggable v-model="itemSectionProps.itemSectionCategory">
Vue cannot assume itemSectionProps.itemSectionCategory will exist in the future.
You should give it a default (see Vue docs) to create the expected values in that object.
props: {
itemSectionProps: {
type: Object,
default() {
return { itemSectionCategory: '' };
}
},
},
Do this for all the props you use on itemSectionProps.
data() can't seem to see props:
You can write this.itemSectionProps instead of only itemSectionProps.
But itemSectionProps is already defined in props. You can just remove itemSectionProps from data.
If you need to change that value, use a copy and promote changes with this.$emit.

You are probably calling the props without using this. on your data method.
You can as well define your variable itemSectionData as below:
data(){
return {
itemSectionData: Object.assign({}, this.itemSectionProps)
}
}
Object.assign()
The Object.assign() method copies all enumerable own properties from one or more source objects to a target object. It returns the target object. See more details here
Then use the newly defined variable itemSectionData within your component. Like:
<draggable v-model="itemSectionData.itemSectionCategory">
If you want to update the prop's values, simply emit an event from your child component and capture it on the parent as below:
methods:{
updatePropValues(){
this.$emit('updateProp', this.yourNewValues);
}
}
On the parent component handle the event as:
<inventory-section-component #updateProp="setNewValues" :itemSectionProps="getItemSection">
</inventory-section-component>
methods:{
setNewValues(newValues){
this.itemSections = newValues;
}
}
Check it out in action here

Related

How to get the props value and use in lifehooks cycle of Vue JS

I'm searching a way to get the props value through some lifehooks like mounted or updated and trying to save the value with my v-model with some string. But I can't get it.
Though I tried :value on the input element with the props value and some string and I was able to get it, but it seems like I can't access it without v-model, as I researched v-model and :value can't be together.
The purpose is to get the value(with from props and some string) of a input tags.
Parent Component
<invite :user_token="user_token"/>
Child Component
export default {
props: ['user_token'],
data() {
return {
link: ''
}
},
mounted() {
console.log(this.user_token);
this.link = `"http://localhost/johndoe-vue/public/#/invite${this.user_token}"`;
},
updated() {
console.log(this.user_token);
this.link = `"http://localhost/johndoe-vue/public/#/invite${this.user_token}"`;
}
}
Welcome to SO Nigel!
Are you looking for something like this, perhaps?
ParentComponent.vue
<template>
<div id="wrapper">
<invite :userToken="userToken"></invite>
</div>
</div>
<script>
import Invite from "#/Invite.vue";
export default {
components: {
Invite
},
data() {
return {
userToken: "fooBar",
};
}
}
</script>
ChildComponent.vue
<template>
<div id="wrapper">
<p v-if="inviteLink != ''">{{ inviteLink }}</p>
</div>
</template>
export default {
props: {
userToken: {
type: String,
}
},
data() {
return {
inviteLink: ""
}
},
created() {
if(this.userToken != "") {
this.inviteLink == "/your-link-here/"+this.userToken;
}
}
}
Also, you should check out the Vue.js Style Guide. They've marked multi-word component names as essential. Your Invite component should be renamed to BaseInvite or something like that.
Have you tried to $emit this.link
Props is accessible through the $props property of your component. You would reference it like: this.$props.[property name]. $props is called an instance property; there are many of them and they are each accessible this way. See https://v2.vuejs.org/v2/api/#Instance-Properties
Keep in mind that the Vue life cycle methods are somewhat inconsistent. Which instance properties are accessible depends on the method (ie: you can't reference $el in created(...).
https://v2.vuejs.org/v2/guide/instance.html#Lifecycle-Diagram

Vuex pass mutable object as prop, call mutation with the passed object as argument

Let's say I have a Vuex State which contains an array of objects which I want to mutate in the component. I iterate over the array and spawn a Component for each Object which takes the Object as a prop. Inside the component I call Vuex mutations with the Object passed down as a direct argument. For example:
Parent
<template>
<ItemComponent
v-for="(item, index) in items"
:key="index"
:index="index"
:item="item"
/>
</template>
<script>
import ItemComponent from 'ItemComponent.vue';
export default {
components: {
ItemComponent
},
computed: {
items() {
return this.$store.getters.items;
}
}
};
</script>
Child:
<template>
</template>
<script>
export default {
props: {
item: {
type: Object,
required: true
}
},
methods: {
changeItemProp() {
this.$store.dispatch('changeItemXValue', {this.item, 'newValue'});
}
}
};
</script>
Store:
// ...
mutations: {
changeItemXValue(state, { item, value }) {
item.x = value
}
}
It works, yes. But I'm pretty sure this is an Antipattern, right?
// ...
mutations: {
changeItemXValue(state, { item, value }) {
state.items.find((i) => i === item).x = value
}
}
This doesn't seem much better.
My question: Is this the way to go if I want to encapsulate my mutable data in a subcomponent, or is this considered an anti-pattern? In which case I would like to know what the proper way is to handle this case. I initially prepared vuex exactly to handle this case but I'm not sure if this is quite the right way. Thank you.
Well, there are multiple ways of doing this, but I'll explain a recommended way of doing.
What you're doing right now is that, you're fetching items from store, passing them to a child component (By iteration) then mutating following mutation,
// ...
mutations: {
changeItemXValue(state, { item, value }) {
state.items.find((i) => i === item).x = value
}
}
First of all, you don't need to mutate this item directly, instead you should create an action that finds the item for you and then mutate it using item id or index.
So what you need to do is that, create an action, when changing value of item, dispatch that action, and pass item id or even item to that action. Inside that action, find the item you want to mutate and then commit mutation, inside your mutation, simply write
state.items[itemIndex] = newValue

How can I get the props values in Vue.js?

I'd like to get prop values in Vue.js, because i need to receive eid in my component to do verification, but i don't know how can i do this and if i do this.configs.eid is undefined. Another way is send data() value from my component A to component B.
I have this component and i need to get eid then insert in v-if
<section v-if="" >
<stream :configs="{eid : event.id}"></stream>
</section>
Another way is send this data() from component A to component B
data() {
return {
tipo: String,
link: String,
eid : 0
};
}
In component A my props is
props: {
configs: {
type: Object
}
},
I don't know how to get it, anybody knows? :/
Your question is not clear, there is no definition which component is A and which is B.
It seems that you may have mixed up parent & child, so I'll just try to show how to pass eid both ways.
If you want to pass eid from the child stream component to the parent for v-if check (which I think is the case), you need to use $emit, not prop:
Component A (Parent)
<section v-if="event.id == 0">
<stream #get-event-id="getEventId"></stream>
</section>
data() {
configs: {
event: {}
}
},
methods: {
getEventId(id) {
this.configs.event.id = id
}
}
Component B (Child)
data() {
event: {id: 0}
},
mounted(){
this.$emit('get-event-id', this.event.id)
},
That way if stream eid will be 0, like here, the component will not render.
However, if you would need to pass eid from parent component to stream component, it would look like this:
Component A (Parent)
<section v-if="">
<stream :configs="{eid : event.id}"></stream>
</section>
data() {
event: {id: 0}
}
Component B (Child)
props: ['configs'],
mounted(){
console.log(this.configs.eid)
},
This way you will get in console the parent's eid.
If you're trying to send the event.id to stream as property, then you can simply do it like this
<section v-if="" >
<stream :eventId="event.id"></stream>
</section>
Then from the Stream.vue component, you can receive the property like
export default {
name: "Stream",
props: ["eventId"]
}

Vue2: warning: Avoid mutating a prop directly

I'm stuck in the situation where my child component (autocomplete) needs to update a value of its parent (Curve), And the parent needs to update the one of the child (or to completely re-render when a new Curve component is used)
In my app the user can select a Curve in a list of Curve components. My previous code worked correctly except the component autocomplete was not updated when the user selected another Curve in the list (the component didn't update its values with the value of the parent).
This problem is fixed now but I get this warning:
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:
"value"
The description of this warning explain exactly what behavior I expect from my components. Despite this warning, this code works perfectly fine !
Here is the code (parts of it have been removed to simplify)
// curve.vue
<template>
<autocomplete v-model="curve.y"></autocomplete>
</template>
<script>
import Autocomplete from './autocomplete'
export default {
name: 'Curve',
props: {
value: Object
},
computed: {
curve() { return this.value }
},
components: { Autocomplete }
}
</script>
// autocomplete.vue
<template>
<input type="text" v-model="content"/>
</template>
<script>
export default {
name: 'Autocomplete',
props: {
value: {
type: String,
required: true
}
},
computed: {
content: {
get() { return this.value },
set(newValue) { this.value = newValue }
}
}
}
</script>
A lot of people are getting the same warning, I tried some solutions I found but I was not able to make them work in my situation (Using events, changing the type of the props of Autocomplete to be an Object, using an other computed value, ...)
Is there a simple solution to solve this problem ? Should I simply ignore this warning ?
you can try is code, follow the prop -> local data -> $emit local data to prop flow in every component and component wrapper.
ps: $emit('input', ...) is update for the value(in props) bind by v-model
// curve.vue
<template>
<autocomplete v-model="curve.y"></autocomplete>
</template>
<script>
import Autocomplete from './autocomplete'
export default {
name: 'Curve',
props: {
value: Object
},
data() {
return { currentValue: this.value }
}
computed: {
curve() { return this.currentValue }
},
watch: {
'curve.y'(val) {
this.$emit('input', this.currentValue);
}
},
components: { Autocomplete }
}
</script>
// autocomplete.vue
<template>
<input type="text" v-model="content"/>
</template>
<script>
export default {
name: 'Autocomplete',
props: {
value: {
type: String,
required: true
}
},
data() {
return { currentValue: this.value };
},
computed: {
content: {
get() { return this.value },
set(newValue) {
this.currentValue = newValue;
this.$emit('input', this.currentValue);
}
}
}
}
</script>
You can ignore it and everything will work just fine, but it's a bad practice, that's what vue is telling you. It'll be much harder to debug code, when you're not following the single responsibility principle.
Vue suggests you, that only the component who owns the data should be able to modify it.
Not sure why events solution ($emit) does not work in your situation, it throws errors or what?
To get rid of this warning you also can use .sync modifier:
https://v2.vuejs.org/v2/guide/components.html#sync-Modifier

How to bind dynamic props to dynamic components in VueJS 2

I'd like to know how I can iterate a list of component names (which come from an AJAX call to an API server) and render them as components, and pass relevant properties to each component (i.e. bind their properties dynamically).
So far I have managed to iterate a JSON list of items that represent components, and successfully render these components. What I'd like to do now is bind the properties for each component using v-bind.
In the example below, the item-one component would receive the image property with the item1.jpg value; and the item-two component wouldn't receive any properties.
<template>
<div v-for="item in items">
<component :is="Object.keys(item)[0]" :v-bind="???"></component>
</div>
</template>
<script>
import ItemOne from '../components/item-one'
import ItemTwo from '../components/item-two'
export default {
components: {
ItemOne,
ItemTwo
},
asyncData () {
return {
items: [
{ 'item-one': { 'image': 'item1.jpg' } },
{ 'item-two': { } }
]
}
}
}
</script>
I tried using :v-bind="Object.values(Object.keys(item)[0])" but I get the attribute v-bind="[object Object]" in the rendered element.
First, you need to get rid of the colon before v-bind. The colon is a short-hand for v-bind when prefixed to an attribute. But, when passing an object of key pairs to bind, you simply use the v-bind directive.
Second, you are not correctly referencing the properties of each item. You can reference them like this:
v-bind="item[Object.keys(item)[0]]"
You wouldn't need to use Object.keys if you changed the structure of your items property a bit:
items: [{
type: 'item-one',
props: { 'image': 'item1.jpg' },
}, {
type: 'item-two',
}]
This way, by explicitly labeling the component type and properties beforehand, your template would be much easier to understand:
<div v-for="item in items">
<component :is="item.type" v-bind="item.props"></component>
</div>

Categories

Resources