Dynamically loading controller with angularjs and requirejs - angularjs

I am trying to integrate angularjs app with requirejs. I want to preload templates and controllers on-demand. In my example:
http://plnkr.co/edit/vIps7t92OFzA5RXoTjvI?p=preview
I init controller SocialController inside the app.js and I need to load dynamically StreamController inside the SocialController. Unfortunately I am getting an exception, see browser console.
Argument 'StreamController' is not a function, got undefined
If I remove from SocialController
angular.module('sampleTest').controller('StreamController', StreamController);
and add it to app.js, it works but in this case requirejs will preload it right at the beginning and not when I need, inside the SocialController on demand.

Here is an answer. Looks like angular does not allow instantiating any new services or controllers later on, after the app startup. So the code below I had to add to app.js files at the bottom, after all the controller and services are init. This is a hack but fixes the problem for lazy loading with requirejs.
See my link to plunkr with the fix. Now there are no exception and StreamController is lazy loaded and init after the startup.
sampleApp.config(
function (
$controllerProvider,
$compileProvider,
$filterProvider,
$provide
) {
sampleApp.controller = $controllerProvider.register;
sampleApp.directive = $compileProvider.directive;
sampleApp.filter = $filterProvider.register;
sampleApp.factory = $provide.factory;
sampleApp.service = $provide.service;
}
);

Related

how do i add angular-moment filter here?

Am new to angularjs and given a codebase to add angular-moment (moments.js) to a module.
The module is defined is as follows:
angular.module("xyz", ["ngRoute",
"ui.bootstrap",
"infinite-scroll",
"uiGmapgoogle-maps",
"googlechart"],
function($interpolateProvider) {
$interpolateProvider.startSymbol("[[");
$interpolateProvider.endSymbol("]]");
})
.factory("principal", principal)
.factory("authorization", authorization)
.factory("pingService", abc)
When i add angularMoment just after "googlechart" i get a "unpr" angular error. I did include both moments.js and angular-moments.js in my html.
I need to use javascript moments lib in my angular code.
Please help.
thanks
You haven't included 'angularMoment' in your module as dependency.
Do like following :
angular.module("xyz", ["ngRoute",
"ui.bootstrap",
"infinite-scroll",
"uiGmapgoogle-maps",
"googlechart","angularMoment"],
function($interpolateProvider) {
$interpolateProvider.startSymbol("[[");
$interpolateProvider.endSymbol("]]");
})
.factory("principal", principal)
.factory("authorization", authorization)
.factory("pingService", abc)
And make sure , you have included the angular-moment.js file in your index.html
I hope this helps. Do let me know in case of any query
You don't need to add any directive to use momentjs in your project, reference it just like any other script in your SPA main page an use it on any controler.
The only benefit of loading it via a directive is having it declared as a dependence, and the possibility of mocking it for tests, if you don't care about this, just use it as you would do without Angular.

Route Provider loading order in angular js

I am using Require JS for dynamic loading controllers.
All my controllers and packages , html files are loading after app.js. But " We cannot add controllers, services, directives, etc after an application bootstraps."
My Problem is , I have added route porvider in app.js
But the controllers and html are loading after app.js . SO I am getting controller is undefined issue. Please help me in this
This could be a simple dependancy injection issue.
In my case I've got an app, which has a dashboard module. The dashboard module parents presentation modules. Each module has services, directives, controllers etc. You must properly inject them at the right place.
//in app.js
var app = angular.module('App',[
'App.constants',
'App.modules.preloader',
'App.modules.user',
'App.modules.dashboard'
]);
//in dashboard.module.js
var dashboardModule = angular.module('App.modules.dashboard',[
'App.modules.dashboard.controllers',
'App.modules.dashboard.services',
'App.modules.dashboard.presentations.mileage',
'App.modules.dashboard.presentations.speed',
'App.modules.dashboard.presentations.summary'
]);
//in speed.module.js
var speedModule = angular.module('App.modules.dashboard.presentations.speed',[
'App.modules.dashboard.presentations.speed.controllers',
'App.modules.dashboard.presentations.speed.services'
]);
Hope this helps!
If not, maybe post a bit more details about the issue. Some sample code and maybe a fiddle.

registerModule with dependencies

I'm building an app with MEAN.JS and I'm trying to use a controller from another module. I've found that I can do this with dependency injection:
angular.module(‘MyModule’, [‘Dependency’]);
But the way modules are created in MEAN.JS is:
ApplicationConfiguration.registerModule('MyModule');
And I can't just pass a second parameter to registerModule. So, how should I do this? Do I have to use both methods? Am I doing it wrong?
Example
I want to add a new model: Campaign. Campaigns are created by admins only, but can be seen by the "campaign owner" (another User). The create campaign form should list available Users, so the admin can select the one that's going to be the "owner" of that Campaign.
The problem is, the create campaign form is controlled by CampaignsController, how can I list Users? I've used another controller (UsersController) and thats the problem, it is undefined because we are in the Campaign module.
EDIT:
The problem was grunt autorestarting the app incorrectly:
I moved the controller from one module (folder) to another, but grunt was still trying to load it from the old path, and thus failing: Controller not found. I thought the problem was dependency injection, but you only have to close grunt (Ctrl+C) and run it again. Problem solved.
Anyways, thanks for the answer #gilBirman cause it is correct: there is no need to inject anything with MEANJS.
MEAN.js makes all the modules registered via registerModule available to all other modules in your app by adding it as a dependency to the main app called mean. Here's the part of the MEAN.js source code that does this:
var applicationModuleName = 'mean';
....
// Add a new vertical module
var registerModule = function(moduleName) {
// Create angular module
angular.module(moduleName, []);
// Add the module to the AngularJS configuration file
angular.module(applicationModuleName).requires.push(moduleName);
};
So you're on the right track, however it sounds like you are trying to inject a controller. However, in angular, controllers are not injectable. You can inject services, factories, values, and constants.
First create your own module for example:
angular.module('app.controllers', []) - angular module with controllers.
then add controller to that module:
angular.module('app.controllers', [])
.controller('dashboard.admin.account.controller', ['$scope', ($scope) { .... }]);
then create global module which will bind to your markup:
angular.module('app', [
'app.controllers'
'ui.router',
'ngAnimate'
]);
then bootstrap your global module to markup:
domReady(function () {
angular.bootstrap(document, ['app']);
});
Now you can use your controller.

