How to fix injector error after Angular minification build? - javascript

Before speaking, I read about it made ​​recommendations but still causing error. Look the short code:
function IndexController($scope, $route, $routeParams, $location){
$scope.sfv = project.version.name;
}
angular.module("TkwebMobile", ['ngRoute', 'ngCookies'])
.controller('IndexController', ['$scope', '$route', '$routeParams', '$location', IndexController]);
Only this and the error persists. I'm using grunt to "uglify", and I'm also using the "concat" to unite the codes in a "lib". Even I using "injection" recommended in the Angular documentation.
Uncaught Error: [$injector:modulerr] Failed to instantiate module TkwebMobile due to:
Error: [$injector:unpr] Unknown provider: a
Is it problem of grunt concat? (grunt-contrib-concat)

This is due to your minification, specifically options to minify and mangle your variable names.
Angular determines what value to inject into your functions from the name of the parameters. For example...
angular.factory('MyFactory', function($location) {...});
...will cause angular to look for whatever dependency is named '$location' and then call your function with the $location value passed as it's parameter.
When you minify your javascript, with an option called mangle turned on, then the variable names get mangled. The previous function will turn into this...
angular.factory('MyFactory', function(a) {...});
Angular no longer has the correct parameter name in your source code, as $location is now a. This saves on size of your javascript but totally destroys Angular's implicit dependency resolution. You can solve this in one of two ways.
The first is a feature that angular provides for you.
angular.factory('MyFactory', ['$location', function(a) {...}]);
You provide the names of the parameters in an array, with the last element of the array being the function to inject the parameters into. This way, it doesn't matter what you call your parameters in the code, and the minifier will never change a string literal so Angular always knows what you're wanting.
The other way if you don't want to lose the convenience of not having to use the array notation is to turn off the mangle setting on your minifier. This obviously means you don't minify to the same degree, but ask yourself if it's really worth those extra bytes.
A halfway house is to use something like ngMin, to allow annotation of the array notation into your code and then continue with the minification. This is the best of both world's imo, but increases the complexity of deploying your clientside js.
EDIT
The correct settings to turn off the mangle behaviour in grunt would be this...
uglify: {
options: {
report: 'min',
mangle: false
}
}
But the ngAnnotate package can avoid this. See here for more info. (ngAnnotate is the package that has taken over ngMin)

I've had a similar problem. It turned out that I was not properly identifying and formatting the dependencies for controllers and services, etc. I believe I discovered this by looking at the minification output. (It was rough, let me tell you.)
Basically I had to look through all my files and verify that the dependency list matched what I was using in my controllers and services. It's strange because it worked without the changes.
Here is an example of what I had to do:
Original:
angular.module('FootCtrl', []).controller('FooterController', function($scope) {
$scope.footer = 'Copyright \u00A9 ' + new Date().getFullYear() + name;
});
Fixed
angular.module('FootCtrl', []).controller('FooterController', ["$scope", function($scope) {
$scope.footer = 'Copyright \u00A9 ' + new Date().getFullYear() + name;
}]);
Maybe take note of where I use single quotes vs. double quotes as well.
Hopefully this helps a bit. I'm not sure if you have more code than what is shown- if not, I'm not too sure.

I had this problem and it took me a lot of time to figure out what the issue was because I tried disabling mangling of variable names, using $inject array instead of just passing the services and provider names to function definitions while relying on angular implicit dependency injection but still the problem persisted. It turned out that in one of my controller which uses IIFE, was missing semicolon at the end. Look at the code below.
Before:
(function(){
})()
The above code works okay before minification due to automatic semicolon insertion but it breaks after minification because the absence of semicolon screw things up. So after correction it looked like below.
Correct:
(function(){
})();
This fixed my problem. I hope this might help some one
Note: I use grunt useminPrepare, usemin, copy, uglify and ngAnnotate.

Related

Replace assignment with deferred assignment

In my angular.js page I extend $scope with all attributes I need.
Before, I simply extended $scope with
angular.extend($scope,{
myAttr : aService.getValues()
});
However, turns out I have to add a promise (angular file access) to aService.getValues. Hence, I cant keep the above syntax but have to replace it with
angular.extend($scope,{
myAttr : null
});
aService.getValues().then(function(values){$scope.myAttr = values;})
That's annoying because I have to replace quite a bit of code and am afraid to add bugs. Any alternatives?
The only thing I can suggest is a slightly prettier re-doing of your own solution:
aService.getValues().then(function(values) {
angular.extend($scope, {
myAttr: values
})
});
Is it critical that $scope is extended synchronously? If it is, I don't see any way of feeding asynchronous data into it any other way.
EDIT
If you're reworking your logic, consider using $stateProvider with a resolve property: https://github.com/angular-ui/ui-router/wiki (Scroll down to the section that talks about resolve). Long story short, everything in the resolve must run before the state controller is initialized. This way you can do all your async operations in the resolve, and only let it load after you have everything you need.

