I'm working on a project that needs the backend to be mocked while the frontend implementation is being made.
I have a gulp structure that loads /**/*.mock.js files only for tests and while serving the source for development. I use this .mock.js files to mock test data and services and to intercept http calls for backendless development. The file looks like this:
(function() {
'use strict';
var mockResponseData = {
'mock': 'data'
};
angular
.module('module.name')
.value('mockResponseData', mockResponseData)
.config(config)
.run(run);
/** #ngInject */
function config($provide) {
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
}
/** #ngInject */
function run($httpBackend) {
$httpBackend.whenPOST(/\/api\/path\/$/)
.respond(function() {
return [200, mockResponseData];
});
$httpBackend.whenGET(/^\w+.*/).passThrough();
$httpBackend.whenPOST(/^\w+.*/).passThrough();
$httpBackend.whenPUT(/^\w+.*/).passThrough();
$httpBackend.whenDELETE(/^\w+.*/).passThrough();
}
})();
As you can see, I already have the .passThrough() applied (which is the common solution for the Unexpected request issue) and it works just fine when just one .mock.js file like this exists. After creating the second file, the Unexpected request errors start to appear:
Error: Unexpected request: GET 'path/to/template.html'
After trying some things, I realized that the problem could be with the way I'm injecting $httpBackend. To avoid it from being injected in the dist files and to keep the project structure modular, I have to inject it via $provide instead of directly in the module's definition, in each module that needs it. Which is done in the config block of the code above.
If the $provide.decorator(...) line is removed from all but one .mock.js files, everything works fine again (except that the run block from the files with the line removed would start to complain about the non-existent $httpBackend, but this is not the issue here).
So, any idea of what this could be?
Thanks in advance!
P.S.: I can't miss the modularity structure and the .mock.js files cannot be included in the dist build.
So, I've solved this a while ago and now I'm answering here for future reference.
The basic problem is that I was injecting the $httpBackend service using the $provide.decorator() method, which according to the documentation, works like this:
Register a service decorator with the $injector. A service decorator intercepts the creation of a service, allowing it to override or modify the behavior of the service. The object returned by the decorator may be the original service, or a new service object which replaces or wraps and delegates to the original service.
Injecting the service like this was necessary to avoid injecting it when it wouldn't be used, but somehow, if you decorate this same service twice, AngularJS get confused and starts to throw the errors described in the question.
So, the solution is to create a .mock.js file for the main module. This way it will inject the service only once and avoid the problem:
(function() {
'use strict';
angular
.module('mainModuleName')
.config(config);
/** #ngInject */
function config($provide) {
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
}
})();
Please note that you can still add more decorators to the service as follows:
$provide.decorator('$httpBackend', function($delegate, $injector) {
// Your decorator...
});
As long as you don't inject angular.mock.e2e.$httpBackendDecorator more than once.
Related
Hi I have a problem :) I developed an angular project and it works fine in my localhost apache. I'm using angular 1.4.3 for frontend things and node.js for backend things. Now I need to use Grunt to version it.
I did many config (such as that maple thing disabled.it changes all variables names such as a,b,c from indexCounter,PersonCounter,AgencyCounter and it causes some problems.) But I get an error everytime I try to run. It is like [module] cannot found. My project works on my local machine If I dont grunt it but after the grunt I take 3 files as vendor.js vendor2.js and app.js. In vendor.js I have native angular js files. In vendor2.js I have plugins. and lastly In app.js I have controllers. I'm added them in my index HTML in this order;
vendor.js
vendor2.js
app.js
Do you have any idea about that?
Problem: In angular you cannot minify your code without using either .$inject or array syntax.
Solution
For each controller, directive, service etc. that you add to your module, you will have to add en extra array of strings specifying the dependencies that should be injected into you service, controller etc.
Example
Using $inject property
function mainController(indexCounter,PersonCounter,AgencyCounter) {
//Controller implementation
}
mainController.$inject=['indexCounter','PersonCounter','AgencyCounter'];
angular
.module('mymodule')
.controller('mainController', mainController);
Using array
var mainController;
function controller(indexCounter,PersonCounter,AgencyCounter) {
//Controller implementation
}
mainController=['indexCounter','PersonCounter','AgencyCounter', controller];
angular
.module('mymodule')
.controller('mainController', mainController);
When you don't specify the dependecies in an array, angular will try to find an registered dependency based on its' parameter name. e.g. function mainController(indexCounter) {}, then angular will try to find a registered dependency called indexCounter. It works perfectly fine when the code is not minified. However, when minified the indexCounter parameter name will be changed to a shorter name, e.g. a. And when trying to find dependency a which is not registered in your app. It's gonna fail to instaciate your angular module.
I am wondering how can I inject dependencies in more readable way in angular. I am more interested in AMD(requirejs) way. Like following:
define(function (require) {
var ModuleOne = require("ModuleOne"),
ModuleTwo = require("ModuleTwo");
var ThisModule = (function () {
// code here
})();
return ThisModule;
});
Is it possible to inject dependencies above way in angularjs or is there any better way to do it then current way in angularjs?
From the Angular JS official website
Angular modules solve the problem of removing global state from the
application and provide a way of configuring the injector. 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
fulfil their goals.
Hence, purpose of both the libraries(RequireJS and AngularJS) is totally different. The dependency injection system built into AngularJS deals with the objects needed in a component; while dependency management in RequireJS deals with the modules or, JavaScript files.
In requireJS, Objects of loaded modules are cached and they are served when same modules are requested again. On the other hand, AngularJS maintains an injector with a list of names and corresponding objects. In this case, Object is served whenever it is referenced using the registered name.
Generally we inject dependencies in angularJS like
someModule.controller('MyController', function($scope,greeter){
});
If you want an alternative way to inject dependencies in angularJS, you may do something like.
var MyController = function($scope, greeter) {
// ...
}
MyController.$inject = ['$scope', 'greeter'];
someModule.controller('MyController', MyController);
Hope it helps!
I am trying to inject a controller into my app.run function, however i keep getting:
Uncaught Error: [$injector:unpr] http://errors.angularjs.org/1.2.10/$injector/unpr?p0=AuthenticationControllerProvider%20%3C-%20AuthenticationController
Here's my app.js:
var app = angular.module('app', [
'AuthenticationModule'
]);
app.run(['$rootScope', 'AuthenticationService', 'AuthenticationController',
function($rootScope, AuthenticationService, AuthenticationController) {
console.log(AuthenticationController);
}
]);
The AuthenticationService is injecting just fine. Why are AuthenticationController not being injected?
As stated in the AngularJS documentation on modules:
Run blocks - get executed after the injector is created and are used
to kickstart the application. Only instances and constants can be
injected into run blocks. This is to prevent further system
configuration during application run time.
In the documentation for controllers, it states:
In Angular, a Controller is a JavaScript constructor function that is
used to augment the Angular Scope. When a Controller is attached to
the DOM via the ng-controller directive, Angular will instantiate a
new Controller object, using the specified Controller's constructor
function.
A controller is an instance constructor function, not an instance itself, as opposed to a service, which is. Therefor, from what I can gather, controllers cannot be injected into a run block.
If you need to configure a controller at start-up time, then use a provider. As it turns out, in angular, controllers (along with directives, filters, and animations) are all simply syntactic sugar for a provider. Providers can be configured using configuration blocks: configuration block documentation
Configuration blocks - get executed during the provider registrations
and configuration phase. Only providers and constants can be injected
into configuration blocks. This is to prevent accidental instantiation
of services before they have been fully configured.
I am building a GUI for an app whose API is still being worked on. I have an idea of how it will look but it's not functional yet. I have to mimic its behaviour until it's ready.
I'm trying to do this with $httpBackend. I set up my installation with Yeoman.
Attempt to Install
I'm running Angular v1.2.6.
The documentation gives me three ways of installing it:
Google CDN at //ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-mocke2e.js
Bower via bower install angular-mocke2e#X.Y.Z
code.angularjs.org: //code.angularjs.org/X.Y.Z/angular-mocke2e.js
Where X.Y.Z is my version number.
Thing is none of those work. Google gives 404, and won't let me travel up the directories. Bower says no package available, and a 404 for code.angularjs.org. So I browsed code.angularjs.org and found that for 1.2.6, there is no anguler-mock
Anyway I did find a file at https://code.angularjs.org/1.2.6/angular-mocks.js which seems to have $httpBackendProvider defined in it.
When I went back to my Yeoman install - it looks like I already had angular-mocks.js installed.
So, question 1: Is this what I'm looking for to use $httpBackend?
Attempt to Use
So I try to start using it in my project:
// Gotta manually run the app to check cookies
app.run(['$cookies', '$location', '$rootScope', '$httpBackend', function($cookies, $location, $rootScope, $httpBackend){
// Some stuff....
console.log($httpBackend);
}]);
And now my app won't start. I get the following error:
But the app does show me the contents of $httpBackend. I thought that maybe I should put it into a different module, but I don't know.
So question 2: Is it normal that my app won't load once I throw $httpBackend into the app.run() call?
And question 3: Should I be putting my $httpBackend stuff into a different module while I'm testing?
And I guess lastly is Is this even the best way for me to test the API?
You can use $httpBackend to mock your services like so:
$httpBackend.whenGET(/\/api\/foo\/bar/).respond({some:'thing'});
Your app isn't loading because it wasn't expecting a request for your partials. You can avoid that by doing something like this:
$httpBackend.whenGET(/^views\/.*/).passThrough();
It would be good to break these out into individual modules so you can exclude them before deployment.
As far as it being the best solution, it depends on your needs. I would propose integrating some build tools (Grunt, Express, Protractor, etc.) that include a local node server for testing locally. Then you could just use actual services for testing, as proposed by #Ronald91.
Going through a similar situation like this on a project I am working on. Never utilized httpbackend for this but we mocked out the backend services with a MongoDB instance and in our app.js file we set up the routing as followed:
app.get( '/ui/orders/:orderId/details', function( req, res ){
mongoclient.connect( mongoUrl, function(err, db){
if( err ) throw err;
var collection = db.collection('order-details');
console.log( req.query);
collection.findOne( { orderId : req.params.orderId}, function(err, docs){
if( err ) throw err;
console.log( "Docs...");
console.log( docs );
db.close();
res.send( docs );
} );
});
});
The service call we use to utilize this routing is
ourApp.factory('orderDetails', function($resource, $routeParams) {
return function(){
return $resource('/ui/orders/:orderId/:subSystem', {orderId: $routeParams.orderId});
};
});
This is a potential answer to the "Attempt To Install" query.
Yes, angular-mocks.js is what you want. The file contains multiple modules useful for mocking. You don't have to use all of them.
For the specific case you mentioned (and the issue I'm trying to solve too, which brought me here):
include the file you found
add the following dependency to your main app module, you should be able to mock back-end requests.
var yourApp = angular.module('app', ['ngMockE2E']);
You can use the mocks module in unit tests and in application development.
In this repo AngularJS is implimented with RequireJS for AMD.
In this repo the AngularJS team seeds an AngularJS project with AMD that does not include RequireJS.
Am I thinking about this the wrong way - I.E. are they solving different problems?
Does the AngularJS libary support AMD now where it had not once before?
Is it no longer necessary to use RequireJS with AngularJS projects?
Using RequireJS with AngularJS makes sense but only if you understand how each of them works regarding dependency injection, as although both of them injects dependencies, they inject very different things.
AngularJS has its own dependency system that let you inject AngularJS modules to a newly created module in order to reuse implementations. Let's say you created a "first" module that implements an AngularJS filter "greet":
angular
.module('first', [])
.filter('greet', function() {
return function(name) {
return 'Hello, ' + name + '!';
}
});
And now let's say you want to use the "greet" filter in another module called "second" that implements a "goodbye" filter. You may do that injecting the "first" module to the "second" module:
angular
.module('second', ['first'])
.filter('goodbye', function() {
return function(name) {
return 'Good bye, ' + name + '!';
}
});
The thing is that in order to make this work correctly without RequireJS, you have to make sure that the "first" AngularJS module is loaded on the page before you create the "second" AngularJS module. Quoting documentation:
Depending on a module implies that required module needs to be loaded
before the requiring module is loaded.
In that sense, here is where RequireJS can help you as RequireJS provides a clean way to inject scripts to the page helping you organize script dependencies between each other.
Going back to the "first" and "second" AngularJS modules, here is how you can do it using RequireJS separating the modules on different files to leverage script dependencies loading:
// firstModule.js file
define(['angular'], function(angular) {
angular
.module('first', [])
.filter('greet', function() {
return function(name) {
return 'Hello, ' + name + '!';
}
});
});
// secondModule.js file
define(['angular', 'firstModule'], function(angular) {
angular
.module('second', ['first'])
.filter('goodbye', function() {
return function(name) {
return 'Good bye, ' + name + '!';
}
});
});
You can see that we are depending on "firstModule" file to be injected before the content of the RequireJS callback can be executed which needs "first" AngularJS module to be loaded to create "second" AngularJS module.
Side note: Injecting "angular" on the "firstModule" and "secondModule" files as dependency is required in order to use AngularJS inside the RequireJS callback function and it have to be configured on RequireJS config to map "angular" to the library code. You may have AngularJS loaded to the page in a traditional manner too (script tag) although defeats RequireJS benefits.
More details on having RequireJS support from AngularJS core from 2.0 version on my blog post.
Based on my blog post "Making sense of RequireJS with AngularJS", here is the link.
Yes, you can use RequireJS with angular. You need to do a bit of extra work to make it function, as in the link you included, but it's possible.
In general, though, I haven't found any need for AMD with Angular. The whole idea of AMD is that it allows you to declaratively specify the dependencies between your scripts and not worry about the order in which you include them on the page. However, Angular takes care of that for you with its dependency injection mechanism, so you're not really getting any benefit by using AMD on top of that.
tl;dr I haven't found a compelling reason to use AMD with Angular.js.
You can lazy load Angular.js components using providers. From the article:
Providers are essentially objects that are used to create and configure instances of AngularJS artefacts. Hence, in order to register a lazy controller, you would use the $controllerProvider.
...
In summary, you would first define your app module to keep instances of the relevant providers. Then you would define your lazy artefacts to register themselves using the providers rather than the module API. Then using a ‘resolve’ function that returns a promise in your route definition, you would load all lazy artefacts and resolve the promise once they have been loaded. This ensures that all lazy artefacts will be available before the relevant route is rendered. Also, don’t forget to resolve the promise inside $rootScope.$apply, if the resolution will be happening outside of AngularJS. Then you would create a ‘bootstrap’ script that first loads the app module before bootstrapping the app. Finally, you would link to the bootstrap script from your ‘index.html’ file.
http://ify.io/lazy-loading-in-angularjs/