How to pass data in vuejs? [closed] - vue.js

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I'm working with code in vuejs :
data() {
return {
name:'',
age: '',
}
}
created() {
// Get name and age values ​​from methods
console.log(this.name, this.age) //result null
},
methods: {
changeValue:function (name, age) {
this.name = name,
this.age = age,
console.log(name, age); // result : rooney, 20
}
}
I want to pass values ​​from methods to created(), how do I pass it? Give me ideas, thanks

In VueJs you cannot pass values between lifecycle hooks.
However in the created hook, all reactive elements are already hooked up to your instance, so you can access your data objects as shown in the official documentation example.
what happens in your example
since created is called immediatly after your component is instantiated and reactivity has been set up (before it is attached to DOM elements), all your data will contain its default values. In this case for both elements that is ''.
Any calls to the changeValue method from outside will happen after your component is mounted. For more information on this, please check the vue documentation
A guess to what you tried to do
in the comments you mention that you set a name using localStorage.setItem('name', name). Now, vue has no direct link to localstorage. All reactivity in vue happens through the data defined in the data block, which is accessible through this in most other places. If you want a parent component to pass this value to your component, you can do so with props (a.k.a properties/parameters). Or if you want to pass it from a child to a parent, you can do so using v-model.
reacting to those changes can then be done using watchers on those data values or properties
code example
props() {
name:{type: String, default: ''},
age: {type: Number, default: -1},
}
watch:{
name: function(oldName, newName){
//this triggers when name is changed
console.log(this.name, this.age)
localStorage.setItem('name', newName)
}
},
methods: {
changeValue:function (name, age) {
this.name = name,
this.age = age,
console.log(name, age); // result : rooney, 20
}
}

Related

VueJs - Set property in child component's $options based on prop

Reproducible example
https://codesandbox.io/s/youthful-banach-tc83p?file=/src/components/ChildComponent.vue
Background
In the project I'm working on, there are helper functions which depend on a translationFile property in the $options of the component. However, when the component is a generic one being implemented in different places, that property will be different depending on the implementation.
What I want to do
Is there a way that I can set a property in the child component's $options dynamically based on the parent component?
Helper function example
getLabel(labelKey) {
const { translationFile } = this.$options;
if (translationFile && labelKey) {
return this.$tc(`${translationFile}.labels.${labelKey}`);
}
},
this would be called from the child component (CC), hence this.$options would refer to the CC.
When the parent component (PC) is PC1.vue, the file it would look in would be tran1.js, when PC2.vue, then tran2.js
I was able to achieve what I wanted with.
props: {
translationFile: {
type: String,
required: true
}
}
mounted() {
this.$options.translationFile = this.translationFile
}
but any components which depended on this needed to be wrapped with <client-only></client-only>

Vue v-model issue when using a computed setter

