Angular add modules after angular.bootstrap - angularjs

I'm using meteor + angular. My intention is to add more dependencies after the app bootstrap (This is because the package is the one handling the bootstrapping at the start and I don't have much control of it). Now while doing that, I would also want to enforce a basic code structure wherein for example, I would compile all controllers in one module.
Here's the basic idea:
'use strict';
angular.module('app.controllers', [])
.controller('MainCtrl', function() {
// ...
})
.controller('SubCtrl', function() {
// ...
})
.controller('AnotherCtrl', function() {
// ...
});
Then include that to the main module as dependency:
angular.module('app', [
'app.filters',
'app.services',
'app.directives',
'app.controllers' // Here
]);
After some research, I've discovered that what I'm trying to do (Adding dependencies after bootstrap) is actually a part of a feature request to the angular team. So my option is using, for example, $controllerProvider and register() function:
Meteor.config(function($controllerProvider) {
$controllerProvider.register('MainCtrl', function($scope) {
// ...
});
});
Meteor.config(function($controllerProvider) {
$controllerProvider.register('SubCtrl', function($scope) {
// ...
});
});
Meteor.config(function($controllerProvider) {
$controllerProvider.register('AnotherCtrl', function($scope) {
// ...
});
});
It's works though not that elegant. The questions are:
What's a more elegant way of doing the config and register part?
Is there a way to register a module instead?

Create your module:
angular.module('app.controllers', []);
Add it as a dependency:
angular.module('app').requires.push('app.controllers');

according to this presentation (slide 12) you can assign controllerProvider to app.
Example of replacing module's controller method: http://jsfiddle.net/arzo/HB7LU/6659/
var myApp = angular.module('myApp', []);
//note overriding controller method might be a little controversial :D
myApp.config(function allowRegisteringControllersInRuntime($controllerProvider) {
var backup = myApp.controller;
myApp.controller = $controllerProvider.register;
myApp.controller.legacy = backup;
})
myApp.run(function ($rootScope, $compile) {
myApp.controller('MyCtrl', function($scope) {
$scope.name = 'Superhero';
})
var elem;
var scope=$rootScope;
elem = $compile('<p ng-controller="MyCtrl">{{name}}</br><input ng-model="name" /></p>')($rootScope, function (clonedElement, scope) {
console.log('newly created element', clonedElement[0])
document.body.appendChild(clonedElement[0]);
});
console.log('You can access original register via', myApp.controller.legacy);
})

The only method that I've seen that works is replacing the angular.module function with your own function returning the module you used to bootstrap your app.
var myApp = angular.module('myApp', []);
angular.module = function() {
return myApp;
}
So that all modules that are registered afterwards are actually being registered in your myApp module.
This method combined with the one you describe in the question (using providers like $controllerProvider) will allow you to add "modules" after angular.bootstrap.
Demo
See this jsfiddle for a demo: https://jsfiddle.net/josketres/aw3L38r4/
Drawbacks
The config blocks of the modules that are added after angular.bootstrap will not be called. Maybe there's a way to fix this, but I haven't found it.
Overriding angular.module feels like a "dirty hack".

Related

Advantage defining a Angular Module / Service under a different name then the main application

Where is the advantage in defining an angular module as such:
var app = angular.module('app', ['appServices']);
var appServices = angular.module('appServices', []);
appServices.factory('someService', function ($http) {
return { member:true }
});
... as opposed to:
var app = angular.module('app', []);
app.factory('someService', function ($http) {
return { member:true }
});
I cannot see a direct advantage, given that my single page app is named 'app'. Keep in mind I am new to Angular.
You can manage dependencies in modules. You code will be better organized and everyone can tell you what modules depend on other modules.
Thanks

Angularjs - Uknown Provider

