How to delay AngularJS App Initialization? - asynchronous

I have some data that is being processed asynchronously in the background and want to delay the initialization of the entire AngularJS application until this finished.
BackgroundData.initialized is a Q promise, so something like this:
BackgroundData.initialized.then(AngularDoYoStuff)
The problem I run into is the home page's controller starts its initialization procedure, hits BackgroundData and either it has the wrong/no data.
What function can I wrap Angular's initialization in so, instead of just dom-ready, it waits for both dom-ready and BackgroundData.initialization?
UPDATE
I have gotten closer with the documentation on manual bootstrapping:
angular.element(document).ready ->
setupGA()
window.BackgroundData = new DataMachine()
BackgroundData.initialized.then ->
angular.bootstrap(document)
But when the controller files load (after this file), they are still getting initialized before BackgroundData is defined
UPDATE 2
Removing the ng-app directive in the HTML seems to have fixed the problem (since that was telling Angular to auto-init) but now it just ignores all of my angular.module calls

The problem was that I had left the ng-app directive in the html tag, which tells Angular to auto-initialize that scope. Removing it allowed my manual initialization to run correctly.

as Chris mentioned, it can be done with angular.bootstrap and not mentioning the ng-app:
<div id="appArea" ng-controller="someCtrl">
{{name}}
</div>
<script>
angular.module('someApp', [])
.controller('someCtrl', function($scope) {
$scope.name = "test name";
})
setTimeout(function() {
angular.bootstrap(document, ['someApp']);
}, 2000);
</script>

One way to approach this if you are using routes in your app is to have the app initialize but wait on defining routes until the data is available.
I.e. provide a user-friendly 'loading' message, and then load functionality after.
You are able to inject $route where required, and then call something like:
$route.routes["/RouteName/:param"] = {templateUrl:"template.html", reloadOnSearch:true, controller:"ControllerName"};
After that, call $route.reload() or $rootScope.apply() to refresh.
More information at https://groups.google.com/forum/?fromgroups=#!msg/angular/AokZpUhZ6mw/x2kPIN2VAC0J

Related

Where to call IONIC SQLLITE select statement

