What reasons are there to have multiple controllers in an AngularJS application? I've built a few angular apps now and I've never encountered a problem where I thought multiple controllers would make things easier for me.
I'm still a bit of a n00b, have never written a unit test and my code isn't as manageable as it could be so I'm sure it's just ignorance. And I've heard that other people have multiple controllers.
Put another way: how does one know that they should create a new controller?
From what I've seen of it an Angular application should have separate controllers for separate scopes. For instance, almost all applications have user data. You'll want to have this data attached to a user model, inside a user controller:
function UserCtrl ($scope) {
$scope.user = {
name: "bmorrow",
lastLogin: "4/16/2013"
};
}
And the template (our view) will be inside a specific portion of the applications structure. The right side of a navigation bar for example, or on a user details page. We establish where this portion is by assigning it a controller using ng-controller. This creates the scope of said controller and binds the corresponding models to it. The model (our data) is connected to the view (the HTML) via the controller.
Suppose the application has a page for the user's written articles. We can create another controller restricted only to the section of HTML that specifically holds the article content.
function ArticleCtrl ($scope) {
$scope.article = {
title: "Hello World",
body: "Lorem ipsum...."
};
}
In the trivial example above, combining both of the controllers won't do any harm. But once your application begins to grow, logically organizing your controllers/views according to the data it represents will make your code cleaner and easier to understand. Less unneeded complexity will make everything much easier on you. And using one controller for everything is an unneeded complexity.
You can see this illustrated in Basarat's answer as well. You don't necessarily need to use one controller per route, but doing so helps to logically structure the application.
So, to answer your question, you should usually have one controller per category of data. Users, Articles, Fruits, Vegetables, Transactions, and so on.
Read about Angular Controllers and the Model-View-Controller pattern for more information if you haven't already. I hope this helps.
You definitely start to need more controllers when you start to split you application into multiple views.
e.g. When you start to use routes (also called deep linking) you have a template url as well as a controller to go with that template (check out http://docs.angularjs.org/tutorial/step_07) e.g.
angular.module('phonecat', []).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/phones', {templateUrl: 'partials/phone-list.html', controller: PhoneListCtrl}).
when('/phones/:phoneId', {templateUrl: 'partials/phone-detail.html', controller: PhoneDetailCtrl}).
otherwise({redirectTo: '/phones'});
}]);
I like to think as controllers as "widgets." One one page of my backend, they can open up the ViewUsers controller (widget), which can open up more UserDetail controllers.
I guess if you're used to OOP, it feels pretty natural to want to keep their scopes separate and encapsulated.
Related
I need create one huge page on AngularJS. And I'm thinking which architecture is better for this page? So, page has 3 blocks with a lot functionality, but the main is: left block has accounts, user can pick some account and this account should be shown on center block. Center block can be changed and result should be shown on the right block.
For html I created 3 views for each block and included them with ng-include. Also I want somehow divide controller for few files, because I didn't work with a lot of code in one file. And I see a few ways, how to do this.
1. Create controller for each view, and transfer data by broadcast, or save data on RootScope and use watchers.
2. Create parent controller and transfer data by him.
What do you think about this? Or maybe one big controller is the best solution for this? And what is the best way for transferring data through controllers? Thanks.
There are many approaches for your situation, I'll tell you what I think is best
First of all, you make an parent abstract state where you can do all your dirty work in it, wither it was getting data from server or otherwise, inject all the data you need to use in $scope and then go to the child state "where your big page is"
something like that :
.state('parent_state', {
url: '/home',
abstract: true,
templateUrl: 'whatever',
controller: 'yourCtrl as yourCtrl'
})
.state("parent_state.child",{
//Whatever you need here
})
when you go to your child state you will have all the data you need in $scope , so you can focus more on logic instead of wrapping data
from my opinion, a page should have only one controller, there is no point if giving a controller to every portion of your view, else you need to go with custom directives
So if you have one controller that rules them all, or one controller and multiple directives.
your call
Does it make sense to use angular-services when we use ES6 modules? For example we need a singleton module (userService) in our code and we can do like this:
var app = angular.module('app', []);
app.service('userService', function(){
this.users = ['John', 'James', 'Jake'];
});
app.controller('FooController', ['$scope', 'userService', function($scope, userService){
console.log(userService);
}]);
But we can define the service in separate file:
/* ./user-service.js */
export default users = ['John', 'James', 'Jake'];
, then make the code like this:
var app = angular.module('app', []);
var userService = require('./user-service')
app.controller('FooController', ['$scope', function($scope){
console.log(userService);
}]);
and result will be absolutely the same as with services using. So why use angular services when we can use modules?
Yes! It makes perfect sense.
Services implement a particular responsibility in your application, moving data between the data store and views.
Modules allow you to organize your code and separate sections with different responsibilities.
By putting each service into a module, you make it easier to browse and test your code. It's easy to find all of the code that implements a responsibility.
Source: Difference between service, directive and module =)
From my own personal notes (mostly snippets from the docs, google group posts, and SO posts):
Modules
provide a way to namespace/group services, directives, filters, configuration information and initialization code
help avoid global variables
are used to configure the $injector, allowing the things defined by the module (or the whole module itself) to be injected elsewhere (Dependency Injection stuff)
Angular modules are not related to CommonJS or Require.js. As opposed to AMD or Require.js modules, Angular modules don't try to solve the problem of script load ordering or lazy script fetching. These goals are orthogonal and both module systems can live side by side and fulfill their goals (so the docs claim).
Services
are singletons, so there is only one instance of each service you define. As singletons, they are not affected by scopes, and hence can be accessed by (shared with) multiple views/controllers/directives/other services
You can (and probably should) create custom services when
two or more things need access to the same data (don't use root scope) or you just want to neatly encapsulate your data
you want to encapsulate interactions with, say, a web server (extend $resource or $http in your service)
Built-in services start with '$'.
To use a service, dependency injection is required on the dependent (e.g., on the controller, or another service, or a directive).
Directives (some of the items below say essentially the same thing, but I've found that sometimes a slightly different wording helps a lot)
are responsible for updating the DOM when the state of the model changes
extend HTML vocabulary = teach HTML new tricks. Angular comes with a built in set of directives (e.g., ng-* stuff) which are useful for building web applications but you can add your own such that HTML can be turned into a declarative Domain Specific Language (DSL). E.g., the <tabs> and <pane> elements on the Angular home page demo "Creating Components".
Non-obvious built-in directives (because they don't start with "ng"): a, form, input, script, select, textarea. Under Angular, these all do more than normal!
Directives allow you to "componentize HTML". Directives are often better than ng-include. E.g., when you start writing lots of HTML with mainly data-binding, refactor that HTML into (reusable) directives.
The Angular compiler allows you to attach behavior to any HTML element or attribute and even create new HTML elements or attributes with custom behavior. Angular calls these behavior extensions directives.
When you boil it all down, a directive is just a function which executes when the Angular compiler encounters it in the DOM.
A directive is a behavior or DOM transformation which is triggered by a presence of an attribute, an element name, a class name, or a name in a comment. Directive is a behavior which should be triggered when specific HTML constructs are encountered in the (HTML) compilation process. The directives can be placed in element names, attributes, class names, as well as comments.
Most directives are restricted to attribute only. E.g., DoubleClick only uses custom attribute directives.
see also What is an angularjs directive?
Define and group Angular things (dependency injection stuff) in modules.
Share data and wrap web server interaction in services.
Extend HTML and do DOM manipulation in directives.
And make Controllers as "thin" as possible.
I've created a BookService that is used for storing (add), getting (getAll) and removing (remove) books. Now I want to use this BookService inside my controller to publish some methods that I can later use in my view. My controller looks like the following:
app.controller('BookController', ['BookService', '$scope', function(BookService, $scope) {
$scope.addBook = function() { BookService.add(); }
$scope.getAllBooks = function() { BookService.getAll(); }
$scope.removeBook = function() { BookService.remove(); }
}]);
As you can see, the controller is just a proxy that forwards the method calls to the service. Is this in general bad practice? And is there a better way to solve that using Angular.js? Calling the service directly from the view without a controller seems, to me like a bigger problem.
Best Practice
From what I can see this is perfectly good practice, I'm assuming that you're using $scope here to bind these services to a button or something similar. The Idea of using a factory or a service is to create some well organised reusable code that you can then instantiate in your controller exactly like you have done.
Controllers can get quite complex so moving basic functionality to your service is the best thing to do. In your case you have a relatively simple controller so you may not see the benefit of coding in this way. But I assure you that as your code base grows you will be required to code like this just to keep things readable.
Note
Just as an extra note I've added a data process that you might take with an example of a database request
database > API > service/factory > controller > view
This would be the correct path to take when requesting something from a database.
What you have shown is not bad practice; it is in fact recommended that you abstract resource management logic (like adding, removing and finding resources of the same type) into a separate service, for organization and maintainability and reusability purposes.
I do understand from your example how the controller seems to be just a proxy to the service though, and why you would think it's repetitive and possibly redundant. Let me expand on your example to see if I can explain the reasons for abstracting the logic. In doing so, let's consider only the adding operation, even though the logic would apply to others as well.
Reason 1: adding a book (or any resource for that matter) in a web app is usually more complicated than just a bookService.add() call. It requires having some sort of form to enter the details, a submit button to handle the actual request, possible massaging of the entered data, adding new fields like time stamps and author ids, and persisting to some data storage. The first two or three of those steps would be applicable to the screen or view from which you are adding, so it makes sense to put that in a controller (the view model), whereas the rest of the steps should go into the generic service.
Now say you have another screen where you see a friend's list of books and you can add it to your list by selecting a book. In that case, the controller logic for that view's controller will differ from the previous example, but the service steps will still be identical, and thus do not need to be duplicated since the service can be injected into both controllers.
That's a big win.
Example 2: let's say you've already split the code between controller and service as describe in example 1 above. Then you decide that your data storage needs have changed and now you want to update your logic to save to a different database system. Now instead of having to change all the controllers, you just change your services and the everything works.
This is also a really big win.
Yes you'ved done this correctly. Just keep in mind that as you scaled up your app in complexity, you'll want to have isolated scopes, (and) decomposition into component directives. Each directive will have its own controller and template fragment.
I am early on in my angularjs development.
I have created a single page app. A couple of the areas call controllers that have code like below embedded in them.
snippet from the route::
.when('/searchDrug/:searchStr', {
templateUrl: SNIPsArray['searchresultsSNIP'],
controller: 'searchDrugCtrl'
})
snippet from the controller::
function ($scope, $http, $routeParams, $location) {
$http.get(url).success(function(data) {
if ( data.drugmaster.length == 1 ){
$location.path('/fetchDrug/'+data.drugmaster[0].drug_Id);
return;
}
$scope.druglist = data.drugmaster;
});
}
Question:
looking on the angularjs site and reading some articles on best practices, it is suggested to move $http from the controller to a service. I am looking for some help on what the advantage is of adding another level and another file for this call?
Maintainability
As your application grows, you will find that there will be more and more need of encapsulating your business logic somewhere. In angular you do this using services.
Encapsulation
Not only do services allow you to reuse code, but also to introduce good principles like abstraction and encapsulation.
Richer domain
What's also important to note is that perhaps you want to introduce some additional modelling and functionality above what you get back from the service. With a service, you can do stuff like do the http call, then wrap the results in a function with some methods on it. This results in a much richer domain model and readable code.
For example, here the search function returns a promise with some domain logic on it to massage the data coming back from the service:
function ($scope, $routeParams, $search) {
$search($scope.drug)
.then(function(result){
$scope.drugList = result.getDrugList({include: ['title', 'description']});
});
}
Testing
It's obviously much better to write tests for a controller that depends on a service and tests for a service on it's own.
Because of code reuse, maintainability and testability. Testing resources is a separate process than testing whole controller.
Sorry for the vague title;
I've been restructuring some of my AngularJS code, trying to be more "Angular" about it, and I've noticed this pattern popping up quite a bit:
app.service("someService", function(...) {
...
}
app.controller("ControllerForThisSection", function($scope, someService) {
$scope.someService = someService
}
Basically, the controller is mostly there to give the scope a reference to the service so a view can use it, like
<div ng-if="someService.status">
....
</div>
So I have more than a few controllers that do nothing more than depend on certain shared data or services and serve to make references to those services available through the scope.
Is there any disadvantage to using this design? Can I improve my thinking any? Is this the "angular" way to do it?
Thanks for any advice!
This is the "angular way". Shared data should be placed into services, then injected where needed.
I like to think of my Angular apps mainly in terms of models (which are usually stored in services) and views. The controllers are just the glue that allows us to project/extract the parts of our models that a particular UI view needs.
Also, think of services as returning a model API, not a model object (to quote Josh).