how to manage big angular app - angularjs

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) { ... }
]);

Related

What is the simplest way to inject a function into an angularjs module?

I currently have 3 angularjs modules, each of which are (roughly) like so:
(function () {
var generalApp = angular.module('general-app', []);
generalApp.controller("NewsletterSignup.Controller", ["$scope", "$http", NewsletterSignupControllerFunction]);
}());
where NewsletterSignupControllerFunction is a global variable that is a reference to a function, eg:
var NewsletterSignupControllerFunction = function ($scope, $http) { ... };
Rather than use a global variable to share logic between the three modules, what is the simplest way to inject NewsletterSignupControllerFunction into each of the modules so I can use it to create the controllers? I have tried various approaches, none of which I can get to work.
One approach is to define a common module with the controller:
common.js
(function () {
angular.module('common', [])
.controller("NewsletterSignup.Controller",
["$scope", "$http", NewsletterSignupControllerFunction]
)
function NewsletterSignupControllerFunction ($scope,$http) {
//code ...
}
}());
Use that common module as a dependency:
angular.module("myApp",['common'])
For more information, see
AngularJS angular.module API Reference

How to separate Controller file in angularjs

I have looked at various examples of separating the files. I can understand them but the problem comes with the way my code is. I want separate these controllers in different files.
'use strict';
angular.module('myModule', ['ui.bootstrap']);
var myApp = angular.module('myApp', []);
myApp.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}]);
myApp.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.when('/getplaces', {
templateUrl: 'templates/getplaces.html',
controller: 'ListCtrl'
})
The below controller needs to be in a different file.
///////////// MONTHLY DATA /////////////////////////////////////
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/getmonth', {
templateUrl: 'templates/getmacs.html',
controller: 'MonthlyCtrl'
})
}])
.controller('MonthlyCtrl', function ($scope, $http) { $scope.visible = true;
})
I have more than 20 controllers like above. but how do I separate them.
Here is how you should do it,
first declaration
angular.module('appName', ['Module1', 'Module2', 'Service1']);
subsequent declarations
here all the controllers and service can be in separate files.
angular
.module('Module1', [])
.controller('AbcController', ['$scope', '$timeout', 'Service1', function ($scope, $timeout, service1) {} ]);
angular
.module('Module2', [])
.controller('EfgController', ['$scope', '$timeout', 'Service1', function ($scope, $timeout, service1) {} ]);
angular.module('Service1', [])
.service('XYZService', ['$http', function LmnoService($http) {
} ]);
This should easily be done, I would organize my application route configurations into the main app.js file.
myApp.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.when('/getplaces', {
templateUrl: 'templates/getplaces.html',
controller: 'ListCtrl'
}).when('/getmonth', {
templateUrl: 'templates/getmacs.html',
controller: 'MonthlyCtrl'
})
}])
Then when you create a separate controller in each file just reference the application name as such:
myApp.controller('MonthlyCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.visible = true;
}])
You will also notice I am using the array initializer way, this will save you some hastles when you are doing minification.
You can follow this convention:
First load all the library dependencies like angular, angular-routes etc
then your config holder js file which contains your module declaration.
then all other files with controller methods.
I would map specific modules to functionality (and not by layers), each one containing its concerned controllers, services and appropriate directives.
You would have one module called 'places.list' for instance, containing all controllers, services/factories and directives associated to it.
The rule is: one module, one file, otherwise you would be forced to declare those files in order... (first modules declaration..then controllers etc.. ugly)
If you split your modules in the right way, you will notice that each one is easy to maintain and doesn't contain in general a huge amount of code.
Even each route declaration (.config) would be split across those modules.
=> All the route concerning places would be declared inside the module places.list.
Indeed, it would be ugly (and difficult to maintain) to declare the whole navigation rules in your main module..
Thus, each module would be easily testable by loading only specific module's dependencies that are relevant for the test.

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: Separating Utility Methods into a Reusable Service

I'm trying to define a service with methods that I can call from a module in AngularJS.
This is my first attempt at using AngularJS.
Maybe this isn't a good way to do it, but I thought that I could put some helper methods into a service that could be then reusable.
But I'm finding that the UrlHelperService isn't available from the module.
I've tried to simplify and extract just the skeleton of the three files being used:
app.js
angular.module('myApp', ['ngRoute', 'myMod', 'UrlHelperService']);
urls.js
var serv = angular.module('UrlHelperService', []);
serv.service( "UrlHelperService",
function aaMethod( ) {
this.urlHelp = function( url, aa ) {
return url;
}
}
);
module.js
var module = angular.module( "myMod", [] );
module.factory(
"myMod",
[ "$http", "$q", "UrlHelperService",
function myModFactory( $http, $q, UrlHelperService) {
// UrlHelperService isn't available here. Why?
return;
}]);
Well, if you want to use UrlHelperService with myMod you need to declare it as a dependency in myMod (as you do in myApp).
var module = angular.module( "myMod", [
"UrlHelperService"
]);
module.factory(
"myMod", [
"$http",
"$q",
"UrlHelperService",
function myModFactory( $http, $q, UrlHelperService) {
// UrlHelperService isn't available here. Why?
return;
}
]);
But it could possibly be even simpler. You are creating a myMod module and a myMod service (.factory is similar to .service), why?
Anyway, you're absolutely right to putting these helper function in a service you can reuse. It doesn't have top be on its own module though. Perhaps a simpler approach would be good enough. For example:
angular
.module('myApp', [
'ngRoute'
])
.factory('UrlHelperService', [
function UrlHelperService() {
return {
urlHelp: function(url, aa) {
return url;
}
};
}
]);
Here you have your app (its own module) and a service available to everyone else inside myApp. Sometime a separate module for some services etc is better, sometimes a service inside your main module is good enough.

Angular add modules after angular.bootstrap

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".

Resources