Is there a difference between the following? I've seen examples doing both and am unsure why you would choose one over the other.
Vue.component('test', {
data() {
return { myDataA: 10 };
}
//vs
created() {
this.myDataB = 10;
}
}
Variables set in created() on this will not be reactive. In order for them to be reactive, you must define them in the object returned by data().
Example (note how the text does not change in the output):
https://jsfiddle.net/oyf4quyL/
in a component, there are three places where you can define you data:
data properties
computed properties
props
the created property is lifecycle hook in Vue. what this means, is that the Vue will run this function when the component is created. there are also other lifecycle hooks in Vue you can use, like mounted or beforeMount or beforeCreate and etc.
now with this in mind, let's answer your question.
when you define myDataA in data property, Vue will automatically create some "watchers" for this data property, so anytime that you set a new value to myDataA, anywhere that is using it, will be called again. but when you define a property directly on Vue instance (this), you will lose this "watchers" feature. (which by the way is just some getters and setters!)
so as i said, the best way and the correct way to define a data property is on any of the three places that i mentioned, based on your need. (because each of them has different use-cases that the others).
Related
I'm trying to create a simple component whose focus is to display an element in an array, but I'm having issues with Vue's philosophy.
As you may know, if a mutation on a prop is triggered, Vue goes crazy because it doesn't want you to update the value of a prop. You should probably use a store, or emit an event.
The issue is: that since I'm adding functionalities to my codebase (for instance the possibility to start again when I reach the last element of the array), it would be wrong to have an upper component be responsible for this management, as it would be wrong to ask an upper component to change their variable, given that my component is supposed to manage the array, so an emit would be a bad solution.
In the same way, given that I'm making a generic component that can be used multiple times on a page, it would be incorrect to bind it to a store.
EDIT: the reason why the prop needs to be updated is that the component is basically acting as a <select>
Am I missing an obvious way to set this up?
To give an example of my end goal, I'm aiming for a component looking like the one in the picture below, and I think a 2 way bind like in v-model would be more appropriate than having to set an #change just to say to update the value of the passed prop.
If you have a prop the correct way to update the value is with a sync, as in the following example
Parent:
<my-component :title.sync="myTitle"></my-component>
Child:
this.$emit("update:title", this.newValue)
Here is a very good article talking about the sync method.
By the other hand you can alter a Vuex state variable by calling a Vuex mutation when you change the value:
computed: {
title: {
// getter
get() {
return this.$store.state.title
},
// setter
set(newValue) {
this.setTitle(newValue) // Requires mutation import, see the methods section.
// Or without import:
this.$store.commit('setTitle', newValue);
}
}
},
methods: {
...mapMutations("global", ["setTitle"]) // It is important to import the mutation called in the computed section
}
In this StackOverflow question they talk about changing state from computed hook in Vue. I hope it works for you.
I am aware of deep watching of properties using the handler in the "watch" section, but I am not seeing how to make vue deep watch in a getter/setter computed property.
Essentially I have something like this, of which vue is not able to observe the changes.
How do I tell vue to observe the changes of "someComputedProperty"?
computed: {
someComputedProperty: {
set (value) {
this.someComputedPropertyObject[this.someOtherObject.id] = value;
},
get () {
return this.someComputedPropertyObject[this.someOtherObject.id];
}
}
}
Thanks in advance,
Erion
If someComputedPropertyObject is a Vue computed property, its value won't be made observable by design (if it creates a new object).
Furthermore, does someComputedPropertyObject have the this.someOtherObject.id property defined upfront? If not, you're creating a new property which Vue cannot observe. Use Vue.set (or this.$set) instead.
I am trying to make my Vue Component reusable but there is a part in it which requires to run a function on button click which I have defined in the parent component.
The component's button will always run a parent function and the parameter it passes is always the same (its only other property).
Right now I am passing 2 properties to the component: 1) an object and 2) the parent function reference, which requires the object from 1) as a parameter.
The Child-Component looks like this (stripped unnecessary code):
<button v-on:click="parentMethod(placement)">Analyze</button>
Vue.component('reporting-placement', {
props: ['placement', 'method'],
template: '#reporting-placement',
methods: {
parentMethod: function(placement) {
this.method(placement);
}
}
});
The parent is making use of the child like this:
<reporting-placement v-bind:placement="placement" v-bind:method="analyzePlacement"></reporting-placement>
methods: {
analyzePlacement: function(placement) {
this.active_placement = placement;
},
}
As you can see, the child has only one property, placement, and the callback reference. The placement must be put in as a parameter to the reference function from the parent.
But since the parent defines the parameters, the child shouldn't concern itself with what it needs to pass to the parent function. Instead I would prefer to already pass the parameter along in the parent.
So instead of
<reporting-placement v-bind:placement="placement" v-bind:method="analyzePlacement"></reporting-placement>
I would prefer
<reporting-placement v-bind:placement="placement" v-bind:method="analyzePlacement(placement)"></reporting-placement>
(including appropriate changes in the child).
But passing the parameter along does not work that way.
Is it possible (maybe in other syntax) to 'bind' the variable to the function reference so that it is automatically passed along when the callback is called?
Info: I don't get an error message if I write it down as above but the whole Vue screws up when I pass the parameter along to the component.
Hope the issue is clear :-) Thanks a lot!
By reading your proposal I've found out that you are overusing the props passing.
Your concern that child component should not have any knowledge about the way that the parent component uses the data is completely acceptable.
To achieve this you can use Vue's event broadcasting system instead of passing the method as props.
So your code will become something like this:
Vue.component('reporting-placement', {
props: ['placement', 'method'],
template: '#reporting-placement',
methods: {
parentMethod: function(placement) {
this.$emit('reporting-placement-change', placement)
}
}
});
And you can use it like this:
<reporting-placement v-bind:placement="placement" #reporting-placement-change="analyzePlacement($event)"></reporting-placement>
But if you need the data which is provided by the method from parent it's better to consider using a state management system (which can be a simple EventBus or event the more complex Vuex)
And finally, if you really like/have to pass the method as a prop, You can put it in an object, and pass that object as prop.
I am using an opensource vuejs + vuex project and this is the source https://github.com/misterGF/CoPilot/tree/master/src/components
I am currently having problems knowing how to trigger an event from one components to another.
I can use this.$state.store.commit("foo", "bar") to store information in vuex, but when two seperate have two difference export default {} I don't know how I can make the app aware whenever "foo" is for exampled changed to "baz" ... unless I refresh/reload the app, there is no way for me to know the changes
Use this.$store.watch on your component. Created() is a good place to put it. You pass it the state to watch and then specify the callback for when it changes. The Vuex docs do not give good examples. Find more information on this Vuejs Forum page. Store watch functions the same as the vm.$watch, so you can read more about that here in the Vue docs.
this.$store.watch(
(state)=>{
return this.$store.state.VALUE_TO_WATCH // could also put a Getter here
},
(newValue, oldValue)=>{
//something changed do something
console.log(oldValue)
console.log(newValue)
},
//Optional Deep if you need it
{
deep:true
}
)
Your question is not entirely clear so I am going to make some assumptions here.
If you simply want your app to know when a store value has changed you can use a watcher to watch a computed property that is directly linked to a store getter:
So you would set a watcher on something like this:
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
},
watch:{
doneTodosCount(value) {
console.log(`My store value for 'doneTodosCount' changed to ${value}`);
}
}
If you want your commit to behave differently depending on what the current value of your foo property is set to, then you can simply check for this in your commit function.
Let me know if you have some more questions.
Still a little bit young in VueJS but I'm loving every bit of it. But now, fixated somewhere.
I want to initialize some values in data() using values passed via props. This is so that I can be able to mutate them later on, since it is not recommended to mutate props inside a component. In fact the official docs recommend this property initialization using prop values as shown below:
{
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
I have something like the one below:
<template>
<div class="well">
<!-- Use Prop value directly on the template: works (but of no help in initializing data) -->
Department: {{department.name}}
<!-- Use prop value but gotten via computed property: Works inside the template but not in the initialization -->
Department: {{fetchDepartment.name}}
<!-- Use the array I initialized with the prop value: Does not work -->
Department: {{this_department.name}}
</div>
</template>
<script>
export default {
name: 'test',
props: ['department'],
data() {
return {
this_department: this.department
// below does not work either
//this_department: this.fetchDepartment
}
},
created() {
// shows empty array
console.log(this.department)
},
mounted() {
// shows empty array
console.log(this.department)
},
computed: {
fetchDepartment() {
return this.department
}
}
}
</script>
As seen in the commented sections above, the initialization is not successful. Neither does the value of this.department appear either from the created() or the mounted() hooks. And note, I can see it is defined using the Chrome Vue Devtools. So my question is, how exactly should I initialize data() attributes using props values, or which is the best way of going around this issue?
I know my answer comes in late but it helps me and hopefully someone else coming here. When props' data are async:
// in the parent component
<template>
<child :foo="bar" v-if="bar" />
</template>
That way, you render the component when props are already available making it safer to follow the guide's recommended ways to initialize data value with props as so:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
Happy coding!
You CAN modify a prop. Use the '.sync' modifier. I use it frequently because it is convenient and intuitive. This requires emitting an event to update the value on the parent. I am not really sure the warning of how it results in maintenance issues.
Another method I use if I want to modify a value and not update the parent is using Lodash clone. For example (assuming its available on mounted)
mounted(){
this_department = _.clone(this.department)
}
If you consistently want to mutate the prop and have it change with the parent, then use a computed property. However, in most cases you will want to depend on the state of that data within the component and change it using other functions and thus a computed property will not be what you need.
A computed property is the simplest way to provide a mutable version of a prop, but you might not want to lose data when the prop is updated. You could use an explicit watch.
Watchers
While computed properties are more appropriate in most cases, there
are times when a custom watcher is necessary. That’s why Vue provides
a more generic way to react to data changes through the watch option.
This is most useful when you want to perform asynchronous or expensive
operations in response to changing data.
This is most useful when you want to perform asynchronous or expensive
operations in response to changing data.