Controllers <-> Services interaction and unit-testing : Am I doing this wrong? - javascript

I'm building an AngularJS app, and I would it to be respectful or the best practices.
Thus, when I have to call an external $resource or $http request, I do it in an external Service/Factory.
My problem is I don't know what is the best way for retrieving returned value of the Service into the calling Controller.
Currently, what I do is the following :
The controller calls the Service
The Service does the $http call and check the promise.
If promise is successful, the Service $broadcast an event with the returned object.
The Controller catches that event and does operations.
It works great and allows me to use the same event after different requests (ex: In a discussion, after retrieving all messages and after posted myself a message, the same event "new messages to display" is called)
.
But I finally decided to set up testing processes in my application (better late than never), and I realize that I could do it wrong.
--> When unit-testing my Controllers I want to mock some of my Services for returning given values. But as my Controller calls Services which doesn't returns anything (they fire events instead) I think it will be pretty hard and not natural to check Services "return" values. (I can always check if the Service has been called, but not the result).
.
Your opinion, have I done misconception errors ? I consider getting the promises directly into Controllers when calling Services, is this a good (better?) way to do it ?
I aware that each application have its own logic, but I think in AngularJS the number of "working" logics (that preserves modularity, and testability) are particularly restricted to some set of choices and best practices.
Any suggestion would be appreciated.

Without seeing your code, I have my ideas:
Your controller could receive that promise and do what it needs to do. There is no real need to broadcast anything. Your service call $http and then return the promise. When $http promise is resolved, your controller will know and can do what you need to do.
Testing functions in a service that doesn't return anything is not a problem, that is what spyOn is. You can check that the function has been called and that should be enough for your controller. You don't actually care what happens in the service, you cares about your controller (in the test controller, of course).

Please don't use events for such kind of interaction. They make a mess inside of the code and reduce maintainability. Sometimes you can use them but it is fine only for certain operations and for some specific situations. Using promises is good approach.
Here you have three different options:
Just return the $http promise object and handle it inside of your controller
Make your own promise with $q service and place inside of it your own logic for handling the response from $http or direct returning result (it is more flexible approach than the first one)
Just pass to the services a function callback which you can call there inside of your service to pass the result back to controller.
But you definitely don't need to use events in these cases.
What is related to unit-testing you will configure $httpBackend service to mock your queries and in this case everything will work fine and you can test anything you need ($httpBackend). It means that you can inject your service and it works fine (as instead of real http call it will return the object configured as a response in the $httpBackend). So you can still test your controller without need to make anything complicated to your services.

Related

Angular Data that does not affect the view: use directive, object, or function?

I have a repeated set of API calls in an Angular controller.
The relevant data is of course the API URI, POST or GET, some header data, and JSON objects. The API call always returns a JSON object.
I started to use a Directive but that seems most relevant for data that is used with $scope (e. g. in the View). This data runs under the radar and might eventually generate data used in the view but most usually not and most usually not in a way that can be abstracted.
So: Does this mean I shouldn't use a directive? If I don't use a directive, would it be better to use a JS Object (seems more modular) or Function (seems more variable friendly) to handle this code?
Yes, I get that objects can contain functions, but functions can also contain callbacks so...looking for kind of a "best practices" here. In terms of modularity and flexibility.
You should create Angular service for that.
https://docs.angularjs.org/guide/services
Your service will contain a method, lets say "getResults" which will make an API call and return either data or a promise ($http). Then you can inject you service to your controller and use that method to get the data and assign it to $scope.
An Angular service is certainly preferred to a more general JavaScript one because it allows you to take greater advantage of Angular's scope and other such things. Between Angular's Factory, Service, and Providers, a Service is the most in line with what you're trying to do since a Factory is too basic and generally used to solve smaller problems while a Provider is used -- as it says in the Angular docs -- "only when you want to expose an API for application-wide configuration that must be made before the application starts." Which is not what you're trying to do.

How do you 'intercept' all HTTP requests in an EmberJs app?

