In Vue - what's the difference between this.$data.foo vs this.foo? - vue.js

I'm curious since both seem to work and I'm having trouble finding an answer in the Vue docs. Is there a reason you should refer to data in Vue as this.$data.whatever vs just this.whatever?

The data with $ prefix is specifically defined for accessing vue data property rather than user defined property.
For eg.
var data = { foo: 'foo' } // user defined data
var inst = new Vue({ data: { foo: 'foo' } }) // data - built-in vue property
data.foo // user defined data
inst.$data.foo // vue property - data
When you're inside the Vue hooks, you can just simply use this.foo for eg. inside computed method.
For more detail, you can see the docs

The $data attribute is used to access the data property outside the component:
var data = { a: 1 }
// direct instance creation
var vm = new Vue({
data: data
})
vm.a // => 1
vm.$data === data // => true
// must use function when in Vue.extend()
var Component = Vue.extend({
data: function () {
return { a: 1 }
}
})
Source: https://v2.vuejs.org/v2/api/#data

Related

Using custom events in a component that is registered programmatically [VUE]

i am appending a child component in Vue programmatically as seen below:
var ComponentClass = Vue.extend(FormulaGeneratorConstant) //create instance from FormulaGeneratorConstant component
this.constants.push('variable1');
var constant = new ComponentClass({
propsData: {
value: this.constants[this.constants.length - 1]
}
});
constant.$mount();
this.$refs.droppableContainer.$el.appendChild(constant.$el)
but right now i can only pass props in this code.
i would like to know how to implement v-model and handle custom events as well if that is possible.
Found the solution here.
i just passed a created function inside the new Component constructor:
var constant = new ComponentClass({
propsData: {
value: this.constants[this.constants.length - 1]
},
created(){
this.$on(['change'], e => { console.log(e); })
}
});

Set data object to value from Promise in Vue 3

I'm trying to set the data() value in a Vue instance to a value retrieved from a Promise that is returned from a mongodb api call. I'm able to retrieve the data I'm trying to implement, however am struggling to use it in the data() field so that I can access it in the template.
Some of the solutions I've found from vue2 don't seem to work in this instance. Thanks in advance.
Vue
<template>
<Article :title=posts[0].title></Article> // Another Vue component
</template>
let stories;
export default {
data() {
return {
posts: {}
}
},
methods: {
loadPosts() {
stories = getPosts(); // makes an api request to a mongodb that returns a Promise
stories.then(function(response) {
this.posts = response.posts;
console.log(response.posts[0].title); // "My Post Title"
});
}
},
mounted() {
this.loadPosts();
}
}
The error I receive says
Uncaught (in promise) TypeError: Cannot set property 'posts' of undefined
in reference to this.posts = ...
this get the window reference as you are using dynamic scope, use lexical scope binding to get this as Vue
For your specific reference and eagerness to read more about scopes - follow this Static (Lexical) Scoping vs Dynamic Scoping (Pseudocode)
For lexically binding use arrow-function
loadPosts() {
stories = getPosts();
stories.then(response => {
this.posts = response.posts;
console.log(response.posts[0].title); // "My Post Title"
});
}

vue.js two way data-binding between components

Please take a look at this not-working pseudo code:
Vue.component('child', {
props: [],
template: '<div><input v-model="text"></div>',
data: function() {
return {child-text: ""}
}
})
Vue.component('parent', {
template: '<h1> {{text}} </h1>'
data: function() {
return {parent-text: ""}
}
})
What is the most elegant way to fix this code that whenever the user changes the content of input box in child component, then the variable child-text in child component and the variable parent-text in parent component will change automatically? I also want that if the variable child-text and/or parent-text change then the content of input box will change respectively?
I solved this with my own little data store, its a very simple approach but works good enough for me without the necessity to dive into Vuex.
First, I create my data store somewhere before initializing anything else.
window.globalData = new Vue({
data: {
$store: {}
},
});
After that, I add a global Mixin that allows to get and set data to the global storage.
Vue.mixin({
computed: {
$store: {
get: function () { return window.globalData.$data.$store },
set: function (newData) { window.globalData.$data.$store = newData; }
}
}
});
Then, every component can access the data storage by this.$store. You can check a working example here:
https://codesandbox.io/s/62wvro7083

