This is probably a simple question, but can someone pls clarify the following javascript code?
// 'response' is the JSON data gotten from the backend:
....then(response => {
this.myVar = response.data;
});
// 'myVar' is initialised here:
data: function() {
return {
myVar: null
};
}
I can see how we declare a property 'data' that points to a function that returns an initialised variable 'myVar'.
I don't quite get how 'myVar' is set to the response value. I assume it is a JS technique? Can someone please explain this or provide me a link to such?
* EDIT *
From the responses, it seems I didn't supply enough info (sorry about that - I was assuming it be a simple JS issue). I extracted this code from a tutorial, and it works. The code excerpt is from a .vue file. So I supply the whole file here. The question is still the same.
<template>
<div>
<button class="my-button" v-text="myVar.name" :type="type">My Button</button>
</div>
</template>
<script>
export default {
mounted() {
// The "api/vue" route calls a controller that supplies a JSON object with a single name=>value pair
axios.post("api/vue", {}).then(response => {
// ?? How exactly does myVar get allocated the JSON data?
this.myVar = response.data;
});
},
// initiate the 'myVar' variable
data: function() {
return {
// ?? this object is delared here and somehow accessible to the whole file?
myVar: null
};
},
};
</script>
this whole code makes a Vue Component. In a Vue Component, initial data should be a function, which will be invoked when component created. the object that returned by data() is the initial data for the component, and it's observable(observable means, if you change it, something other will be changed also). the techniche of javascript used here is Object.defineProperty : https://v2.vuejs.org/v2/guide/reactivity.html
base on your code, it says you have a initial data named 'myVar', if you change the value of 'myVar', the dom which bound with 'myVar' will change automatically. in your code, it's the text of the button.
mounted
this is a life-cycle hook, it will be invoked after the component mounted into the dom.
you called an AJAX at here, then you used arrow function to deal with the result of AJAX. arrow function means it's this will not change (here , this equals the Vue Component).
we already have a initial data 'myVar' on this component, now we change it:
this.myVar = response.data;
because it's observable/reactive, the text of the button in your template will be changed.
by the way, your initial myVar is null, so your initial text myVar.name will cause an error
// 'response' is the JSON data gotten from the backend:
....then(response => {
this.myVar = response.data;
});
In this first method, you are fetching data from a server, and assigning it this.myVar (not myVar). so it's local to the component, however...
In the code below, whenever you call data(), you are returning an object with a property myVar encapsulated in it. it is not the same as this.myVar.
// 'myVar' is initialised here:
data: function() {
return {
myVar: null
};
}
I'm not sure what you're trying to achieve here, but these variables are unrelated. And maybe the naming needs some work too, so it's easier for us to see what the purpose of this code is. Either way, if this.myVar is a variable owned by your component, the object returned from the data() method will not have any effect on it, since it's structure is different. the myVar in the Object you are returning in data is a new variable, local to that object.
Related
When I started with Vue.js I read about a case where you return a data property with return and sometimes without. I cannot find that article anymore that's why I'm asking here.
That's how I use it today
data: function () {
return {
myData : "data"
}
},
But that's how I see it in documentation very often - don't know the difference anymore:
data: {
myData: "data"
},
https://vuejs.org/2016/02/06/common-gotchas/#Why-does-data-need-to-be-a-function
Why does data need to be a function?
In the basic examples, we declare the data directly as a plain object. This is because we are creating only a single instance with new Vue(). However, when defining a component, data must be declared as a function that returns the initial data object. Why? Because there will be many instances created using the same definition. If we still use a plain object for data, that same object will be shared by reference across all instance created! By providing a data function, every time a new instance is created, we can simply call it to return a fresh copy of the initial data.
I have a template that includes a component.
// pods/workgroup/template.hbs
...
{{workgroup/member-add
wgId=model.id
store=store
peekUser2Workgroup=peekUser2Workgroup
}}
...
Within that component I need to lookup if something is already present in the store.
//somewhere in components/workgroup/member-add/componsent.js
let alreadyInStore = this.store.peekRecord('user2workgroup',u2wId);
I made it work by injecting the store into the component (as above), which of course is bad practise.
So I tried making a property in my parent-controller that does the store lookup:
//in components/workgroup/member-add/componsent.js
let alreadyInStore = this.get('controller').peekUser2Workgroup(u2wId);
//in pods/workgroup/controller.js
peekUser2Workgroup: function(u2wId) {
console.log(this);
console.log(this.store);
return this.store.peekRecord('user2workgroup',u2wId);
}
This works fine as long as I pass the complete store into the compentent as above.
However, if I don't pass the store to the component it get's undefined, although never accessed from the component directly (the store is present in the controller alone).
Logging into console of this gives me surprisingly the component, not the controller, this.store is undefined.
So I've learned, that with this I don't access the controller itself when a function/parameter gets called from outside/a component.
The question is, how can I make the controller to reference to itself with this?
Or how can I access the store when calling a parameter from outside?
Do I really need to pass the controller itself to himself??
like so:
// in component
let alreadyInStore = this.get('controller').peekUser2Workgroup(this.get('controller'), u2wgId);
//in controller
peekUser2Workgroup: function(myself, u2wId) {
console.log(this);
console.log(this.store);
return myself.store.peekRecord('user2workgroup',u2wId);
}
That seems very odd to me, and looks like I'm shifting around even more than I did initially when simply injecting the store to the controller...
Ember: 2.0.1
Ember-Data: 2.0.0
Instead of passing the store into the component as a property, inject it using Ember.service like this:
store: Ember.service.inject()
Then instead of passing in the function, just pass in the id vale you're looking up:
{{workgroup/member-add
wgId=model.id
}}
Now in your component you can fetch the record:
workgroup: function(){
return this.get('store').peekRecord('user2workgroup', this.get('wgId'));
}.property()
I am using the q service in one of my controllers to make sure my requests finish before binding the responses in the then clause. Now here is the tricky part. There is a directive on the page who's template updates a scope variable. This scope variable is used to switch between different parts of the response json, A selector if you will. I need to updated a variable set in the then clause after the page is loaded. It is set by the id added in a directive.
I can't seem to figure out an efficient way to go about updating them.
$scope.selector = {}; //property added from a child scope
$q.all({
//some factory calls and assignment to properties
}).then(function(responses){
//scope variable assignments off of the responses object.
//some assignment that uses the selector. a[selector.id] ex.
}, function(err){
$log.error(err);
}).finally(function(){
//some setting of load params
});
//Then I need to update those variables set in the then based on whether or not the selector id was changed in the directive template.
Any help would be greatly appreciated.
Taking a guess here as the question isn't clear but from the looks of it, you should just save the entire set of responses on the scope and then pull out the data you need. I don't see why you are trying to update the entire response everytime you want to pull one aspect out.
$scope.selector = {}; //property added from a child scope
$scope.responses = {};
$q.all({
//some factory calls and assignment to properties
}).then(function(responses){
//scope variable assignments off of the responses object.
//some assignment that uses the selector. a[selector.id] ex.
$scope.responses = responses;
}, function(err){
$log.error(err);
}).finally(function(){
//some setting of load params
});
// Use something like $watch here and have it call a function
Watchers in AngularJS might be helpful as well.
I created an Angular factory for fetching JSON data. I'm using $resource with the get method to retrieve this JSON object from the server. The object itself contains child objects.
I've been trying to use the data I retrieved with this factory in my controller but when I call the $scope variable, I get some variation of this
Cannot read property propertyName of undefined.
I could read the property when I logged it to the console so I don't get why it just disappears. To diagnose it, I tried passing by reference another variable.
The problem is that I can find the object and its keys by logging the console. The problem is that when I try to use this data, the object keys become undefined. I have no idea why this happens.
NewOrder.get({"id": $stateParams.loopId, "orderId": $stateParams.orderId}).$promise.then(function(order) {
$scope.myData = order["data"];
console.log("this is an order", order);
});
Here is my factory
angular.module('myApp')
.factory('NewOrder', function($resource) {
return $resource('/api/loops/:id/orders/:orderId');
});
I found out that if I create a new variable and set it equal to the value of myData, the object inside my key disappears.
This works
$scope.getOrder = function() {
console.log($scope.myData);
}
=> Object {recruitingLeague: "NAHL", playerPosition: "LeftDefense", playerDOB: Object, playerHeight: Object, playerWeight: Object…}
Creating a new variable and passing by reference the value of the previous variable (for diagnosis purposes) doesn't.
$scope.newData = $scope.myData;
$scope.getOrder = function() {
console.log($scope.newData);
}
=> undefined
I cannot understand why the objects I'm retrieving from my server suddenly disappear.
The service is asynchronous, so $scope.myData isn't there when
$scope.newData = $scope.myData;
occurs, but it is already there when
$scope.getOrder = function() {
console.log($scope.myData);
}
is called.
I want to insert a Meteor template (a simple login form) but I want to control what happens after the form is submitted. Ideally, I'd like to be able to pass a function afterLogin() to the template. But I'm quite unsure how to do this and if this is even possible.
I've recently seen an interesting package viewmodel and I'm not sure how related it is. But the goal in this context is basically to render a view with a different view model.
Any ideas? I'm currently using a session variable and then after login, I check that session variable to run the correct function but this is ugly and not easy to work with. Any ideas?
This is how I do it :
I assume that your login form is called from within a parent template, use the attributes syntax to pass the value of a custom helper to the data context of the login form.
<template name="parent">
{{> loginForm options=loginFormOptions}}
</template>
The helper returns an object encapsulating a function, the caller is responsible for setting this function to whatever they want.
Template.parent.helpers({
loginFormOptions:function(){
return {
afterLogin:function(){
// we assert that the context is correct :
// this will print Template.loginForm
console.log(this.view.name);
}
};
}
});
Your login form code, acting as a library, can read from its data context the function that was passed by the caller template, and then call the function with the proper this context.
Template.loginForm.events({
"submit":function(event,template){
// ...
Meteor.loginWithPassword(...,function(error){
if(error){
console.log(error);
return;
}
// guard against the existence of the custom afterLogin function
if(template.data.options && template.data.options.afterLogin){
// execute the custom function with proper context
template.data.options.afterLogin.call(template);
}
});
}
});
First of all, I would use Iron Router for navigating through different views of my application, you can get it here:
https://github.com/EventedMind/iron-router
meteor add iron:route
Then, check http://docs.meteor.com/#template_events. I would use something like:
Template.loginFormTemplate.events({
'click .loginButton': function() {
//... if success login ...
Router.go('nextScreen');
}
});
[Update 1]
I am afraid that trying to pass function to Route is an ugly approach in a sense of Meteor architecture.
You can try though, defining some Global variable, which is responsible for listening and forward-triggering events across the Routes
var eventHelper = (function () {
var self = _.extend({
afterLogin: function () {
self.trigger('forwardedEvent');
}}, Backbone.Events);
return self;
})();
Route1.events({
'click': function () {
//... Let's call after login
eventHelper.afterLogin();
}
});
eventHelper.on('forwardedEvent',function() {
// ...
});