What is difference between 'data:' and 'data()' in Vue.js? - javascript

I have used data option in two ways. In first snippet data object contains a key value, however, in second data is a function. Is there any benefits of individuals.Not able to find relevant explanations on Vue.js Docs
Here are two code snippets:
new Vue({
el: "#app",
data: {
message: 'hello mr. magoo'
}
});
new Vue({
el: "#app",
data() {
return {
message: 'hello mr. magoo'
}
}
});
Both are giving me the same output.

It seems as though the comments on your question missed a key point when considering your specific code example.
In a root Vue instance i.e. constructed via new Vue({ ... }), you can simply use data: { ... } without any problems. The issue is when you have reusable components that are defined via Vue.component(...). In these instances, you need to either use data() {return { ... };} or data: function() {return { ... };}.
The reason for this is to ensure that for each individual instance of the reusable child component, there is a unique object containing all of the data being operated on. If, in a child component, you instead use data: { ... }, that same data object will be shared between the child components which can cause some nasty bugs.
Please review the corresponding section of the Vue.js documentation for more information regarding this problem.

[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.
so initiating a new vue instance dose not matter between data:{} as a object or data(){return{}} or data:function(){return{}}.
It matters when it comes to component lets try an example:
<body>
<div id="app">
<counter></counter>
<counter></counter>
</div>
<script>
Vue.component('counter', {
template: '<button v-on:click="counter += 1">{{ counter }}</button>',
data: {
counter:0
}
});
</script>
Output:
[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.
Now lets watch in vue object:
<body>
<div id="app">
<button v-on:click="counter += 1">{{ counter }}</button>
<button v-on:click="counter += 1">{{ counter }}</button>
</div>
<script>
new Vue({
el: '#app',
/*data() {
return {
counter:0
}
},*/
//or (output same)
/*data: function () {
return {
counter: 0
}
}*/
//or (output same)
data:{
counter:0
}
});
</script>
</body>
//Now let's try data as a function in the component to reuse same component over and over again.
<body>
<div id="app">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<script>
Vue.component('counter', {
template: '<button v-on:click="counter += 1">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
}
})
new Vue({
el: '#app'
});
</script>
</body>

Related

Passing the properties of an object as props Vuejs

hi everyone,
I have some confusion. I have two component ( child and parent component) and I pass the properties of an object as props
<child :child-data="abc" ></child>
Vue.component('childComponent', {
props: ['childData'],
data: function(){
return {
count:this.childData,// recommend use Vue.util.extend
}
},
});
Vue will recursively convert "data" properties of childComponent into getter/setters to make it “reactive”.
So why doesn't it automatic bind data to the template? I read some recommend use Vue.util.extend. Why Vue.util.extend?
UPDATE
my example:
https://jsfiddle.net/hoanghung1995/xncs5qpd/56/
when i set default value of parentData ,childDataA will display it. But when i use v-model to override parentData then childDataA not “reactive”. I must use "watch" to override "data" ,similar to childDataB
Vue.util.extend example: https://jsfiddle.net/sm4kx7p9/3/
Why do Vue.util.extend work fine but not use "watch"?,
To explain what is actually happening in the background, Linus Borg has as excellent answer for your question. To summarize his answer, the reason why your approach doesn't work is being data is a computed property while props are being passed in as primitive types. In other words, data makes a copy of your props (instead of passing by reference).
Another way to bypass this is to declare your childData as computed properties instead of data, i.e.:
computed: {
childDataA() {
return this.childPropsA;
},
childDataB() {
return this.childPropsB;
}
}
The reason why using computed works is because the computed properties now watches changes to their dependencies.
A proof-of-concept example based on your original fiddle:
Vue.component('child', {
props: ['childPropsA', 'childPropsB'],
template: "#sub",
computed: {
childDataA() {
return this.childPropsA;
},
childDataB() {
return this.childPropsB;
}
}
});
new Vue({
el: '#app',
data: {
parentData: '123'
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
parentData:{{parentData}}<br>
<input type="text" v-model="parentData">
<child :child-props-a="parentData" :child-props-b="parentData"></child>
</div>
<template id="sub">
<div>
<p> 1- {{ childDataA }}</p>
<p> 2- {{ childDataB }}</p>
</div>
</template>
The approach above is functionally identical to the data + watch approach, but I find it rather cumbersome and adds unnecessary verbosity to your code:
data: function() {
return {
childDataA: this.childPropsA,
childDataB: this.childPropsB
};
},
watch: {
childPropsA() {
this.childDataA = this.childPropsA;
},
childPropsB() {
this.childDataB = this.childPropsB;
}
}
Vue.component('child', {
props: ['childPropsA', 'childPropsB'],
template: "#sub",
data: function() {
return {
childDataA: this.childPropsA,
childDataB: this.childPropsB
};
},
watch: {
childPropsA() {
this.childDataA = this.childPropsA;
},
childPropsB() {
this.childDataB = this.childPropsB;
}
}
});
new Vue({
el: '#app',
data: {
parentData: '123'
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
parentData:{{parentData}}<br>
<input type="text" v-model="parentData">
<child :child-props-a="parentData" :child-props-b="parentData"></child>
</div>
<template id="sub">
<div>
<p> 1- {{ childDataA }}</p>
<p> 2- {{ childDataB }}</p>
</div>
</template>
Watch does only watch properties which are indeed reactive. Passing objects doesn`t makes their properties reactive. Just pass the properties that you want as props.
With Vue.util.extend you force the property to be reactive in this instance.

Vue 2.0 - How passing function to child component?

I have one issue. I want to pass function link to the child component. It's working but in HTML I get that code. It's correct how improve it?
I have Vue instance
app = new Vue({
... some code
data: {
onAppClose: null,
onAppSend: null
}
})
I want to add from global window any function. Or register function in Vue instance
app.onSend = () => console.log('data')
And pass this function to child
<div id="app">
<dynamsoft-component v-if="displayComponent"
:docs="docs"
:onAppSend="onSend"
:onAppClose="onClose"
></dynamsoft-component>
</div>
But I get this HTML template in console
<div id="app">
<div onappsend="()=>{}" onappclose="function (data) {
console.warn('dwdawad')
console.log('data')
}"></div>
</div>
You example code is not making a lot of sense - do you want to add a listener not a div or pass a function to a child component?`
I assume the latter. Vue has custom events for that .
Parent template:
<div v-on:appsend="someMethod" v-on:appclose="someOtherMethod"></div>
Parent component methods:
methods: {
someOtherMethod: function (data) {
console.warn('dwdawad')
console.log('data')
},
// ...
}
And then emit form the child:
this.$emit('appclose', {id: 'whatever'} /*pass data here*/)
Edit:
I still don't see how those functions would end up directly in the template, but the real problem is: HTML is not case-sensitive. so :onAppSend becomes :onappsend. You have to use kebap-case: :on-app-send. Vue will convert it to onAppSend in the component.
I have never used Vue.js before now..
But having a look at the how to on their site, this seems to work
In Vue style guide have recommendations about props naming
https://v2.vuejs.org/v2/style-guide/#Prop-name-casing-strongly-recommended
Vue.component('dynamsoft-component', {
props: ['onAppSend'],
template: '<button v-on:click="buttonclick">click me</button>',
methods: {
buttonclick(e){
// Check if onAppSend is defined.
if(Boolean(this.onAppSend)){
this.onAppSend();
}
}
}
})
new Vue({
el: '#app',
methods: {
onSend: function(){
console.log('child clicked');
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<dynamsoft-component :on-app-send="onSend"></dynamsoft-component>
</div>

how to share data between components in VUE js (while creating list)

Could you please tell me how to share data between components in VUE js (while creating list).I have two components list components and add todo component.I want to add items in list when user click on add button.But issue is input field present in different component and list is present in different component
here is my code
https://plnkr.co/edit/bjsVWU6lrWdp2a2CjamQ?p=preview
// Code goes here
var MyComponent = Vue.extend({
template: '#todo-template',
props: ['items']
});
var AddTODO = Vue.extend({
template: '#add-todo',
props: ['m'],
data: function () {
return {
message: ''
}
},
methods: {
addTodo: function () {
console.log(this.message)
console.log(this.m);
//this.m =this.message;
},
},
});
Vue.component('my-component', MyComponent);
Vue.component('add-todo', AddTODO)
var app = new Vue({
el: '#App',
data: {
message: '',
items: []
},
});
The whole point of having a great MVVM framework is to let you have a view-model: a central store of all the state in your page/app/whatever. Components can emit events. You can have an event bus. But if you can save the day with a simple, global variable containing all your state, this is by far the cleanest, best solution. So just put your to-dos in an array, in a variable in global scope, then declare them in the data of every component that needs them. Here it is working in Plunkr.
markup
<div id="App" >
<add-todo></add-todo>
<my-component></my-component>
</div>
<template id="add-todo">
<div>
<input type="text" v-model="message">
<button #click="addTodo">Add todo</button>
</div>
</template>
<template id="todo-template">
<div>
<ul >
<li v-for="(item,index) in store.items">
{{item.message}}
</li>
</ul>
</div>
</template>
<script src="vue.js"></script>
<script src="script.js"></script>
code
// This is the magic store. This is all you need.
var vueStore = {items : []};
var MyComponent = Vue.extend({
template: '#todo-template',
data : function(){return {store : vueStore}}
});
var AddTODO = Vue.extend({
template: '#add-todo',
data: function () {
return {
message: '',
store : vueStore
}
},
methods: {
addTodo: function (event) {
this.store.items.push({'message' : this.message})
},
},
});
Vue.component('my-component', MyComponent);
Vue.component('add-todo', AddTODO)
var app = new Vue({
el: '#App',
data: {
store : vueStore
},
});
This is not a savage hack! We're being called to stop thinking about events, move up the food chain, and think about reactive pipes. Components don't care when or by who the central store gets updated. Vue takes care of it.
Here's the page on state management.
So you could use events and emit the created todo to the root vue instance.
I edited / forked your plunkr (I'm rather the fiddle type).
https://plnkr.co/edit/bnMiDmi30vsj3a8uROBK?p=preview
So I edited this line here, which listens for a custom event added and pushes the first argument to items.
<add-todo v-on:added='items.push(arguments[0])'></add-todo>
And also these lines, which emit the event. And i changed from the property m to the data message, because you shouldnt mutate props:
<input type="text" v-model="message">
<button #click="$emit('added', message)">Add todo</button>

vuejs prototype array not being watched

in my vuejs program i am trying to make a global instance of an alert/notification system. This would be at the rootmost instance of the app. and then my plan was to push to an array of objects and pass that through to the component.
This only half works.
in my app.vue i have
<template>
<div id="app">
<alert-queue :alerts="$alerts"></alert-queue>
<router-view></router-view>
</div>
</template>
in my main.js i have
exports.install = function (Vue, options) {
Vue.prototype.$alerts = []
}
and my alert_queue.vue is
<template>
<div id="alert-queue">
<div v-for="alert in alerts" >
<transition name="fade">
<div>
<div class="alert-card-close">
<span #click="dismissAlert(alert)"> × </span>
</div>
<div class="alert-card-message">
{{alert.message}}
</div>
</div>
</transition>
</div>
</div>
</template>
<script>
export default {
name: 'alert',
props: {
alerts: {
default: []
}
},
data () {
return {
}
},
methods: {
dismissAlert (alert) {
for (let i = 0; i < this.alerts.length; i++) {
if (this.alerts[i].message === alert.message) {
this.alerts.splice([i], 1)
}
}
}
}
}
</script>
I can add to this list now by using this.$alerts.push({}) and i can see they are added by console.logging the results.
The problem is that the component doesn't recognise them unless i manually go in and force it to refresh by changing something in code and having webpack reload the results. as far as i can see, there is no way to do this programatically.... Is there a way to make prototype components be watched like the rest of the application?
I have tried making the root most file have a $alerts object but when i use $root.$alerts.push({}) it doesn't work because $root is readonly.
Is there another way i can go about this ?
You could make $alerts a Vue instance and use it as an event bus:
exports.install = function (Vue, options) {
Vue.prototype.$alerts = new Vue({
data: {alerts: []},
events: { ... },
methods: { ... }
})
}
Then in your components you might call a method this.$alerts.addAlert() which in turn pushes to the array and broadcasts an event alert-added. In other places you could use this.$alerts.on('alert-added', (alert) => { ... }
Other than that, I think this is a good use case for Vuex, which is pretty much designed for this: https://github.com/vuejs/vuex
Properties defined on Vue.prototype are not reactive like a Vue instance's data properties.
I agree that, in most cases, Jeff's method or using Vuex is the way to go.
However, you could simply set this.$alerts as a Vue instance's data property and then updating that property (which would be reactive) would, by association, update the global $alerts array:
Vue.prototype.$alerts = ['Alert #1'];
Vue.component('child', {
template: `<div><div v-for="i in items">{{ i }}</div></div>`,
props: ['items'],
})
new Vue({
el: '#app',
data() {
return {
globalAlerts: this.$alerts,
}
},
methods: {
addToArray() {
this.globalAlerts.push('Alert #' + (this.globalAlerts.length + 1));
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.1/vue.min.js"></script>
<div id="app">
<child :items="$alerts"></child>
<button #click="addToArray">Add alert</button>
</div>

How to keep data in component when using v-with in Vue js

So here is my problem:
I want to make a component that takes it's values from v-with="values" and add them to my component model after some modification, then display those modified properties.
But from what I understand, when I set values with "v-with", component data are erased so the binding between my component data (not v-with one) and my directives are lost.
I'm really new to this framework, I don't see any solution, so I guess it was time to ask my first question here !
Here is the HTML:
<script type="text/x-template" id="my-template">
<p v-on="click:reloadParentMsg">Msg parent : {{ParentMsg}}</p>
<p v-on="click:reloadChildMsg">Msg child : {{ChildMsg}}</p>
</script>
<div id="myVue">
<my-component v-with="ParentData" ></my-component>
</div>
And here is the Javascript:
Vue.component('my-component', {
template: '#my-template',
data: function () {
return {
ChildMsg: "wololo"
}
},
methods:{
reloadParentMsg : function(){
this.ParentMsg="Parent";
console.log(this.ParentMsg);
},
reloadChildMsg : function(){
this.ChildMsg="Child";
console.log(this.ChildMsg);
}
}
})
var myVue = new Vue({
el: '#myVue',
data: {
ParentData:{ParentMsg: "gloubiboulga"}
}
})
And the js fiddle http://jsfiddle.net/KwakawK/hfj1tv4n/3/
I'm not totally clear on what you're trying to do, but I believe it can be solved by using the second form of v-with, which is v-with="childProp: parentProp". Rather than the parent property overriding all of the child data, this will replace only the property on the left of the colon.
So I think your code can be fixed by changing the v-with to this:
<my-component v-with="ParentMsg: ParentData.ParentMsg" ></my-component>
Here's the updated code as a snippet:
// register the grid component
Vue.component('my-component', {
template: '#my-template',
data: function () {
return {
ChildMsg: "wololo"
}
},
methods:{
reloadParentMsg : function(){
this.ParentMsg="Parent";
console.log(this.ParentMsg);
},
reloadChildMsg : function(){
this.ChildMsg="Child";
console.log(this.ChildMsg);
}
}
})
// bootstrap the demo
var myVue = new Vue({
el: '#myVue',
data: {
ParentData:{ParentMsg: "gloubiboulga"}
}
})
<script src="http://cdnjs.cloudflare.com/ajax/libs/vue/0.11.4/vue.min.js"></script>
<script type="text/x-template" id="my-template">
<p v-on="click:reloadParentMsg">Msg parent : {{ParentMsg}}</p>
<p v-on="click:reloadChildMsg">Msg child : {{ChildMsg}}</p>
</script>
<div id="myVue">
<my-component v-with="ParentMsg: ParentData.ParentMsg" ></my-component>
</div>
See the Vue guide for more information.

Categories

Resources