I have a DB function as shown below:
If I want to use the results of this query in a page module, where do I call this function? Do I call it in the constructor or the lifecycle method (ionViewDidLoad, ionViewDidEnter). Please advise.
Currently I call it in the constructor but it seems like it slows down the app. I am not sure if I am doing the right thing
getAddressDetails() {
return new Promise((resolve, reject) => {
let sql = "select * from tablename"
this.database.executeSql(sql, []).then((data) => {
resolve(data)}
});
}```
(...) main difference between the constructor and the ionViewDidLoad
(...) is that sometimes you want to interact with the DOM (maybe to
initialize a map).
In that case, if you try to access the DOM in the constructor, you
will notice that the DOM is not ready by that point and you won't be
able to get the map element. The correct approach to do it would be
inside the ionViewDidLoad (...).
See this answer.
constructor is called before all, once per instantiation of the
page, here you can do initialization that does not refer the HTML DOM
ionViewDidLoad is called when the page DOM has been loaded, before
than the page is shown, also a single time per page instantiation,
here you can do initialization thet needs the HTML DOM to be ready
And see this answer.

Can someone explain how Meteor.defer() works?

So... Meteor.defer(function(){ // stuff }) isn't in the docs:
https://github.com/meteor/meteor/issues/2176
But this links seems to say that it's simply equivalent to
Meteor.setTimeout(function(){ // stuff }, 0);
If that's the case, how does this do, um, anything? It's basically saying "wait for 0 ms and then run the function".
So... it runs the function instantly.
What am I missing here? Is this kind of like Tracker.afterFlush or something? Does it somehow wait for "things" (what kind of things?) to finish before running?
I see Meteor.defer() a lot on SO being used as a bit of a hack on added helper methods to run after the dom is (somewhat) loaded - basically to get the same effect as running code inside of a Template.foo.rendered method.
However, the main (and best) use of Meteor.defer is to run a task asynchronously.
Let's say we have an app where we are sending an email. On the server, it may take several seconds for that to process inside of a meteor method, slowing down your application drastically. However, if you wrap that process in a Meteor.defer the email processing won't block execution, the email still sends (when it gets a chance, not instantly), but everything runs much faster, since the code that follows isn't waiting. There is a great example lesson about deferring execution at Bulletproof Meteor.
You can actually get the same effect with a setTimeout(f,0) - if you have a slow function, you could wrap it in the setTimeout and the rest of the code will complete, and 'defer' the slow process in the timeout, so although it doesn't seem like it, setTimeout(f,0) does actually have a pretty useful purpose!
To see an example of this in action, here's a fiddle, open the console and watch where 'foo' logs.
I faced some issue in my project because of asynchronous callback. Inside onCreated i was making a server Meteor.call and set the response inside reactiveVar. And i was doing something inside onRendered with that reactiveVar. Every time reactiveVar was showing undefined.
So i used Meteor.defer(function(){...}) inside onRendered and it sloved my issue.
Here is some demo with and without using Meteor.defer()
Template.myTemplate.onCreated(function () {
var instance = this;
instance.myTemplateModel = new ReactiveDict();
Meteor.call('all-user', function(err, res){
if(res){
console.log('inside callback');
instance.myTemplateModel.set('users', res);
}
});
});
Template.myTemplate.onRendered(function () {
var instance = this
console.log('rendered start');
Meteor.defer(function(){
console.log(instance.myTemplateModel.get('users'));
});
console.log('render end');
});
Console:
/*Without Meteor.defer()*/ | /*With Meteor.defer()*/
render start | inside callback
undefined | render start
render end | render end
inside callback | [Object, Object, Object]

In Meteor, is there a way to detect when data is fully loaded to the browser?

This problem probably has been asked before, but I cannot seem to figure out an easy way to do it.
I have a Meteor page that shows messages posted by users in chronological order (newest message at the bottom). I want the page to:
#1) Subscribe to the messages (using publish/subscribe) based on a parameter supplied in the URL
#2) Render the page
#3) Display the messages on the page
#4) Scroll to the bottom.
There's no apparent way to know when 1, 2, and 3 are complete before initiating the scroll to bottom. Meteor does have an observe/added function to do an event when new messages are added to a subscription, however that's only when documents are insertted into Mongo, and it does not trigger when displaying results to the initial subscription.
I'll show code for the steps above:
#1: Subscribe to the messages using publish/subscribe: in /client/messages.js
Template.messages.created = function() {
this.autorun( function() {
this.subscription = Meteor.subscribe("messages", Router.current().params.category);
}.bind(this));
};
#2 Render the page, in /client/messages.html
<template name="messages">
{{#each messages}}
{{messageText}}<br><br>
{{/each}}
</template>
#3: Display the mssages on the page: /client/messages.js
Template.messages.helpers({
messages: function() {
var category = Router.current().params.category;
return Messages.find({category: category}, { sort: { messageDate: 1 } });
},
});
All this works, but does not automatically scroll to the bottom.
I cannot add a jquery scroll command to the Meteor onRendered section because it runs BEFORE the data is written to the DOM. I can do a Meteor.setTimeout to wait 1 second before scrolling to the bottom, but does not work if it takes longer than a second to fill the DOM with subscribed data.
Here's another thing complicating the matter. I am supplying the category variable in the URL. When the client selects another category, using Meteor/Blaze pathFor,
{{pathFor 'messages' channelid='new'}}
the browser does not reload/rerender the page, it simply updates the URL parameter which triggers the autorun to change what messages it has subscribed to. It simply writes the new data to the DOM. Because of this, I cannot to a jquery $(document).ready to detect whether the page is ready (because the page is always ready), and I cannot use some fancy handlebars thing like {{scrollToBottom}} at the end of my {{#each}} in messages.html because that it not re-run.
So, is there a simple way to detect when Meteor/Blaze completely finishes writing new data to the browser?
If I understand correctly, you really just want to know when all of your data is published from the server to the client (so you can then do something, in this case, scroll to the bottom).
All calls to Meteor.subscribe() return a subscription handle. This has a ready() method, that tells you all the data has been sent. This includes those done at a template level (which you might want to consider if appropriate for your use-case). More information here:
http://docs.meteor.com/#/full/Blaze-TemplateInstance-subscribe
There are several ways to use this - you need to decide what is appropriate for you. As ready() is reactive, you can just use this. However, you might find it easier to implement an onReady callback within your subscription (see documentation above).
I suspect you will need to implement an autorun within your template rendered. This is to avoid the reverse scenario where the data arrives before the rendering (and so the callback fires too early). Something like this should do the trick (noting you have already set your subscription handle in your template creation function to this.subscription):
Template.messages.onRendered(function() {
var self = this;
this.autorun(function(computation) {
if(self.subscription.ready()){
$(document).scrollTop($(document).height());
computation.stop()
}
});
});
This should ensure both that the function is only called after rendering and the data from the subscription is complete. The autorun is also stopped after executing the scroll to stop continued calling should new documents cause ready() to no longer be truthy (and then become ready again) - which might surprise people with the page re-scrolling to the bottom!

Correctly stopping an autorun() function when template is destroyed

I have a template that contains a chart, rendered using MorrisJS. The chart should update when the currentData session variable is changed, so I have made it a reactive data source with:
Template.chart.rendered = function() {
var template = this;
Deps.autorun(function(c) {
// Stop if the template is removed from the dom
// Q: Is this really right?
if(template.__component__.dom.parentNode() === null) {
c.stop();
return;
}
var results = Session.get('currentData');
// ... render a chart with `results` as the data
Morris.Bar({element: template.$(".chart-container"), data: results, ...});
});
};
Notice how I do a fairly horrid check for when to stop the autorun above. This was necessary because without this, when I navigate away from the page using the template (I'm using iron-router) to another page and back, I get warnings in the log like "Can't select in removed DomRange". I'm pretty sure this is happening because the template instance is removed, but the autorun is still running.
I feel like I'm doing something wrong here, though. Is there (a) a better place to put the autorun so that it doesn't have this problem or (b) a better way to stop the computation when the template instance is removed from the DOM?
I tried to find a way to do it with created and destroyed handlers, but I couldn't figure out how.
Tracker.autorun returns a handle that you can store as a template instance property, then call its stop method in the onDestroyed lifecycle event.
Template.chart.onRendered(function(){
this.computation = Tracker.autorun(function(){...});
});
Template.chart.onDestroyed(function(){
this.computation.stop();
});
EDIT 29-09-2014
In newer versions of Meteor (0.9 onward), there is a new autorun function available on template instances which provide simpler code to achieve the same result : no need to store and stop the computation manually, this is taken care of by the framework.
Template.chart.onRendered(function(){
this.autorun(function(){...});
});
With the new autorun
Template.chart.onRendered(function(){
this.autorun(function(computation){
...
computation.stop();
});
});
but with this autorun, when chart template is removed from the DOM it is removed automatically.
This is in Meteor documentation here:
The Computation is automatically stopped when the template is destroyed.

ReferenceError, meteor

I've seen this problem posted but did not really understand the explanations. I am new to meteor and doing this project. From what I understand some of the functionality with node is not available in the browser, but I am unsure of how to fix the problem. I have tried to wrap the code within some function(window) but still was not able to get it working. I also tried to use npm but was coming up empty with some errors about some Illegal tokens. Thanks for any help.
Your app is crashing. Here's the latest log.
/home/alex/TacticsTrainer2/.meteor/local/build/programs/server/boot.js:186
}).run();
^
ReferenceError: window is not defined
at app/js/bootstrap.min.js:6:353
at app/js/bootstrap.min.js:8:3
at /home/alex/TacticsTrainer2/.meteor/local/build/programs/server/boot.js:155:10
at Array.forEach (native)
at Function._.each._.forEach (/home/alex/.meteor/tools/5bf1690853/lib/node_modules /underscore/underscore.js:79:11)
at /home/alex/TacticsTrainer2/.meteor/local/build/programs/server/boot.js:82:5
=> Exited with code: 8
=> Your application is crashing. Waiting for file change.
It looks like you may have not structured your app correctly. You have code that is intended to run on the client/browser side that is running on the server side.
In your app:
place all code to be run on the client in the /client directory
place all server side code in the /server directory
place code that you want to run on both the server and client in the root folder or a non reserved name (public, private, tests, server or client)
For more details about this see the meteor docs: http://docs.meteor.com/#structuringyourapp
ReferenceError is a Node error. Meteor is a framework on top of Node.
Node has a global scope (aka Node's global variable). This error is thrown by Node (not Meteor) if you try to access an undefined global variable.
Browsers also have a global scope called window, and do not throw ReferenceErrors when undefined variables are accessed.
Here's a pattern I like for adding functionality to a class (it's very Meteor):
/lib/Helpers.js <-- Helpers for everyone (node+browser)
/server/Helpers.js <-- Server helpers (node)
/client/Helpers.js <-- Client helpers (browser)
Consider these implementations:
// /lib/Helpers.js
Helpers = {/* functions */}; // Assigned to window.Helpers and global.Helpers
// /server/Helpers.js
Helpers = _.extend(Helpers, {/*more functions*/}
// /client/Helpers.js
Helpers = _.extend(Helpers, {/*more functions*/}
This is a trivial example. What if I didn't want to worry about load order? Why not _.extend() in /lib/Helpers.js?
// /lib/Helpers.js
// Helpers = {/* functions */}; // Overwrites...
Helpers = _.extend(Helpers, {/* functions */}); // ReferenceError
Because you'll get a ReferenceError from Node if Helpers isn't defined - specifically the "Helpers" used as an argument. (Node knows to assign Helpers as global.Helpers).
Here are two ways to "fix" this:
1) Assign Helpers to something
// /lib/Helpers.js
if (typeof Helpers === 'undefined') Helpers = {};
Helpers = _.extend(Helpers, {/* functions */});
2) Use helpers from the global
// /lib/Helpers.js
Helpers = _.extend(global.Helpers, {/* functions */}); // works in node, but...
Both of which suck.
1)'s syntax is horrible.
2) works in node, but there is no global in browsers. So it fails it's purpose.
So I gave up and went back to overwriting it the first time in lib, and looking for runtime errors if anything was overwritten.
If you have a handy cross-browser syntax for this, do comment :-)
I think you are calling a method that is loaded on the client and server.
You could write the code inside the Meteor.method like this:
if (!this.isSimulation) {
// code with node js that should only run in the server
} else {
// code that doesn't have nodejs runtime (browser)
}
This happens when you loaded the Method both in the client and server.

Resources