AngularJS, RequireJS, JQuery loading in wrong order - angularjs

I'm developing simple AngularJS (I'm quite new with Angular) application and I encountered problem that js are loading in wrong order. Below there are relevant (I hope) snippets of my app.
index.html
(...)
<script data-main="scripts/main.js" src="scripts/require.js"></script>
(...)
main.js
requirejs.config({
baseUrl: 'scripts',
paths: {
angular: '../../bower_components/angular/angular',
jquery: 'jquery',
domReady: 'domReady',
'jquery.scrollTo': '../../bower_components/jquery.scrollTo/jquery.scrollTo',
},
shim: {
jquery: {deps: ['domReady']},
'jquery.scrollTo': {deps: ['jquery']},
angular: {deps: ['jquery.scrollTo'], exports: 'angular'}
},
packages: []
});
requirejs(['angular', 'domReady', 'jquery', 'app', 'scripts', 'jquery.scrollTo'], function(angular, domReady) {
'use strict';
domReady(function() {
angular.bootstrap(document, ['myApp']);
});
});
app.js
define('app', ['angular', 'domReady', 'jquery', 'jquery.scrollTo'], function (angular) {
return angular.module('myApp', []);
});
scripts.js
require(['domReady', 'jquery', 'jquery.scrollTo'], function(domReady, $) {
domReady(function() {
console.log('scripts.run');
});
});
I assumed that loading order should be following:
main
domReady
jquery
jquery.scrollTo
angular
scripts
app
But in real loading order is following:
The most strange for me is why scripts.js is loaded before jquery.js and jquery.scrollTo.js if require statement define that it is dependent on domReady, jquery and jquery.scrollTo?

I suggest to remove jquery completely from your code (if its possible for you) and requirejs. AngularJS has great dependency injection module which you could use to support timing of loaded parts.
Also - you could try to inline ng-app attribute inside html code insted of invoking angular via boostrap call, than you won't need "domReady".
If you can, please tell something more about your app so we can give you better response on availible solutions.

deitch's statement regarding loading order is correct. RequireJS will guarantee that the order in which the factory functions of your modules (factory == the callback you give to define) are executed will respect the dependencies specified. As far as the order in which modules are fetched the only thing you can be sure is that a module will be fetched before its factory function is executed but RequireJS otherwise has the freedom to fetch modules in any order.
In the case you are showing us, note how the modules that are first fetched after main are those which do not have a shim configuration defined for them. Modules with a shim configuration require a bit more processing from RequireJS and thus follow a different code path.
While we're at it, a few bizarre things in your code:
A path configuration that just repeats the name of the module does nothing. For instance, jquery: 'jquery'. You could remove such path specifications.
You have require at the top level of scripts.js. You should have define there instead.
Your call to define in app.js specifies a module name (1st parameter to define). Leave the module name out.
You give a jQuery a shim configuration. jQuery calls define. Giving a shim to a module that calls define is likely to result in undefined behavior. Don't do this.

Related

Apply a piece of config to several controllers