I would like to be able to capture all HTTP requests and responses, and modify them, before they get to the rest of an EmberJs app. I would like to do this globally - across the entire app. I have not been able to find this digging through the API. How can this be done?
(Modification is to perform some conditional logic based in certain headers, or to add or modify certain headers).
In AngularJS, you can accomplish this using something like this:
App.factory('AppHttpInterceptor', function($q) {
return {
request: function(req) {
//modify request
return req;
},
response: function(res) {
// modify response
return res || $q.when(res);
}
};
});
App.config(function ($httpProvider) {
$httpProvider.interceptors.push('AppHttpInterceptor');
});
First, it's important to note that Angular and Ember are not built to serve the same purpose. They are both javascript frameworks, and that's about where the similarities end. Another big factor in the difference of async requests in the two framework is Angular's integration of promises into it's async services. Ember async services are not promise based, and therefore response interceptors are not possible (see my note below).
AngularJS provides $httpProvider as a configurable singleton that returns a configured instance of $http as a promise object. Angular focuses on services rather than methods (although it does have a few methods it gives you), and that's what sets Angular apart from other frameworks like Ember, which give you a structure to build services, but doesn't provide services in it's core.
Instead, with Ember, you'd have to build a service and service provider concept yourself. You could take something like cerebris/ember-rest, and extend it in such a way as to use properties you describe. This library provides an Ember.resource method, which you could use prototype to extend from there:
Ember.ResourceAdapter.extend({
_prepareResourceRequest: function(params) {
params.beforeSend = function (xhr, settings) {
//set your xhr interceptors here
}
}
});
Edit: Clarification on use of $ajax in ember and $http in Angular (promises vs callbacks)
The biggest difference on how angular makes response interceptors possible, is that async requests in angular are promises, whereas $ajax calls are not. Without getting too much into the weeds, you can assign variables to each step of a promise, making it available for mutation/handling along every step of the way, whereas with $ajax, you can only perform operations when the data has completely returned. With Promises, assign variables to represent states at any point during the lifecycle of the promise, and using that reference, you can mutate the promise as needed, at any point in time prior to the event of the promise fully resolving.
Thus why it's possible to do request interceptors with $ajaxPrefilter but there is no good way to do response interceptors, using the abstract configuration approach with $ajax. To truly do in Ember what AngularJS does with $http, you need to create a promise based async request/response service, as opposed to utilizing non-promise based xhr requests such as $ajax.
jQuery does provide an $ajaxSetup() method, which you might be able to set a dataFilter property to and define a handler function, however this is not recommended. With angular, the $httpProvider can be configured by module, and through de-coupling and Separation of Concerns, this can become truly powerful, allowing you to encapsulate and cascade http interceptor configurations with a lot of control. Making the same changes to your ajax settings will register themselves on the global jquery namespace, and can cause conflicts if you need to grow your app.
One video I found particularly englightening on this subject was from ng-conf 2014: Christion Lilley: Going Postal with Angular Promises
Edit 2: addressing Ember.RSVP
While Ember.RSVP is indeed a promise class useable in the framework, it does not have any methods available to perform resource requests. This means you have to manually assign an http request instance to an instance of RSVP.deferred such that it resolves your http request before returning the promise.
This allows you to do interceptors on both sides of each individual request, but does not provide a solution for configuring interceptors for all requests. You'd have to create another function or service for that, and extend RSVP with this function.

SailsJS: how to access the res.render function from anywhere

I just need to use the res.render function at some places but it is not passed to the callback I am using. Isn't there somewhere a global version of the render function or is there another way to access this?
In Sails v0.10.4 there is
sails.renderView(viewPath, options, callback);
Also, when rendering emails, you probably don't need to use a layout. So before calling sails.renderView, add
options.layout = false;
You don't need it at all places, really.
res is a response object and it makes sense only in controllers, because only controllers deal with request handling and response rendering.
Do you want to use res.render from models, services? Maybe it's because you think that all your code is always in we_have_request_and_user_is_waiting_for_response state.
Well, It's not true: model methods and services can be called from sails console, or from background workers, or from code, than runs by schedule. In these cases we don't have res object at all.
Controllers are place specially designed to contain user actions responding logic. So, you should place that logic in controllers. MVC
Technically, previous speaker is somewhat right: there is a reason res exists only within controllers. For example, what if it's a CLI application, and there's simply no request/ response in HTTP-meaning?
That said, if you just need to render some HTML for one or another purpose, you can always use global Express' renderer, which doesn't require response to be presented: it's accessible via sails.express.app.render, see Express' documentation for the method's params.

Angular services decoupling