I want to create an input field that the user can fill out. The catch is that I don't want them to fill this field in with special characters. Currently, I have this html setup:
<input class="rc-input-editing" id="bioInput" type="text" v-model="wrappedBioName">
And this Vue.js setup (As you can see, I'm trying to approach this problem using a computed setter) :
data () {
return {
newBioName: '',
}
},
computed: {
wrappedBioName: {
get () {
alert('getting new name!')
return this.newBioName
},
set: function (newValue) {
const restrictedChars = new RegExp('[.*\\W.*]')
if (!restrictedChars.test(newValue)) {
this.newBioName = newValue
}
}
}
Currently, my issue is that the client is able to continue filling out the text input field, even when this.newBioName isn't updating. In other words, they are able to enter special characters into the input field, even though the this.newBioName isn't being updated with these special characters.
This behavior is different than what I'm expecting, given my current understanding of v-model. Based on what I've read until now, v-model binds the input element to some vue instance data, and that vue instance data to the input element (two way binding). Therefore, I'm expecting that the text in the text input field will directly match the value of this.newBioName.
Clearly, I'm missing something, and would appreciate a second pair of eyes!
Vue.js two way binding system doesn't work as you expected. Each binding process works one way each time. So, the thing you should do is not to let the input text change.
Try keypress event instead of computed property like this:
<input class="rc-input-editing" id="bioInput" type="text" v-model="newBioName" #keypress="nameKeyPressAction">
data() {
return {
newBioName: ""
};
},
methods: {
nameKeyPressAction(event) {
const restrictedChars = new RegExp("[.*\\W.*]");
const newValue = this.newBioName + event.key;
if (!restrictedChars.test(newValue))
this.newBioName = newValue;
return event.preventDefault();
}
}
Edit:
When you set a data property or a computed property as v-model of an input, vue associates them and yet, if user updates dom object via the input, property's setter is triggered and the process ends here. On the other hand, when you change the value of the property on javascript side, vue updates the dom object and this process also ends here.
In your sample code, it seems like you expect that the computed property's getter to set the dom again but it can't. The property is already updated via dom change and it can't also update it. Othervise, there might occur infinite loop.

Tracking a child state change in Vue.js

I have a component whose purpose is to display a list of items and let the user select one or more of the items.
This component is populated from a backend API and fed by a parent component with props.
However, since the data passed from the prop doesn't have the format I want, I need to transform it and provide a viewmodel with a computed property.
I'm able to render the list and handle selections by using v-on:click, but when I set selected=true the list is not updated to reflect the change in state of the child.
I assume this is because children property changes are not tracked by Vue.js and I probably need to use a watcher or something, but this doesn't seem right. It seems too cumbersome for a trivial operation so I must assume I'm missing something.
Here's the full repro: https://codesandbox.io/s/1q17yo446q
By clicking on Plan 1 or Plan 2 you will see it being selected in the console, but it won't reflect in the rendered list.
Any suggestions?
In your example, vm is a computed property.
If you want it to be reactive, you you have to declare it upfront, empty.
Read more here: reactivity in depth.
Here's your example working.
Alternatively, if your member is coming from parent component, through propsData (i.e.: :member="member"), you want to move the mapper from beforeMount in a watch on member. For example:
propsData: {
member: {
type: Object,
default: null
}
},
data: () => ({ vm: {}}),
watch: {
member: {
handler(m) {
if (!m) { this.vm = {}; } else {
this.vm = {
memberName: m.name,
subscriptions: m.subscriptions.map(s => ({ ...s }))
};
}
},
immediate: true
}
}

How to handle the state when multiple components use the same actions in Vuex for fetching data?

I have been using Vuex to control my whole app state for a while. But now I am facing a problem I have never meet before.
The Workflow is like this:
The root component will fetch data from my database, which is an array with multiple objects in it.
I created child-component and use v-for to show this array to user (that means, each child-component represents an object in the array later)
The Problem comes, when I try to fetch async data for each child-component, for the data-fetching, I also need parameter which come from the array I mentioned.
I use the Vuex actions in the created hook of my child components. The actions fetch_sim_Type will take the payload (which is parameter from parent-component, also from the initial array). And change the state simType in the state: {} of Vuex.
By using the computed properties I can get the fetched simType in my child-component and show it to the user. Everything works so far.
CODES:
THE initial Array (simSumArray in Parent-component) looks like this:
[
{ typeid: 1, name: XXX },
{ typeid: 2, name: ZZZ },
{ typeid: 3, name: GGG },
{ typeid: 4, name: JJJ },
]
PARENT-COMPONENT:
<template
v-for="(singleSim, idx) in simSumArray"
>
<sim-single :singleSim="singleSim"></sim-single>
</template>
CHILD-COMPONENTS:
props: ['singleSim'],
created () {
store.dispatch('fetch_sim_Type', this.singleSim.typeid);
},
computed: {
simType () {
console.log("store.getters.get_simType: ", store.getters.get_simType)
return store.getters.get_simType;
}
},
IN VUEX:
state: {
simType: 'unknown'
},
actions: {
fetch_sim_Type (context, typeid) {
//.. fetch the data based on typeid from DB
context.state.simType = fetchedData;
}
}
But it only works when in the array only when object exist. When there are more than one child-component being created. The state simType in the Vuex store.js will be replaced many times and in every child-component the simType() is always the same.
The Problem is kind of hard to describe. The central problem is, the state in Vuex is meant to be shared everywhere in the whole app, so if I have multiple child-components, they all fetch data for themself, than the shared state will be replaced all the time and I can't get individual state for every child-components.
I don't know if I describe the problem clair but I really tried hard to.
Maybe There is a better way to do this data fetching job without Vuex or maybe I just used the Vuex by the wrong way.
I am sure this should not be a hard question. But I can't find any relative answer online.
Reading your code, the behaviour you describe is normal. I see two solution to your problem (solution 2 is probably closer to what you are looking for) :
Solution 1 - store simType in your component
if you need to access the simType from somewhere else than inside your component and have it stored in your state, skip to solution 2
When your component is created, store the simtype in the component's data. This would look like this:
In your component:
data () {
return {
simType: undefined //declare simType as one of your component's data property
}
},
created () {
store.dispatch('fetch_sim_Type', this.singleSim.typeid).then(simType => {
this.simType = simType //store the fetched simType
})
}
In your vuex Actions:
actions: {
fetch_sim_Type (context, typeid) {
//.. fetch the data based on typeid from DB
return fetchedData //pretty much the same except return the result
}
}
Solution 2 - store simTypes in your state indexed by their IDs
Store your fetched simType by id, like this:
state: {
simTypes: {} //simTypes is now plural, and is an empty object at first. It will later contain several simTypes, indexed by their respective Ids
},
actions: {
fetch_sim_Type (context, typeid) {
//.. fetch the data based on typeid from DB
context.state.simType[typeid] = fetchedData; // a fetched simtyped will be store in simTypes and can be accessed with the typeid as a key
}
}
to retrieve a simType, you can write a vuex getter like this:
getters: {
getSimTypeById: (state) => (typeId) => {
return state.simTypes[typeId]
}
}
So in your example, the computed method would be :
computed: {
simType () {
console.log("store.getters.getSimTypeById(this.singleSim.typeid): ", store.getters.getSimTypeById(this.singleSim.typeid)
return store.getters.getSimTypeById(this.singleSim.typeid);
}
},
This solution, as a bonus, allows you to fetch a simType only once if several of your items have the same simType.
I have had success by keeping shared data in the Vuex store, and watching it from my components.
Although not a best practice, I sometimes don't even bother to use actions or commits to change the state, and just modify the state directly. In this scenario, Vuex just acts like a shared data object for all my components.
Vue Store
state: {
myvalue: []
}
Components
watch: {
'$store.state.myvalue'(value) {
}
}

Deeply nested data objects in VueJS

I've got a VueJs front end that fetches some data from an API. The app uses vue-router.
The data fetched for one component is similar to the following:
{
name: ...,
email: ...,
order: {
data: {
line_items: [
{
quantity: ...
}
]
}
}
}
The component is instantiated with a data object called info:
data () {
return {
info: {}
}
}
In the beforeRouteEnter hook, the data is fetched by a vue-resource http.get and info is set to the body of the result like this:
vm.info = result.body
When the component renders, the following errors are produced:
TypeError: undefined is not an object (evaluating _vm.order.data.line_items')
In the template, the data is referenced in curly braces as per usual, however, if I just reference info in the template like this:
{{ info }}
it will output all of the data and not complain at all.
What is the correct way to assign a deeply nested data object?
If you are finding #saurabh answer is not working then you may need to check how you are assigning the new values to your object.
Firstly is the data being accidiently set as a string? hence {{ info }} working (or appearing to). May be worth using response.json() to set the data.
If thats not it then the error may be produced as the data you have set is not reactive. As you are assigning a nested object you may need to use different methods to make it reactive, i.e
Vue.set(vm.someObject, 'b', 2)
or
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
check out: https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
because your response is an object you may want to break out your data into corresponding params, i.e.
data () {
return {
info: {
name: '',
email: '',
order: {},
},
}
}
then you can assign name & email as you expected (info.email = ...).
For info.order you'd use Vue.set:
Vue.set(this.info, 'order', result.body.order)
The actual issue here is a life cycle one. The route guard beforeRouteEnter is called after the component is created so the error is thrown because the data isn’t there when the component tries to access it.
You have to use condition rendering here, which you can easily do with help of Vue directive v-if. It may give error if the data is not populated and you try to access it, so v-if will render that part of HTML only when data is present.
You need to do something like following:
<div v-if="info.order">
<span>
{{ info.order }}
</span>
</div>
In my scenario, I had to use one Vue.set that wrapped an Object.assign:
I'm trying to set state.workouts[state.workoutDate].isDone in a Vuex mutation
toggleWorkout(state, isDone) {
Vue.set(
state.workouts,
state.workoutDate,
Object.assign({}, state.workouts[state.workoutDate], { isDone: isDone })
);
},
Force update object setting a new object with same content
<button #click="my.object.nested.variable = 2; my = Object.assign({}, my);">
or
this.my.object.nested.variable = 2;
this.my = Object.assign({}, this.my);