Meteor: getting route param on page loading - javascript

Here is my issue:
I am trying to make an API call when loading my page to load one entity, saving few of this entity attributes into the session - so I basically make one call, and use the attributes whenever I need them.
But when I try to get the id param from the Route, the Router.current() at the beginning of my script, it is null.
On the other hand, when I call the same Router.current() on a helper, I can get my param.
Any idea what is wrong / how can I get this param before calling my helpers ?
Here is my html where I will be using one of this attribute:
<input type="text" name="name" id="addCampaignInputName" value="{{currentCampaignName}}" placeholder="New Campaign Name">
And the JS:
if (Meteor.isClient)
{
$(function () {
console.log( Router.current()); ==>> NULL
if( Router.current() !== null) {
Meteor.call("getApiCampaignsById", Router.current().params.id, function(error, results) {
Session.set('currentCampaignDetailsName', results.data.name);
Session.set('currentCampaignDetailsTrackingMethod', results.data.tracking_method_id);
Session.set('currentCampaignDetailsCampaignTypeId', results.data.campaign_type_id);
});
}
});
Template.addCampaign.helpers({
currentCampaignName: function() {
console.log( Router.current()); ===>> DUMP DATA
return Session.get('currentCampaignDetailsName');
},
});
}

That's because template helpers are always run inside reactive computation, and Router.current() happens to be a reactive data source, so when it's ready the helper will rerun with the correct value.
Since you're using iron:router, you could use an onBeforeAction hook, because they can be tied to a specific route and are executed when route parameters are available (use this.params).
Router.route("/my-route", {
name: "myRoute",
onBeforeAction: function(){
Meteor.call("getApiCampaignsById", this.params.id, function(error, results) {
Session.set('currentCampaignDetailsName', results.data.name);
Session.set('currentCampaignDetailsTrackingMethod', results.data.tracking_method_id);
Session.set('currentCampaignDetailsCampaignTypeId', results.data.campaign_type_id);
});
//
this.next();
}
});

You should load your template only once its subscriptions/parameters are ready. Look at my answer here, maybe it will solve your problem (especially the part where you attach your parameters to your template data object.)

Your first function is likely running on the page before anything else has loaded, and it doesn't know what Router is. The best way to set these before the template helpers are loaded is to use iron-router's onBeforeAction hook:
Router.route('/', {
name: 'home',
onBeforeAction: function() {
//set your session variables here
this.next();
}
});
This way, the router should load the page with your session vars already set.

Related

Ember How to retain query parameters while manually refreshing the page?

I am using ember 2.7.0.while manually refreshing the page ember clears the ember-data as well us query parameters, so i am unable to load the page in setupController while refreshing. Is there any possible way to retain both model & query parameters, at least retaining query parameter would be fine to reload my page.
route.js
model(params) {
return this.store.peekRecord("book",params.book_id);
},
setupController(controller,model,params){
if(!model){
//fetch book record again if the model is null
}
controller.set('isdn',params.queryParams.isdn);
controller.set('book',model);
}
Any help should be appreciable.
Edited setupController as per Adam Cooper comment :
setupController(controller,model,params){
var isdn = params.queryParams.msisdn;
controller.set('isdn',isdn);
if(!model){
this.store.findRecord('book', isdn).then((customer) => {
this.set('book',customer);
},(resp,status) => {
this.set('errorMessage', `Book with this ${isdn} does not exist.`);
this.set('book', []);
});
}else{
controller.set('device',model);
}
}
Page gets rendered before "findRecord" returning promise.Is there any way to stop page rendering till find record resolves the promise?
You are setting in route properties instead of controller..
setupController(controller, model, params){
var isdn = params.queryParams.msisdn;
controller.set('isdn', isdn);
if(!model){
this.store.findRecord('book', isdn).then((customer) => {
controller.set('book', customer);
}, (resp, status) => {
controller.set('errorMessage', `Book with this ${isdn} does not exist.`);
controller.set('book', []);
});
}else{
controller.set('device', model);
}
}
Only the controller properties will decorate template.
You can even try the below, why don't you give opportunity to model hook to resolve since that will wait for the Promises to resolve.
model(params) {
var result = this.store.peekRecord("book",params.book_id);
if(result !== null){
result= this.store.findRecord('book', params.book_id)
}
return result;
}
setupController(controller,model){
controller.set('book',model);
}
You will need to generate an actual controller for your route and then define a queryParams property in the controller. It looks like the query param you're trying to hold onto is isdn so your controller should look something like:
export default Ember.Controller.extend({
queryParams: ['isdn']
});
"manually refreshing the page ember clears the ember-data as well us query parameters"
Once you completely refresh the browser, a new ember app instance is created and hence ember-data cannot be retained. Ember-data is just for the app on the UI, once ember is exited it will not be retained.
"as well us query parameters"
your query params are part of your url and it should not get cleared. Make sure the below two are present
Include queryParams in ur controller i.e.
queryParams: ['param1', 'param2']
And in your route make sure you have done
queryParams : {
param1: {
refreshModel: true
},
param2: {
refreshModel: true
}
}
"Page gets rendered before "findRecord" returning promise"
You are not doing something right, is the adapter, model, serializer etc defined correctly(if required) in order to use findRecord? Just to debug return a plain object and make sure ur setupController is called before rendering. i.e.
model() {
return {dummy: 'dummy'};
}