I'm using AngularJS and Django together, so I apply the recipe found here to reconfigure Angular's template delimiters:
angular.module("some module name").config(function($interpolateProvider) {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
I have to re-apply this piece to each module, only changing the "some module name" string. Is there a way to define this piece of configuration once, and then cleanly require and inject it into a module at the module's definition site?
Since $interpolateProvider is a provider, any change to it will affect not only the current module but also all the modules that depends on it.
You can, for example, define a "config" module and add it as dependency to other modules:
angular.module('config', []).config(function($interpolateProvider) {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
And on the other modules:
angular.module('module1', ['config']);
angular.module('module2', ['config']);
Demo plunker

dynamically adding script tags to angular app

ZI'm building an app where based on user preferences/configuration, I'll be loading different modules into the page.
To accomplish this, when the page loads, I run a script which builds a list of script-tags and adds all those tags to the body of the page. I also build a dependency injector list, and include the dependencies when the app is loaded.
I'm delaying app load using by putting
window.name = "NG_DEFER_BOOTSTRAP!";
and then after the script tags are added, I run
angular.element(document).ready(function () {
setTimeout(function(){
angular.resumeBootstrap();
},1000);
});
I have two problems.
First of all, for some reason, if I include angular.js in the configuration file, I get errors referring to angular not being found.
I can see that angular is being downloaded to the browser, but I'm guessing it's a timing issue, as angular takes longer to download than many of the other files.
I was going to just put angular in a regular script tag, but then I found that one of the modules I'm loading requires jQuery and jQuery UI, both to be loaded before angular, so that means more files loaded as scripts, and possibly not needed for that user.
I looked at using require.js and passing the config params to
requirejs.config.paths = //the custom built list from the users configuration file
But, from what I understand, that would also mean I'd need to wrap all of my modules in
requirejs(['jquery', 'angular', 'otherModule'],
function ($, angular, otherModule) {
// my module stuff here
});
Any other suggestions on how to accomplish this? Is there a better AMD than require.js?
I'd post all my code here, but I don't think it would help as the problem is coming from the browser downloading the files after the document is ready.
RequireJs is the best AMD implementation as far as I know.
If you want to specify your modules, you'd better to wrap your moduls in define calls:
define(['jquery', 'angular', 'otherModule'],
function ($, angular, otherModule) {
// DO NOT FORGET TO RETURN YOUR MODULE!
});
Also, why don't you start your applicaiton on domReady? You may use this requirejs plugin.
Here is a basic requirejs configuration for your case:
requirejs.config({
paths : {
jquery : 'libs/jquery',
jqueryUi : 'libs/jqueryui',
angular : 'libs/angular'
},
shim : {
angular : {exports : 'angular', deps : ['jquery']},
jqueryUI: {exports : '$', deps : ['jquery']}
},
map : {
'*' : {
domReady : 'require-plugins/domReady'
}
}
});
requirejs(['domReady!', 'angular'], function(document, angular) {
//Thank to domReady!, this callback executes on domReady events. And no need in jquery or angular:)
});

proper way to separate angular modules from AMD modules?

I am confused about the proper way to implement requireJS (and the r.js optimizer) into a very large AngularJS SPA. I would like some advice on how to organize the project so I get the benefit of AMD modularization, but not over complicate things (wrapping angular modules inside require js defined modules, which are all dumped into an optimized .js file...).
I have a few different categories of files I'm dealing with:
I have a bundle of vendor libraries (jquery, angular, angular-resource, require, lodash, etc)
I have some container level AMD modules (authentication, analytics, etc)
I have lots of angular modules, services, filters, etc
Using r.js to concat and minify all the vendor stuff together seems like a no-brainer, but when I started adding in all the other code, I felt like my project got muddy. For example, I have an Auth service, a couple controllers, and my vendor dependencies (jquery...). So my main.js that require looks at is sorta like this:
require.config({
paths: {
// load vendor libraries
'domReady': '../vendor/requirejs-domready/domReady',
'angular': '../vendor/angular/angular',
'jquery': '../vendor/jquery/jquery.min',
requireLib: '../vendor/requirejs/require'
... lots of these
//load app code
'app': '../app/app',
'AuthCtrl': '../app/modules/auth/AuthCtrl',
'UserCtrl': '../app/modules/auth/UserCtrl',
'SettingsCtrl': '../app/modules/auth/SettingsCtrl',
// potentially dozens and dozens of these...
//load auth library
'auth': '../app/modules/common/auth',
'analytics' '../analytics'
},
include: ['domReady', 'requireLib'],
shim: {
'angular': {
exports: 'angular'
}
}
...
});
Currently, I have been dumping all of the code from all 3 'categories' above into 1 big uglified js file.
What I'm not happy with is I feel like my controller is starting to need a laundry list of module dependencies:
require('app',[
'angular',
'jquery',
'AuthCtrl',
'UserCtrl',
'SettingsCtrl',
' ... potentially dozens? '
...
], function (angular, $, Auth, user, settings, ...potentially dozens?) {
'use strict';
return angular.module('ngAD', ['AuthCtrl', 'UserCtrl', 'SettingsCtrl' ...]);
});
So my question is two fold I guess:
What is the benefit of keeping all my angular modules, controllers, filters, etc, defined in their own AMD modules and included manually via requirejs? They are all in memory already (having been loaded into the big pile-o-files via r.js). Is there a smarter way to handle all these discreet angular modules when there will be tons of them? Should I concat them all into their own module that can be a single injectable dependency?
I'm getting mixed up between my "AMD modules" and "angular modules". Do all my angular components (controllers, directives, etc) need to be in their own AMD modules? I feel like I'm wrapping a module (angular) inside a module (require's AMD format).
There should be no need to do all of that.
I used this as a starting point
https://github.com/Matthew-Odette/angular-require-bootstrap-seed
Then all that is required is to add controllers and services to the require section at the end of the AMD file. e.g.
require(
[
// Dependencies from lib
'angular',
'ngRoute',
'../lib/less.min.1.5.0',
// Angular directives/controllers/services
'app',
'core/viewHomeController',
'core/commonRoutes',
'core/header',
'events/events-ctrl
],
function (angular) {
var AppRoot = angular.element(document.getElementById('ng-app'));
AppRoot.attr('ng-controller','AppCtrl');
angular.bootstrap(document, ['TheApp']);
});
events-ctrl.js being new controller/service that's been added, further controllers/services would be added the same way
Then the controllers/services needs to be wrapped in require code e.g. this is beginning of events-ctrl.js
define(['app'], function (app) {
app.factory('EventService', function($resource) {
return $resource('/api/events/:id');
});
app.controller('EventListCtrl', ['$scope', '$modal', 'EventService', function($scope, $modal, EventService) {
console.log('EventListCtrl working');
...

Dealing with require.js

I guess I do not completely understand the way require.js works. Here is a a simple module I've created:
requirejs.config({
paths: {
'underscore' : 'libs/underscore-min',
'backbone' : 'libs/backbone-min'
}
});
define([
"underscore",
"backbone"
], function(_, Backbone) {
console.log(_);
console.log(Backbone);
var MyCollection = Backbone.Collection.extend({
initialize: function() {
this.on("all", function(event) {
console.log(event);
});
}
});
return MyCollection;
});
I load it from my html:
<script data-main="js/mycollection.js" src="js/libs/require.min.js"></script>
The problem is it works intermittently. Sometimes Backbone object is there in the function when I need it, sometimes it doesn't (and gives me http://requirejs.org/docs/errors.html#notloaded error). If I just hit reload in the browser, I'd get 50/50 change of it working.
I must be missing something really basic here, the reported error doesn't make any sense to me, I thought the whole idea of the require.js mechanism is that my function will be called only when all the dependencies are loaded.
Since Underscore and Backbone haven't been defined as AMD modules, require.js does not know that Underscore is a dependency of Backbone. So I guess what happens in 50% of your cases is that Underscore isn't loaded when Backbone tries to use it.
You can use the require.js shim config http://requirejs.org/docs/api.html#config-shim to tell require.js about the dependency structure.
My guess is that you're not using an AMD version of underscore and Backbone. If that's the case and the two packages aren't wrapped as AMD modules - then the define function which is meant for modules won't work like it should.
For non-module js scripts, the more appropriate form would be to use the require() function.
OR - you can find the AMD version of underscore and Backbone here. AMD support was taken out of Underscore and Backbone at some point.
AMD Underscore
AMD Backbone

How can I keep templates out of /js/?

I'm using RequireJS 2 along with backbone and pals to build the website. The beginning of every view is usually something like this:
define([
'libs/jquery',
'libs/underscore',
'libs/backbone',
'text!templates/coletas/agendamento.html',
], function(...
My problem is that the last line expects to find the template at js/templates/coletas/agendamento.html, and so this poor programmer ends up having to put non-js files in /js/. Is there a way to avoid that and set the base url to ./templates/ everytime I use the text plugin?
I also cannot use absolute paths, because I know not where my code will end up in the server.
This snippet is from my main.js
require.config({
paths: {
loader: 'libs/backbone/loader',
jQuery: 'libs/jquery/jquery',
Underscore: 'libs/underscore/underscore',
Backbone: 'libs/backbone/backbone',
templates: '../templates'
}
});
Here is an example require.config that you can use to load in modules like my comment (sorry, I did not realize I had this in my app until I read your comment).
Once configured, then you can access various javascript libraries and your seperate templates directory like so (this snippet is from my js/views/overview/main.js:
define([
'jQuery',
'Underscore',
'Backbone',
'models/overview',
'text!templates/overview/main.html'
]
The directory structure I try to follow with requirejs is
webapp/
main.js
index.html
app.js
js/ <- your code here
libs/ <- 3rd party stuff here
templates/
styles/
etc/
...
so that the require.js -modules' dependencies are evaluated at the webapp - folder level, allowing you to maintain separation of different kinds of files.
Hope this helps!

Resources