I have created an app based on ng-boilerplate. All works just fine. However, now I am coming close to deployment, I want to compile and minify the code. This is straighforward enough using grunt compile, but of course the app breaks when minified. I didn't expect anything less!
I have injected dependencies into my controllers like this:
var appCtrl = app.controller('AppCtrl', function AppCtrl($scope, $rootScope, helpService, userService, menuService, $location) {... body ...});
appCtrl.$inject = ['$scope', '$rootScope', 'helpService', 'userService', 'menuService', '$location'];
I have also tried it like this:
var appCtrl = app.controller('AppCtrl', ['$scope', '$rootScope', 'helpService', 'userService', 'menuService', '$location',
function AppCtrl($scope, $rootScope, helpService, userService, menuService, $location) {... body ...}]);
All I ever get is an error like this: Error: Unknown provider: aProvider <- a
I have also looked at my services and injected the dependencies in a similar way to the second method above, but then I started getting errors in the program even when it was not minified. It was telling me $q has no method defer()!
app.factory('checkAuth', ['$q', '$location', '$filter', '$rootScope', function ($q, $location, $filter, $rootScope) {...body...}]);
My question is, what am I missing? Am I doing the dependency injections correctly? Is there somewhere else needing DI?
Thanks!
EDIT:
Just found this: Angular.module minification bug
It hasn't completely fixed the problem... I now get the error:
TypeError: Object #<error> has no method 'slice'
but at least this is away from Error: Unknown provider: aProvider <- a :-)
Your second approach is using angular's inline annotation method, and that should be able to be minified correctly.
Other approaches will not work because of how Angularjs handles it's dependency injection - it will look at the parameter's name to determine the provider. Your minifier will change the parameter name from $scope to a, and Angjuar will try to find a provider for a. The inline style fixes this at the cost of more typing and a bit more maintenence. (There's a tool that will alter your angular code so that it can be minified by grunt here: https://github.com/btford/ngmin )
I would expect that your issue is that not all of your controllers or modules were altered to use the inline style, and you continued to get the error. Make sure you change all of it - read up on Angularjs DI, especially inline annotation.
OK - Fixed it: I changed the filter to return text rather than a boolean value like this:
function orgFilter(item, args) {
if ((args.searchString !== "" && item["strName"].indexOf(args.searchString) == -1) &&
(args.searchString !== "" && item["strShortName"].indexOf(args.searchString) == -1) &&
(args.searchString !== "" && item["strCode"].indexOf(args.searchString) == -1)
) {
// return false;
return 'continue_coreloop';
}
// return true;
return 'add_result';
}
... and then changed the compileFilter() function to match the text
var filterBody = filterInfo.body
.replace(/return \'continue_coreloop\'[;}]/gi, "{ continue _coreloop; }")
.replace(/return \'add_result\'[;}]/gi, "{ _retval[_idx++] = $item$; continue _coreloop; }")
.replace(/return ([^;}]+?);/gi,
"{ if ($1=='add_result') { _retval[_idx++] = $item$; }; continue _coreloop; }");
Uglify doesn't change the text values so the match will still be true.
Related
I'm trying to inject my factory into my controller and I'm getting this error from AngularJS:
Error: $injector:unpr Unknown Provider
I have looked through almost all of the questions on here and still cannot find a solution to my problem. I believe my controller and factory and declared correctly and the injection is correct but it looks like this isn't the case.
My factory code is as follows:
var app = angular.module('test', []);
app.factory('processingFactory', function () {
var factory = {};
factory.newTest = function() {
console.log("TEST");
}
return factory;
});
This is then injected into the controller which looks like this:
angular.module("test", ["angularModalService", "anguFixedHeaderTable",
'angular-loading-bar', "ngResource", "agGrid",
'ui.tree']).controller("dashboardController", [
"$scope",
"$timeout",
"$http",
"$window",
"$interval",
"$resource",
"ModalService",
"$filter",
'$q',
'processingFactory',
function($scope, $timeout, $http, $window, $interval, $resource,
ModalService, $filter, $q, processingFactory) {
//other code removed
$scope.newWorkorder = processingFactory.newWorkorder;
}
]);
This function is called through a button click on the web page. All of the files needed are in script tags on this html page. I am fairly new to angular so this could be a simple error or something I am not aware of.
Calling angular.module with an array as the second argument declares a module, which can only happen for any given module name. You are declaring the module twice (once in your controller code, and again in your factory code).
Try changing the first part of your factory code to:
var app = angular.module('test');
If you are doing the same thing elsewhere in the app you will need to remove the second argument there too, so that there is only one module declaration in the whole app.
if there are any dependencies for your module "test" why do not you have them declared in the first line itself like:
var app = angular.module("test", ["angularModalService", "anguFixedHeaderTable",
'angular-loading-bar', "ngResource", "agGrid",
'ui.tree']);
Then declare your controller like::
app.controler(...)
Things should work fine.
I would like to understand the difference between the declaration of MyOtherService and MyOtherComplexService. Especially what is the purpose of square bracket part? When to use them and when not?
var myapp = angular.module('myapp', []);
myapp.factory('MyService', function($rootScope, $timeout) {
return {
foo: function() {
return "MyService";
}
}
});
myapp.factory('MyOtherService', function($rootScope, $timeout, MyService) {
return {
foo: function() {
return "MyOtherService";
}
}
});
myapp.factory('MyOtherComplexService', ['$rootScope', '$timeout', 'MyService', function($rootScope, $timeout, MyService) {
return {
foo: function() {
return "MyOtherComplexService";
}
}
}]);
myapp.controller('MyController', function($scope, MyOtherService, MyOtherComplexService) {
$scope.x = MyOtherService.foo();
$scope.y = MyOtherComplexService.foo();
});
It enables AngularJS code to be minified. AngularJS uses parameter names to inject the values to your controller function. In JavaScript minification process, these parameters are renamed to shorter strings. By telling which parameters are injected to the function with a string array, AngularJS can still inject the right values when the parameters are renamed.
To add to Ufuk's answer:
ngmin - compiles your standard modules to min-safe modules
Angular's min-safe square bracket notation is cleary less convenient, because you have to type every dependency twice and argument order matters. There is a tool called ngmin which compiles your standard modules to min-safe modules, so you don't have to manage all those things by hand.
Angular + CoffeeScript
If you're using CoffeeScript the situation is even worse. You may choose between ngmin, which will destroy your source map, or if you want to write it out all by yourself you'll have to wrap your entire code with square brackets, which is super ugly.
angular.module('whatever').controller 'MyCtrl', ['$scope', '$http' , ($scope, $http) ->
# wrapped code
]
In my opinion this is not a CoffeeScript flaw, but a poor design decision of the Angular team, because it's against all JS/CoffeeScript conventions not to have the function as the last argument. Enough ranting, here is a little helper function to work around it:
deps = (deps, fn) ->
deps.push fn
deps
This is a very simple function that accepts two arguments. The first one is an array of strings containing your dependencies, the second one is your module's function. You may use it like this:
angular.module('whatever').controller 'MyCtrl', deps ['$scope', '$http'] , ($scope, $http) ->
# unwrapped code \o/
Just to exemplify what was already said, if you use the following syntax:
myapp.factory('MyService', function($scope, $http, MyService) { ... });
most of the JS minifiers will change it to:
myapp.factory('MyService', function(a, b, c) { ... });
since functions argument names usually can be renamed for shorter names. This will break the Angular code.
In Angular, to get your code minifiable in all minifiers, you use the bracket syntax:
myapp.factory('MyService', ['$scope', '$http', 'MyService', function($scope, $http, MyService) { ... }]);
that will be minified to:
myapp.factory('MyService', ['$scope', '$http', 'MyService', function(a, b, c) { ... }]);
Note that minifiers do not touch on strings so Angular will see the minified code and match arguments in order:
$scope = a
$http = b
MyService = c
To avoid this ugly square bracket syntax, you should use smart minifiers like ng-annotate.
As of now, ng-min is deprecated.
Use ng-annotate instead.
It is good practice to use ng-annotate in your build job so you don't have to deal with the min-safe / bracket notation when developing, as it makes the code harder to read and maintain.
There is a grunt-plugin and a gulp plugin available on npm.
I don't know why I am getting this error.
The error I am getting is Error: [ng:areq] Argument 'PreviewController' is not a function, got undefined.
Can someone help me out with this?
Also is there any other way to inject services in a controller?
My code is as follows:
(function() {
'use strict';
angular
.module('MyModule')
.controller('PreviewController' ['$scope','Service1','Service2',
function($scope, $http) {
$http.get("https://api.myjson.com/bins/30e2a")
.success(function(response) {
//Dummy data taken from JSON file
$scope.firstName = response.firstName;
$scope.lastName = response.lastName;
$scope.dateAdded = response.dateAdded;
});
//Functions have been defined. Functionality to be added.
$scope.cancelAndBack = function() {
window.history.back();
};
}]);
}());
You are defining you module incorrectly.
`angular.module('MyModule')`
Is looking for an already initialised module called 'MyModule'.
If you are creating a module from scratch you need to empty array. This would be more module dependencies.
`angular.module('MyModule', [])`
This is how angular knows the difference between, 'create an app' and 'get me an app'.
Finally services. Your using angulars array notation. That is so you can minify your javascript.
angularjs injection system works by name, that's how it can find the dependencies your require, that's also why you can list them in any order you like. However minifying your code changes your variable names and so breaks angular's injection.
The solution is to provide an array of strings telling angular the services you wish to inject and the order they are injected in.
So your array values and properties passed into your controller function must match.
Correct:
.controller('test', ["$scope", "$http", "myService", function( $scope, $http, myService){}]);
Incorrect: (myService won't be defined as its missing from the array)
.controller('test', ["$scope", "$http", function( $scope, $http, myService){}]);
I'm new to angular and have the following code.
angular.module('MyApp')
.controller('loginController', ['$scope', '$http', 'conditionalDependency',
function ($scope, $http, conditionalDependency{
}
I would like to have conditionalDependency loaded conditionally. Something like this
if(true)
{
//add conditionalDependency
}
How can this be done. I've seen this post . However, my requirement is that I have the dependency specified in function
Thanks in advance.
Not quite clear as to why you would have to have it in a named function like in your example but...
If you need conditional dependencies, I would suggest taking a look at the following:
Conditional injection of a service in AngularJS
I've used this method in a couple niche scenarios and it works quite well.
EXAMPLE:
angular.module('myApp').controller('loginController',
['$injector', '$scope', '$http',
function($injector, $scope, $http) {
var service;
if (something) {
service = $injector.get('myService');
}
});
You can use it even without injecting injector in your controller
if(something){
var injector = angular.element(document).injector();
var myService = injector.get('myService')
}
Use:
angular.injector().get('conditionalDep');
You can inject $injector once to your file and call $injector.get('dep');
I'm trying to use cookies ( set and retrieve), I have this code copies from a site and changed it, but I wouldn't work and all my angular parts stop working.
This is a sample of angular website
can you tell me where the problem is?
var app = angular.module('test', ['ui.bootstrap'], ['ngCookies']);
app.controller('ExampleController', ['$cookieStore', function ($scope, $cookieStore) {
// Put cookie
$cookieStore.put('myFavorite', 'oatmeal');
// Get cookie
$scope.itemValue = $cookieStore.get('myFavorite');
// Removing a cookie
//$cookieStore.remove('myFavorite');
}]);
and usage is :
<span ng-controller="ExampleController">{{itemValue}}</span>
it gives me this error
Error: [$injector:modulerr] http://errors.angularjs.org/1.3.0-beta.5/$injector/modulerr?......
You're declaring your module wrong, the second parameter should be an array of dependencies, but you're passing each dependency as it's own separate array. It should be:
var app = angular.module('test', ['ui.bootstrap', 'ngCookies']);
You're using a "minification safe" array for your controller, but you're only including $cookieStore, not $scope, it should be:
app.controller('ExampleController', ['$scope', '$cookieStore', function ($scope, $cookieStore) {
...
}]);
Your syntax is incorrect, go through the docs to find the correct syntax for angular.