How to wait for Meteor.user() to be defined in Template onRendered

I am working on a Meteor project and I need to access some information stored in a user's profile to make a map on the page. However, when I try to access Meteor.user(), I receive undefined because when the function is called, Meteor.user() has not been loaded.
Template.body.onRendered () ->
console.log Meteor.user()
address = Meteor.user()['profile']['address']
GoogleMaps.ready 'studyMap', (map) ->
# maps code that relies on address
Because Meteor.user() is not defined, I am not able to get the map to work. How can I wait for Meteor.user() to be defined?
I think your going to need an asynchronous callback to load the map only when address = Meteor.user()['profile']['address'] loads. As far as how to implement this, I'm not really sure.
One of my apps has to do something similar to wait on a subscription. I defined the waitOn() handler in my router.
Router.map(function(){
this.route('Brocator', {
path: '/',
template: 'brocator',
waitOn: function() {
return (Meteor.subscribe('people', this.params._gridId)
&& Meteor.subscribe('gridlog', this.params._gridId) );
},
data: function() {
var gridId = this.params._gridId;
return (People.find({gridId: gridId})
&& GridLog.find({gridId: gridId}));
},
fastRender: true
}),
... other routes deleted for clarity
})
You may decide to use something along the lines of
return Meteor.user() != 'undefined';
in your waitOn() handler.
Posted an answer for meteor/react here however the same can be done with blaze. I am not familiar with blaze but within your Template.body.onRendered function you can listen to make sure that the login services are configured before rendering anything.
Tracker.autorun(() => {
if (Accounts.loginServicesConfigured()) {
// accounts is ready go ahead and render
} else {
// still waiting show loading spinner
}
});
I would recommend extracting this logic out to a higher level so that you don't keep repeating the same code.

Accessing asynchronously loaded object based on relations in Ember

