$http request in controller? - javascript

I have an impression that all data-related requests has to be delegated to services/factories. But reading examples of $http implementation met not once that they are put right inside of a controller, e.g.:
var app = angular.module("MyApp", []);
app.controller("PostsCtrl", function($scope, $http) {
$http.get('data/posts.json').
success(function(data, status, headers, config) {
$scope.posts = data;
}).
error(function(data, status, headers, config) {
// log error
});
});
is that considered as normal practice/pattern or it is just for the sake of an example?

So best practices are dependent on the project-specific context, mostly overall code size of your application but also size of team, length of project/engagement, etc. For a small utility with just a few hundred lines of code, creating a service to encapsulate a single use of $http is over engineering. It makes your project harder, not easier, to read and maintain. However, for a full-size application with many modules, dozens of files, thousands of lines of code, where the same service logic might need to be reused across several controllers, then yes, it makes sense to move your $http code to a service where it can be independently encapsulated, shared, and tested. So no, for "normal" applications (medium or large in size), using $http in a controller is not considered a best practice pattern. However, for an instructive example/demo code snippet, or a very trivial project, it's fine.

Related

Angular.js Method duplication in controller when using service

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.

Is there any harm in adding more dependencies in angular js controller or service

Suppose this is my angular controller
app.controller("MyCtrl", function($scope, $modal, $state,) {
});
I am thinking of having one global variable holding most commonly used dependencies like
var all = ['$scope', '$modal', '$state']
and then use all at every place along with some other dependencies if needed
Is there any performace issue having put all dependencies everywhere
Having to inject more code would have a performance hit, but not a major one. I don't recommend defining your dependencies globally like that because dependencies should be very visible. You should know exactly what you're doing with them without having to open another file and check.
If you need to reuse a set of dependencies everywhere, that suggests more that there's probably something wrong with the code. How come different regions of the code base all talk to the same stuff? That suggests duplication of concerns. I don't extend that assertion to just having to inject $scope or $http all the time.
In short, I don't think it's a good idea to manage dependencies like that.
While I don't have deep knowledge of the Angular internals, nor have I bothered to actually measure the performance of what you are asking, I would venture my educated guess as .... no. You're not going to see a performance impact here. The only impact would be on controller instantiation, which only happens once per view. And even then, we're just talking about new-ing up a few objects ... the perf impact ought to be very negligible, and not something I would worry about.
You can't inject using a variable you define within another controller or service. One thing you could do is create a factory and put your dependencies in the $rootScope.
app.factory('root',function($rootScope, $modal, $state){
$rootScope.modal = $modal;
$rootScope.state = $state;
});
You just put $rootScope in all your controllers and you have access to whatever you like. You would only need to inject 'root' in your main controller (if you have one). It seems like fishy architecture to need this shortcut, but that is how I would do it. No perf hit really - non-primitives are reference types.

Using the $injector instance directly vs. obtaining dependencies as parameters

Within Angular, is it considered a good practice getting the dependencies directly from an $injector instance, rather than as parameters?
I have the problem that my controllers started having a lot of dependencies, so rather than:
myApp.controller(['$scope', 'Dep1', 'Dep2', 'Dep3', function($scope, Dep1, Dep2, Dep3) {
...
}]);
I would do:
myApp.controller(['$scope', '$injector', function($scope, $injector) {
var Dep1 = $injector.get('Dep1');
var Dep2 = $injector.get('Dep2');
var Dep3 = $injector.get('Dep3');
}]);
I find it the same functionality-wise, but with much less clutter on the parameters. I suppose that this will make my components slightly less easy to test though, right?
What do you think?
Based on my opinion and after reading the following posts:
Post 1
Post 2
Angular's documentation on DI (Dependency Injection)
Your second approach in order to minimize the long dependency list is considered as Service Locator Anti Pattern.
See - Service locator AntiPattern
Using the Service Locator Anti Pattern is bad because it will make your life as a maintenance developer worse because you will need to use considerable amounts of brain power to grasp the implications of every change you make. Using the $injector obfuscates the actual dependencies of the resource (in this case controller) and kills maintainability.
In addition, according to angular's documentation the the preferable way is:
Using the inline array annotation (preferred)
If your controller is ending up using so many dependencies maybe you are doing something wrong, Maybe you broke the Single responsibility principle. consider:
Delegate more of the logic to service(s) that are injected in
Separate out into different controllers, so each only has (just about) 1 responsibility
It depends how you write the code and how is easy to you and make you flexible to write code. I always write the controllers in this way hope it will help :}
var appCtrl = function($scope, others){...}
app.controller('appCtrl',['$scope', 'others', appCtrl]);

why should i move this http call to an external service

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.

AngularJS - Why have more than one controller

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.

Categories

Resources