Angular, trouble getting directive to work

I seem to be having trouble getting my directive to work, I think I may be registering it incorrectly, but I can't seem to figuire out how. Possibly the naming convention?
Here's what I have -
My Directive -
'use strict';
angular.module('demoApp')
.directive('packeryAngular', ['$rootScope', '$timeout',
function($rootScope, $timeout) {
return {
restrict: 'A',
scope: true,
link: function(scope, element, attrs) {
};
}
]);
On my div, I just call it like so
<div packeryAngular>
However, when I call it just like this, nothing seems to be happening. My first assumption was that I needed to register on my app like so
angular.module('demoApp', [ 'packeryAngular ']
However, when I do that I get the error in the console of :
Error: [$injector:modulerr] Failed to instantiate module packeryAngular due to:
Error: [$injector:nomod] Module 'packeryAngular' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
So, I'm pretty new to this but I'm not entirely sure what I'm doing wrong here, I've tried to copy other working examples and can't seem to get it working. Would appreciate any help, and thank you very much for reading!
You dont need to inject the directive , you just need to use it in html .Second Word of Directive with capital first letter becomes small proceeding with a hyphen,So on all next Capital first letters will become small and will be preceded with hyphen like yourCustomDirective becomes your-custom-directive
<div packery-angular>
get rid of injecting it in module
angular.module('demoApp', []);
You must put the following attribute on your node:
<div packery-angular>
When registering your directives, your names must use the lower camel case form. But HTML does not deal well with upper case letters in attribute names, so AngularJS expects upper case letters to be replaced by a dash followed by their corresponding lower case letter.
So your directive name in javascript code is yourDirectiveName, but in the HTML code, it must be referred as your-directive-name.

How to inject an entire module dynamically in AngularJS?

Alright, so I'm trying to set-up $httpBackend to use as a mock server for local development against an API. I have two services in separate modules: search and searchMock:
search returns a $resource object that exposes the API verbs when in staging or production environments, and works as expected
searchMock exposes $httpBackend which, in turn, responds with mock JSON objects, and works as expected by itself
I have another service, APIInjector, that determines what the current environment is based on a config file that's included dynamically by Grunt when the app is built and injects either search or searchMock accordingly.
My problem is, as far as I can tell from searching high and low, $httpBackend needs to be set-up within a module's run method. The problem with this is I can't inject the run method within my APIInjector's conditional logic.
How can I expose $httpBackend if the dev environment condition is met, and my $resource service otherwise? Note that I didn't include the calling controller's or the searchService's code, but I can if needed for clarification.
searchMock:
var searchMockService = angular.module('searchMockService', []);
searchMockService.run([
'$httpBackend',
function($httpBackend) {
results = [{name: 'John'}, {name: 'Jane'}];
$httpBackend.whenGET('/search').respond(results);
}]);
APIInjector:
var APIInjectorService = angular.module('APIInjectorService', [
'searchService',
'searchMockService'
]);
APIInjectorService.factory('APIInjector', [
'$injector',
'ENV_CONF',
function($injector, ENV_CONF) {
var isDevelopment = ENV_CONF.IS_DEVELOPMENT;
// Check to see if we're in dev and, if so, inject the local API services
if (isDevelopment) {
return {
Search: // Not sure what to do here to expose searchMock's run method???
};
} else {
return {
Search: $injector.get('Search') // This returns $resource from the search service, as expected
};
}
}]);
Just to put a final point on this question, I ultimately pulled this out to my build script (Grunt) and abandoned the above approach completely. Instead, I'm passing in the environment I want to build for (development, staging, production, etc) as a flag to Grunt and then loading the appropriate API service during the build.
Hope that helps someone else trying to implement backendless development!
As bad as it looks at first place, i'd use the document.write method,if you are not using requirejs(it shouldnt be a problem with requirejs).
1 - bootstrap your ENV_CONF variable in a script tag as first script in HEAD tag.
2 - load all the common scripts
3 - test the ENV_CONF.IS_DEVELOPMENT in another SCRIPT tag.
4 - if true document.write('<script src="angular-mocks.js"></script><script src="main-dev-module.js"></script>')
5 - if false document.write('<script src="main-prod-module.js"></script>')
6 - profit
That way you dont need to deal with ENV_CONF.IS_DEVELOPMENT inside your modules.
html5 boilerplate uses this technique to either fetch jquery from a cdn or locally
https://github.com/h5bp/html5-boilerplate/blob/master/index.html
Edit: I'm sure there are cleaner ways to inject services dynamically in angularjs,but that's what i'd do.I'd like someone to propose a better alternative.

Should angular.module be set variable on AngularJS

I’ve read two AngularJS sample on Github. I’m confused to how to modularize controller. The first on is set variable(foodMeApp) and re-use it. Controllers need less arguments. It’s easier to read.
But, second one doesn’t use variable(foodMeApp) and has more arguments. I think this way is frequently used on AngularJS samples.
Is this any merit to use the second way?
1.https://github.com/IgorMinar/foodme
var foodMeApp = angular.module('foodMeApp', ['ngResource']);
foodMeApp.constant('CONFIG_A', {
baseUrl: '/databases/',
});
foodMeApp.controller('HogeController', function HogeController($scope, CONFIG_A) {
console.log(“less arguments");
});
2.https://github.com/angular-app/angular-app
angular.module('foodMeApp', ['ngResource']);
angular.module('foodMeApp').constant('CONFIG_B', {
baseUrl: '/databases/',
});
angular.module('foodMeApp').controller('HogeController', ['$scope', 'CONFIG_B', function($scope, CONFIG_B) {
console.log("more arguments");
}]);
since angular.module('...') ,constant,provide,controller,factory ... return the same mdoule you can chain module method calls if you want to or not... it's just javascript.
you can write
angular.module('foo',[])
.controller('bar',function(){})
.service('baz',functinon(){})
.constant('buzz',something)
.value('bizz',somethingelse);
it makes no different.
This example is using an array for your dependencies which are going to be injected into your controller. The array method is typically used when the code is going to minified at some time and allows angularjs to know exactly which items will be injected. If this array were not there, angular would only see 'a', 'b' as the items injected into the function and would not be able to figure out what those things were. Items in the array are strings and will not be changed when the minification happens.
angular.module('foodMeApp').controller('HogeController', ['$scope', 'CONFIG_B',
function($scope, CONFIG_B) {
console.log("more arguments");
}]);
Hope this helps explain the differences which you have seen.
There is no such 'merit' of going for 2nd option. 1st approach is more popular as it avoids repeating angular.module('foodMeApp') so many times.
If you give it a name "foodMeApp" you can directly use foodMeApp as you would have used in 2nd approach. Less repetition, simpler life.

AngularJS Controllers: The Right Way

So, I've been talking with co-workers about the "right way" to create an AngularJS controller. I know there are several ways of creating one, but I'm interested in getting my team writing them the same way AND the "right" way. By "right way" I'm talking about easy to read, testable, and performant. I'm sure there are competing theories on the best way to create a controller, but I'm most interested in test-ability at the end of the day since that is what AngularJS is built to do well.
Without further ado, here's the contenders:
Let's assume that our app is declared as so: var app = angular.module('app',[]);
1.
app.controller('myCtrl', function(){
...
});
2.
function myCtrl = {
...
}
app.controller('Ctrl', myCtrl);
3.
(function(app) {
app.controller('myCtrl', function() {
...
}
})(app);
Please let me know if I've missed one.
This does not take into consideration the changes needed for minification so please do not add that to this conversation.
Thanks! (I hope this doesn't start a flame war ><)
My team exclusively uses 1. I've considered 3 as well, and we just have a standing rule (and jshint constraint) about global level code. I would never use 2.
I always do it this way:
angular.module('myModule').controller('MyController', ['$scope', function($scope) {
<stuff>
}]);
Yes, it's a little more verbose, but it creates nothing in global scope, it is very clear what module the controller belongs to, and if I need to separate my controller into another file I just copy and past it without worrying about where 'app' is defined or about whether I missed a '.' in a chained declaration.
And I know I was supposed to ignore it, but of course this version is minification safe.
In general I believe you should find a clear standard and stick to it. That way if something doesn't look "right" you can tell right away.
The Angular team recently posted a best practices guide so that might be a good place to start: http://blog.angularjs.org/2014/02/an-angularjs-style-guide-and-best.html. Also this presentation discusses some best practices on creating controllers: https://docs.google.com/presentation/d/1OgABsN24ZWN6Ugng-O8SjF7t0e3liQ9UN7hKdrCr0K8/present?pli=1&ueb=true&slide=id.p
If you were using number 2 you wouldn't really need app.controller('Ctrl', myCtrl);. You can reference a controller from ng-controller even if it isn't defined with module.controller(...)
You might want to also consider making your controller as much like "classes" as javascript will allow and using the 'controller as' syntax.
var myApp = angular.module("myApp", []);
myApp.MyCtrl = function($log) {
this.$log = $log;
}
myApp.MyCtrl.prototype.sayHello = function() {
this.$log('hello');
}
There are cases where it may be useful to have your controllers globally accessible. Like if you want to create an instance of your controller with $injector.instantiate() you need to have access to its constructor (although you could use the $controller service to accomplish the same goal using a string).
var myCtrl = $injector.instantiate(myApp.MyCtrl); //Works
var myCtrl2 = $injector.instantiate('MyCtrl'); //Doesn't Work
But if you don't have a specific use case for making your controllers global you should probably encapsulate them with module.controller(...);.
This answer also advocates using module.controller() https://stackoverflow.com/a/13363482/373655

Categories

Resources