Whenever I do this:
app.controller('hangmanController', ['$scope', 'wordnickAPIService', function ($scope, wordnickAPIService) {
I get this:
[$injector:unpr] Unknown provider: wordnickAPIServiceProvider <- wordnickAPIService
I read through This discussion on the topic, but didn't see an answer that applied. I am sure it is something simple or trivial that I am missing, but, jeez, if Angular isn't giving me fits trying to piece it all together.
Relevant HTML:
<body ng-app="angularHangmanApp" ng-controller="hangmanController">
My controller:
'use strict';
var app = angular.module('angularHangmanApp', []);
app.controller('hangmanController', ['$scope', 'wordnickAPIService', function ($scope, wordnickAPIService) {
[...]variable declarations[...]
var wordListURL = 'http://long_url_that_returns_some_json';
$scope.wordList = wordnickAPIService.get(wordListURL);
}]);
My factory:
'use strict';
var app = angular.module('angularHangmanApp', []);
app.factory('wordnickAPIService', ['$http', function($http) {
return {
get: function(url) {
return $http.get(url);
},
post: function(url) {
return $http.post(url);
},
};
}]);
The problem is that you are creating multiple modules with the same name.
To create a module in angular you use:
var app = angular.module('angularHangmanApp', []);
Then to get That module somewhere else you just type:
var app = angular.module('angularHangmanApp');
No extra []...
Also make sure you declare the service before trying to call it.
In your factory and your controller, you are actually redefining the app module.
Instead of saying
var app = angular.module('angularHangmanApp', []);
say
var app = angular.module('angularHangmanApp');
Use the first style of invocation only once in your application (maybe just app.js). All other references should use the second style invocation, otherwise, you're constantly redefining the angular app and losing all of the controllers, factories, directives declared previously.

angularJS using TypeScript the difference between the following methods

Could please explain the difference between two methods to load your controllers,service.... etc
var appModule = angular.module("myApp", []);
appModule.controller("MyController", ["$scope", ($scope)
=> new Application.Controllers.MyController($scope)]);
module todos {
'use strict';
var todomvc = angular.module('todomvc', [])
.controller('todoCtrl', TodoCtrl)
.directive('todoBlur', todoBlur)
.directive('todoFocus', todoFocus)
.service('todoStorage', TodoStorage);
}
The first method does dependency injection inline. The second method depends on $inject/constructor argument being setup properly in the controller.
Suggestion : http://www.youtube.com/watch?v=WdtVn_8K17E&hd=1

how to manage big angular app

I'm having trouble managing my app. I would like to separate my controllers on several files. I read Brian's Ford blog ( http://briantford.com/blog/huuuuuge-angular-apps.html ) but I cannot quite understand how should I do it.
On my controller.js file I had something like this :
function loginCtrl($scope){
....
}
function landingCtrl($scope){
...
}
And the only way I found to separate controller per file is to do this:
app.js:
var musicApp = angular.module('musicApp', []);
controller1.js:
musicApp.controller('loginController', ['$scope', loginCtrl],function loginCtrl($scope){
....
});
controller2.js:
musicApp.controller('landingCtrl', ['$scope', landingCtrl],function landingCtrl($scope){
....
});
Is there a better way to do this?
I'm using a similar way as below:
Main.js
angular.module('app', []);
FooCtrl.js
angular.module("app").controller("FooCtrl", [
"$scope", function($scope) {
}
]);
Another way adopted by google is:
# init empty Object
var docsApp = {
controller: {},
directive: {},
serviceFactory: {}
};
# FooCtrl.js
docsApp.controller.FooCtrl = ['$scope', function($scope){}]
# BarCtrl.js
docsApp.controller.BarCtrl = ['$scope', function($scope){}]
... add services
... directives
# bootstrap angular
angular.module('docsApp', ['...']).
config(...).
factory(docsApp.serviceFactory).
directive(docsApp.directive).
controller(docsApp.controller);
Take a look at this js from google angularjs tutorial: http://docs.angularjs.org/js/docs.js
You can achieve this in multiple ways.
one file for all your controllers using "."
musicApp.controller('loginController', ['$scope', loginCtrl,function loginCtrl($scope){
....
}]).controller('landingCtrl', ['$scope', landingCtrl,function landingCtrl($scope){
....
}]);
just remember to include the function inside the injected paramenters array.
or one controller for each file.
just need to include every js file with whatever method you are using (script tag, requirejs, etc.)
I have something similar:
main_router.js:
var myApp = angular.module('theApp', ['controllers'])...
var controllers = angular.module('controllers', []);
some_controller.js:
controllers.controller('SomeCtrl', ['$scope',
function ($scope) { ... }
]);

AngularJS - Error: Unknown provider: searchResultsProvider

EDIT: I have managed to get my unit tests running - I moved the code containing the services to a different file and a different module, made this new module a requirement for fooBar module, and then before each "it" block is called, introduced the code beforeEach(module(<new_service_module_name)). However, my application still won't run. No errors in console either. This is the only issue that remains - that when I use global scope for controllers definition, the application works, but when I use angular.module.controller - it does not.
I have a file app.js that contains the following:
'use strict';
var app = angular.module('fooBar', []);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/', {
templateUrl: 'partials/form-view.html',
controller: FormViewCtrl
}).
when('/resultDisplay', {
templateUrl: 'partials/table-view.html',
controller: TableViewCtrl
}).
otherwise({redirectTo: '/'});
}]);
app.service('searchResults', function() {
var results = {};
return {
getResults: function() {
return results;
},
setResults: function(resultData) {
results = resultData;
}
};
});
I have another file controllers.js that contains the following:
'use strict';
var app = angular.module('fooBar', []);
app.controller('FormViewCtrl', ['$scope', '$location', '$http', 'searchResults',
function ($scope, $location, $http, searchResults) {
//Controller code
}]);
searchResults is a service that I created that simply has getter and setter methods. The controller above uses the setter method, hence the service is injected into it.
As a result, my application just does not run! If I change the controller code to be global like this:
function ($scope, $location, $http, searchResults) {
//Controller code
}
then the application works!
Also, if I use the global scope, then the following unit test case works:
'use strict';
/*jasmine specs for controllers go here*/
describe('Foo Bar', function() {
describe('FormViewCtrl', function() {
var scope, ctrl;
beforeEach(module('fooBar'));
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('FormViewCtrl', {$scope: scope});
}));
}
//"it" blocks
}
If I revert to the module scope, I get the error -
Error: Unknown provider: searchResultsProvider <- searchResults
Thus, by using global scope my application and unit tests run but by using app.controller, they seem to break.
Another point that I have noted is that if I include the controller code in app.js instead of controllers.js, then the application and unit tests start working again. But I cannot include them in the same file - how do I get this to run in the angular scope without breaking the application and unit tests?
You don't need to go that route. You can use the modular approach, but the issue is with your second parameter.
In your app.js you have this:
var app = angular.module('fooBar', []);
Then in your controller, you have this:
var app = angular.module('fooBar', []);
What you're doing there is defining the module twice. If you're simply trying to attach to the app module, you cannot pass in the second parameter (the empty array: []), as this creates a brand new module, overwriting the first.
Here is how I do it (based on this article for architecting large AngularJS apps.
app.js:
angular.module('fooBar',['fooBar.controllers', 'fooBar.services']);
angular.module('fooBar.controllers',[]);
angular.module('fooBar.services', []);
...etc
controllers.js
angular.module('foobar.controllers') // notice the lack of second parameter
.controller('FormViewCtrl', function($scope) {
//controller stuffs
});
Or, for very large projects, the recommendation is NOT to group your top-level modules by type (directives, filters, services, controllers), but instead by features (including all of your partials... the reason for this is total modularity - you can create a new module, with the same name, new partials & code, drop it in to your project as a replacement, and it will simiply work), e.g.
app.js
angular.module('fooBar',['fooBar.formView', 'fooBar.otherView']);
angular.module('fooBar.formView',[]);
angular.module('fooBar.otherView', []);
...etc
and then in a formView folder hanging off web root, you THEN separate out your files based on type, such as:
formView.directives
formView.controllers
formView.services
formView.filters
And then, in each of those files, you open with:
angular.module('formView')
.controller('formViewCtrl', function($scope) {
angular.module('formView')
.factory('Service', function() {
etc etc
HTH
Ok - I finally figured it out. Basically, if you wish to use the module scope and not the global scope, then we need to do the following (if you have a setup like app.js and controllers.js):
In app.js, define the module scope:
var myApp = angular.module(<module_name>, [<dependencies>]);
In controllers.js, do not define myApp again - instead, use it directly like:
myApp.controller(..);
That did the trick - my application and unit tests are now working correctly!
It is best practice to have only one global variable, your app and attach all the needed module functionality to that so your app is initiated with
var app = angular.module('app',[ /* Dependencies */ ]);
in your controller.js you have initiated it again into a new variable, losing all the services and config you had attached to it before, only initiate your app variable once, doing it again is making you lose the service you attached to it
and then to add a service (Factory version)
app.factory('NewLogic',[ /* Dependencies */ , function( /* Dependencies */ ) {
return {
function1: function(){
/* function1 code */
}
}
}]);
for a controller
app.controller('NewController',[ '$scope' /* Dependencies */ , function( $scope /* Dependencies */ ) {
$scope.function1 = function(){
/* function1 code */
};
}
}]);
and for directives and config is similar too where you create your one app module and attach all the needed controllers, directives and services to it but all contained within the parent app module variable.
I have read time and time again that for javascript it is best practice to only ever have one global variable so angularjs architecture really fills that requirement nicely,
Oh and the array wrapper for dependencies is not actually needed but will create a mess of global variables and break app completely if you want to minify your JS so good idea to always stick to the best practice and not do work arounds to get thing to work
In my case, I've defined a new provider, say, xyz
angular.module('test')
.provider('xyz', function () {
....
});
When you were to config the above provider, you've inject it with 'Provider' string appended.
Ex:
angular.module('App', ['test'])
.config(function (xyzProvider) {
// do something with xyzProvider....
});
If you inject the above provider without the 'Provider' string, you'll get the similar error in OP.

Resources