re-open and add dependencies to an already bootstrapped application

Is there a way to inject a late dependency to an already bootstrapped angular module? Here's what I mean:
Say that I have a site-wide angular app, defined as:
// in app.js
var App = angular.module("App", []);
And in every page:
<html ng-app="App">
Later on, I'm reopening the app to add logic based on the needs of the current page:
// in reports.js
var App = angular.module("App")
App.controller("ReportsController", ['$scope', function($scope) {
// .. reports controller code
}])
Now, say that one of those on-demand bits of logic also requires their own dependencies (like ngTouch, ngAnimate, ngResource, etc). How can I attach them to the base App? This doesn't seem to work:
// in reports.js
var App = angular.module("App", ['ui.event', 'ngResource']); // <-- raise error when App was already bootstrapped
I realize I can do everything in advance, i.e -
// in app.js
var App = angular.module("App", ['ui.event', 'ngResource', 'ngAnimate', ...]);
Or define every module on its own and then inject everything into the main app (see here for more):
// in reports.js
angular.module("Reports", ['ui.event', 'ngResource'])
.controller("ReportsController", ['$scope', function($scope) {
// .. reports controller code
}])
// in home.js
angular.module("Home", ['ngAnimate'])
.controller("HomeController", ['$scope', '$http', function($scope, $http){
// ...
}])
// in app.js, loaded last into the page (different for every page that varies in dependencies)
var App = angular.module("App", ['Reports', 'Home'])
But this will require I initialize the App everytime with the current page's dependencies.
I prefer to include the basic app.js in every page and simply introduce the required extensions to each page (reports.js, home.js, etc), without having to revise the bootstrapping logic everytime I add or remove something.
Is there a way to introduce dependencies when the App is already bootstrapped? What is considered the idiomatic way (or ways) to do this? I'm leaning towards the latter solution, but wanted to see if the way I described could also be done. thanks.
I solved it like this:
reference the app again:
var app = angular.module('app');
then push your new requirements to the requirements array:
app.requires.push('newDependency');
Simple...
Get an instance of the module using the getter like this:
var app = angular.module("App");
Then add to the "requires" collection like this:
app.requires[app.requires.length] = "ngResource";
Anyway, this worked for me. GOOD LUCK!
According to this proposal on the Angular JS google group this functionality does not exist as of this moment. Hopefully the core team decides to add this functionality, could use it myself.
If you wish to add multiple dependencies at once, you can pass them in push as follows:
<script>
var app = angular.module('appName');
app.requires.push('dependencyCtrl1', 'dependencyService1');
</script>
I realize that this is an old question, however, no working answer has yet been provided, so I decided to share how I solved it.
The solution requires forking Angular, so you can't use CDN anymore. However the modification is very small, so I am surprised why this feature doesn't exist in Angular.
I followed the link to google groups that was provided in one of the other answers to this question. There I found the following code, which solved the issue:
instanceInjector.loadNewModules = function (mods) {
forEach(loadModules(mods), function(fn) { instanceInjector.invoke(fn || noop); });
};
When I added this code to line 4414 in the angular 1.5.0 source code (inside the createInjector function, before the return instanceInjector; statement), it enabled me to add dependencies after bootstrapping like this $injector.loadNewModules(['ngCookies']);.
Since version 1.6.7 it is now possible to lazy load modules after the app has been bootstrapped using $injector.loadNewModules([modules]). Below is an example taken from AngularJS documentation:
app.factory('loadModule', function($injector) {
return function loadModule(moduleName, bundleUrl) {
return getScript(bundleUrl).then(function() { $injector.loadNewModules([moduleName]); });
};
})
Please read full documentation about loadNewModules as there are some gotchas around it.
There's also a very good sample app by omkadiri using it with ui-router.

How to update a route from inside an AngularJS controller?

I have an AngularJS application into which I want to load some plugins that are discovered by a controller when it starts up. In order for the plug in to work I have add some routes to the $routeProvider from the controller, but there seems to be no way to do this.
Right now I'm using a pretty ugly hack as below:
var routeProvider;
angular.module('example', [], function($routeProvider) {
routeProvider = $routeProvider;
// Set up other routes
}
function Controller($http, $location, $timeout) {
// Use $http to find some plugin
routeProvider.when(plugin.url, plugin.options);
// Ugly hack so that the plugin appears if $location.path() already points to it
var path = $location.path();
$location.path("/");
$timeout(function() { $location.path(path); }, 10);
}
If I don't do the nonsense with $timeout then if I start (load the page) at the route for the plugin it won't load ($route.current remains blank). With the jump between paths the route gets resolved properly and the plugin view loads as it should.
Is there a better way of doing this?
You could tear $routeProvider from the source and make your own version? :-)
$routeProvider is just a provider made for you. https://github.com/angular/angular.js/blob/master/src/ng/route.js
The way that we did this in the end was to have all of the routes downloaded from services before we start up AngularJS and then use that data structure to set up the routes, then bootstrap Angular once we had the information and everything was set up properly.
The only downside of this is the delay in start up, especially if there are multiple service plug ins that you need to handle.

Resources