I'm playing with angularjs doing some simple stuff. I wanted to incorporate some real time messaging like PubNub, PubSub or Pusher.
Basically I have it working, but it seems too cumbersome in some areas. Maybe you can show me a better way.
Ok, so here is my working jsfiddle (with pubsub faked) http://jsfiddle.net/canree/dD5VR/
I have external service (pubsub) and created angular service for it.
Now as I don't want other parts of my app to be dependent on this concrete implementation , I decided to create another one (inbox) that would store all the messages pubsub sent. And this inbox would be the dependency across my app (instead of pubsub).
So I want my dependencies to be:
controller -> inbox -> pubsub
When pubsub receives a message it needs to add it to inbox so that controller can see it and react accordingly. The problem is I don't want this backward dependency (pubsub -> inbox). I'd like pubsub to notify observers that message was received.
What I have now is ugly callback registration (inbox registers callback in pubsub). I don't like it very much as it has a lot of boilerplate (regarding this handling etc).
I wonder if there is other way to connect those services? Events (but without being forced to use $rootScope everywhere)? If so, where to register such event handler? Can services receive events?
Take a look at the fiddle provided and advice me how to solve it better way.
I don't want other parts of my app to be dependent on this concrete implementation
The interaction with the server is already wrapped/abstracted in the pubsub service. If the interaction with the server needs to change, it is already isolated to that service. I don't think adding another layer of abstraction buys you enough to offset the extra code you need to write (as you've already seen).
Here are a few other observations about how you could simplify your code (some of this you probably already know, but it might be instructive for others):
The content() function is not necessary (especially since messages is not private data). In Angular the controller $scope/model is often directly tied to a service model, so you could simply use:
$scope.messages = inbox.messages;
(If you like the content() function, consider making messages private in the inbox service.)
There is no need to send an event to the inbox controller about the change to inbox's messages array. The append() function just needs to call $rootScope.$digest(). And actually, this is probably not needed, since the real service would likely be using $http or $resource, which will automatically call $apply() or $digest. So _broadcastNewMessage() is not needed, nor is the $scope.$on() in the controller.
Even if you leave the content() function as is, you still won't need to broadcast anything.
Since inbox is dependent on pubsub, I would have the inbox service register itself
pubsub.subscribe(this.append, this);
pubsub's init() method can be in-line (no function required)
Fiddle with all of the above observations incorporated.
I wonder if there is other way to connect those services?
I can only think of model sharing (e.g., in service1: this.service1_obj_or_obj_property = service2.service2_obj_or_obj_property) and creating functions on the dependent service that the other service can call.
Can services receive events?
Since $on and $broadcast are defined on Scope, and since services are singletons and are not affected by scopes, I'm pretty sure the answer is "no."

AngularJS $http and $resource