I have a trouble with asynchronously loaded models in Ember. I thought I have already understood the whole "background Ember magic", but I haven't.
I have two models, let's say foo and boo with these properties:
foo: category: DS.belongsTo("boo", { async: true })
boo color: DS.attr("string")
In my route, I load all foos:
model: function(params) {
return this.store.findAll("task", "");
},
than in my template I render a component: {{my-component model=model}}. In the component's code I need to transform the model into another form, so I have:
final_data: function() {
this.get("model").forEach(function(node) {
console.log(node.get("category"));
});
return {};
}.property("model"),
When I try to access the "category" in the model, my code crashes:
EmberError#http://localhost:4200/assets/vendor.js:25705:15
ember$data$lib$adapters$errors$$AdapterError#http://localhost:4200/assets/vendor.js:69218:7
ember$data$lib$adapters$rest$adapter$$RestAdapter<.handleResponse#http://localhost:4200/assets/vendor.js:70383:16
ember$data$lib$adapters$rest$adapter$$RestAdapter<.ajax/</hash.error#http://localhost:4200/assets/vendor.js:70473:25
jQuery.Callbacks/fire#http://localhost:4200/assets/vendor.js:3350:10
jQuery.Callbacks/self.fireWith#http://localhost:4200/assets/vendor.js:3462:7
done#http://localhost:4200/assets/vendor.js:9518:1
.send/callback#http://localhost:4200/assets/vendor.js:9920:8
It seems to me, like the Ember didn't load the boos. How should I access them right to make Ember load them?
It's trying to load category, but the adapter is encountering some error. Can't tell what from your example.
Check your network tab.
When you access an async association from a template, Ember knows what to do. From code, such as your component's logic, Ember has no idea it needs to retrieve the association until you try to get it. The get will trigger the load, but will return a promise. You can do this:
get_final_data: function() {
Ember.RSVP.Promise.all(this.get("model") . map(node => node.get('category'))
.then(vals => this.set('final_data', vals));
}
As I'm sure you can see, this creates an array of promises for each node's category, calls Promise.all to wait for them all to complete, then stores the result into the final_data property.
Note, this is not a computed property; it's a function/method which must be called at some point, perhaps in afterModel.

Updating AngularJS View when user action has completed

I'm writing some small exercises to teach myself AngularJS and I'm trying to write some simple user Authorisation tasks. I have a form to collect/input a username and password, these are then sent to a rest service using $http and CORS (as my REST service is running on a different port), they are checked and if there is a match I return a UUID and create a token and I $broadcast a loggedIn value to true that is on the $rootScope, something like this.
// this is in a service I call 'authService'
this.login = function (user) {
return $http({method: 'POST', url: 'http://127.0.0.1:3000/login', data: user})
.then(function (response) {
// set up a local storage token
storageService.setLocalStorage('token', response.data[0].uuid);
// broadCast is loggedIn - we have a match
$rootScope.loggedInUser = true; // this is set to false at the .run() of the app
$rootScope.$broadcast('LoggedIn');
return 1;
}, function () {
// return http code later
return 0;
});
};
this.getLoggedIn = function () {
return $rootScope.loggedInUser;
};
Now in a separate menu view I have the following condition (the authService is added as a dependancy on the menu controller):
<div id="logIn" ng-show="!authService.getLoggedIn()">...</div>
Now when I load the app for the first time the condition is correct, however I want this condition to update should a user log in correctly (so the div) isn't shown. In the menu controller I have the following code, none of it seems to do anything?
$scope.$on('LoggedIn', function () {
authService.getLoggedIn(); // doesn't update the view?
console.log($rootScope.loggedInUser); // returns true
console.log(authService.getLoggedIn()); // returns true
});
$scope.$watch('loggedInUser', function () {
console.log('loggedInUser has changed ' + $rootScope.loggedInUser);
// This runs once when we set $rootScope.loggedInUser in the .run() of the app, output is: 'loggedInUser has changed false'
// then when we have successfully logged in again, output is 'loggedInUser has changed true'
});
Okay, so the condition on the <div> in my menu view doesn't update when I changed the $rootScope.loggedInUser, I'm doing something wrong in my approach, can someone give me some advice or correct my approach to this. Thanks
You don't have to do anything special in Angular to update view when some prop is updated, provided it is in the correct scope. I provided a Plunkr for you which demonstrates that you don't have to do anything special to refresh the view.
http://plnkr.co/edit/B6kUwJdA4lRKkjAItSFO?p=preview
You don't have to do watches, you don't have to do anything. That is the power of Angular. Also, it is weird that you set stuff in rootscope. My advice for you is to look at my example and revise/restructure your code. Also, having stuff like this:
ng-show="!authService.getLoggedIn()"
Is not the recommended way of doing things. You can have a controller, in which you say:
$scope.userLoggedIn = autService.getLoggedIn();
and then in your view:
ng-show="userLoggedIn"
You can also take a look at this plunkr:
http://plnkr.co/edit/aRhS0h7BgpJJeRNvnosQ?p=preview

Adding an AngularJS resource that may or may not exists

Using AngularJS 1.0.4
One of our Angular apps is dependent on a resource being loaded before anything else can be loaded. We do this from a service that gets initialized in app.run() and then broadcast an event that everything else listens for to start loading.
In the controllers we also need to have access to the resulting resource. So I then have the following in each one:
$scope.parent = null;
if(!svc.parent) {
$scope.on('parentLoaded', function() {
$scope.parent = svc.parent;
});
} else {
$scope.parent = svc.parent;
}
Each of the controllers is tied to a view and can be called in any order. So it's not guaranteed that the resource is loaded when the controller gets called, although it can be if another controller was called before hand. The load event only gets trigger the first time the service is initialized when the app first loads.
Is there a better way to this?
It seems kind of redundant & not clean.
I would just use a promise. You would have something like:
var deferred = $q.defer();
$http.get('/application').then(function(res) {
deferred.resolve(res);
});
function fetch() {
return deffered.promise;
}
To load your initial resource, we'll call the resource "application" for example. Then, to load your next portion, you can do:
application.fetch().then(function(svc) {
//res is whatever is returned from our $http.get, earlier
$scope.parent = svc.parent
//do whatever required your resource here
});
Instead of doing this:
$scope.parent = null;
Create an empty array instead (or object depending on what resource is).
$scope.parent = [];
This way angular object watchers will react to changes of the array. For more advanced issues can use promises when loading data

Categories