vue computed : add new computed value from created lifecycle hook

I'm beginner in vue
I'm trying to push a computed data with name , that name come from vuex which comes after creating the instance
How can i push new computed property to the instance in the created() hook ??
Here is the code
computed: {
// 3 - i want to extract the object properties here computed
// that is easy bu using spread operator ...
// Problem : vue intialize computed before the created() hook
// so the spreed work befor passing the filling the object
...this.mapGettersObj
},
created(){
// 1- i can access only this line after creating the object
this.stocks = this.$store.state
let arr=[]
for (let stock in this.stocks){
arr.push(stock+'Getter')
}
// 2 - mapGetters returns an object
this.mapGettersObj=mapGetters(arr)
}
If I can create new computed value after creating that will solve the problem
You can do this in beforeCreate hook。
beforeCreate: function() {
this.$options.computed = {
demo2: function() {
return this.demo
}
}
}
I don't know why you are doing what you do, but if you want to have a variable available before the computed is called you can use beforeCreate hook: https://alligator.io/vuejs/component-lifecycle/
You could also do something in the lines of
computed: {
...function() {
this.stocks = this.$store.state
let arr=[]
for (let stock in this.stocks){
arr.push(stock+'Getter')
}
return mapGetters(arr);
}();
},

Vue: shared data between different pages

var app = new Vue({
el: '#app',
data: {
sharedData : ""
},
methods: {
goToPageB: function() {
if (some condition is met) {
window.location.href="pageB.html";
sharedData = 1234; <------- I want to access sharedData in page B as well
}
}
}
I am new to Vue, i made a dummy login page and try to make my main page display sharedData according to the username. But the data is always lost after my app directs to Page B. How can I fix this?
You can pass the sharedData as an URL param : 'window.location.href="pageB.html?sharedData=myData"' and access it in the other page. If you would had been using vue-router, you could just do
this.$route.params.sharedData
As you are not using it, You can just use plain javascript to get it, however you will have to write a helper function like following to get the params, as explained here
var QueryString = function () {
// This function is anonymous, is executed immediately and
// the return value is assigned to QueryString!
var query_string = {};
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
// If first entry with this name
if (typeof query_string[pair[0]] === "undefined") {
query_string[pair[0]] = decodeURIComponent(pair[1]);
// If second entry with this name
} else if (typeof query_string[pair[0]] === "string") {
var arr = [ query_string[pair[0]],decodeURIComponent(pair[1]) ];
query_string[pair[0]] = arr;
// If third or later entry with this name
} else {
query_string[pair[0]].push(decodeURIComponent(pair[1]));
}
}
return query_string;
}();
Vue way
You can use a centralised state management to manage your shared data, which will be available across page unless you reload the page.
You can define you state, like following:
var store = {
state: {
message: 'Hello!'
},
setMessageAction (newValue) {
this.state.message = newValue
},
clearMessageAction () {
this.state.message = 'action B triggered'
}
}
and you can use it in your components like following:
var vmA = new Vue({
data: {
privateState: {},
sharedState: store.state
}
})
var vmB = new Vue({
data: {
privateState: {},
sharedState: store.state
}
})
A Better Vue way: Vuex way
However Vue ecosystem havs a dedicated a redux like state management system as well, called vuex which is quite popular among community. You store all your projects/users etc in vuex state, and each component can read/update from the central state. If its changed by one component, updated version is available to all components reactively. You can initiate the data in one place using actions in vuex itself.
Following is the architecture diagram:
You can have a look at my answer here on similar question and have a look at example on how to call api and save data in store.
It doesn't really look like you need to employ state management for something that minor. If you're simply looking to pass data from one child component to another then I would suggest using an event bus.
Non Parent-Child Communication
Sometimes two components may need to communicate with one-another but they are not parent/child to each other. In simple scenarios, you can use an empty Vue instance as a central event bus:
var bus = new Vue()
// in component A's method
bus.$emit('id-selected', 1)
// in component B's created hook
bus.$on('id-selected', function (id) {
// ...
})
In more complex cases, you should consider employing a dedicated state-management pattern.