I have some web services that I want to call. $resource or $http, which one should I use?
$resource: https://docs.angularjs.org/api/ngResource/service/$resource
$http: https://docs.angularjs.org/api/ng/service/$http
After I read the two above API pages I am lost.
Could you please explain to me in plain English what is the difference and in what situation should I use them? How do I structure these calls and read the results into js objects correctly?
I feel that other answers, while correct, don't quite explain the root of the question: REST is a subset of HTTP. This means everything that can be done via REST can be done via HTTP but not everything that can be done via HTTP can be done via REST. That is why $resource uses $http internally.
So, when to use each other?
If all you need is REST, that is, you are trying to access a RESTful webservice, $resource is going to make it super easy to interact with that webservice.
If instead, you're trying to access ANYTHING that is not a RESTful webservice, you're going to have to go with $http. Keep in mind, you could also access a RESTful webservice via $http, it will just be much more cumbersome than with $resource. This is the way most people have been doing it outside AngularJS, by using jQuery.ajax (equivalent of Angular's $http).
$http is for general purpose AJAX. In most cases this is what you'll be using. With $http you're going to be making GET, POST, DELETE type calls manually and processing the objects they return on your own.
$resource wraps $http for use in RESTful web API scenarios.
Speaking VERY generally: A RESTful web service will be a service with one endpoint for a data type that does different things with that data type based on HTTP methods like GET, POST, PUT, DELETE, etc. So with a $resource, you can call a GET to get the resource as a JavaScript object, then alter it and send it back with a POST, or even delete it with DELETE.
... if that makes sense.
$http makes general purpose AJAX call, in which general means it can include RESTful api plus Non-RESTful api.
and $resource is specialized for that RESTful part.
Restful Api came to prevalent in recent years because the url is better organized instead of random url made up by programmers.
If I use a RESTful API to construct the url, it would be something like /api/cars/:carId.
$resource way to fetch data
angular.module('myApp', ['ngResource'])
// Service
.factory('FooService', ['$resource', function($resource) {
return $resource('/api/cars/:carId')
}]);
// Controller
.controller('MainController', ['FooService', function(FooService){
var self = this;
self.cars = FooService.query();
self.myCar = FooService.get('123');
}]);
This will give you an resource object, which is accompanied with get, save, query, remove, delete methods automatically.
$http way to fetch data
angular.module('myApp', [])
// Service
.factory('FooService', ['$http', function($http){
return {
query: function(){
return $http.get('/api/cars');
},
get: function(){
return $http.get('/api/cars/123');
}
// etc...
}
See how we need to define each common operation on RESTFul API. Also one difference is that $http returns promise while $resource returns an object. There are also third-party plugins to help Angular deal with RESTFul API like restangular
If the API is something like /api/getcarsinfo. All left for us is to use $http.
I think the answer depends more on who you are at the time you're writing the code. Use $http if you're new to Angular, until you know why you need $resource. Until you have concrete experience of how $http is holding you back, and you understand the implications of using $resource in your code, stick with $http.
This was my experience: I started my first Angular project, I needed to make HTTP requests to a RESTful interface, so I did the same research you're doing now. Based on the discussion I read in SO questions like this one, I chose to go with $resource. This was a mistake I wish I could undo. Here's why:
$http examples are plentiful, helpful, and generally just what you need. Clear $resource examples are scarce, and (in my experience) rarely everything you need. For the Angular newbie, you won't realize the implications of your choice until later, when you're stuck puzzling over the documentation and angry that you can't find helpful $resource examples to help you out.
$http is probably a 1-to-1 mental map to what you're looking for. You don't have to learn a new concept to understand what you get with $http. $resource brings along a lot of nuance that you don't have a mental map for yet.
Oops, did I say you don't have to learn a new concept? As an Angular newbie you do have to learn about promises. $http returns a promise and is .thenable, so it fits right in with the new things you're learning about Angular and promises. $resource, which doesn't return a promise directly, complicates your tentative grasp on Angular fundamentals.
$resource is powerful because it condenses the code for RESTful CRUD calls and the transformations for input and output. That's great if you're sick of repeatedly writing code to process the results of $http yourself. To anyone else, $resource adds a cryptic layer of syntax and parameter passing that is confusing.
I wish I knew me 3 months ago, and I'd be emphatically telling myself: "Stick with $http kid. It's just fine."
I Think it is important to emphasize that $resource expects object or array as response from server, not raw string. So if you have raw string (or anything except object and array) as a response, you have to use $http
When it comes to choose between $http or $resource technically speaking there is no right or wrong answer in essence both will do the same.
The purpose of $resource is to allow you to pass in a template string (a string that contains placeholders) along with the parameters values. $resource will replace the placeholders from the template string with the parameter values those being passed as an object. This is mostly useful when interacting with RESTFul datasource as they use similar principles to define the URLs.
What $http does is to perform the Asynchronous HTTP Requests.
resource service is just useful service for working with REST APSIs.
when you use it you don't write your CRUD methods (create,read,update and delete)
As far as I see it, resource service is just a shortcut,
you can do anything with http service.
One thing i noticed when using $resource over $http is
if you are using Web API in .net
$resource is tied up into one controller that execute a single purpose.
$resource('/user/:userId', {userId:'#id'});
[HttpGet]
public bool Get(int id)
{
return "value"
}
public void Post([FromBody]string value)
{
}
public void Put(int id, [FromBody]string value)
{
}
public void Delete(int id)
{
}
While $http could be of anything. just specify the url.
$http.get - "api/authenticate"
[HttpGet]
public bool Authenticate(string email, string password)
{
return _authenticationService.LogIn(email, password, false);
}
it's just my opinion.
The $resource service currently does not support promises and therefore has a distinctly different interface to the $http service.
What I could understand is that if you are having a RESTFUL resources to manage, using $resource will let you define the resource once in code, and use the resource for multiple operations on the resource. This increases the readability and maintainability of the code.
If you just want to have general purpose calls not managing it like post, delete, update, you may go with $http or $resource (as you prefer).

Categories