I am trying to inject a service in app.config as illustrated in Inject service in app.config. However, minification breaks the app. How to overcome this?
This doesn't work with minification:
app.config(['$routeProvider',function ($routeProvider) {
$routeProvider
.when('/',
{
templateUrl: "partials/editor.html",
controller: "AppCtrl",
resolve: {
//Following method doesn't work with minification
data: getData
}
})
function getData (dbService) {
return dbService.getData();
}
}]);
Please note the following code doesn't work: (Typescript does not allow compilation)
['dbService',function getData(dbService){
return dbService.getData();
}]
In order to safeguard against minification, you need to annotate (see Dependency Annotation here) the data function like you did with the config function.
There are two ways to do this.
1.
Instead of passing a function, pass an array with the names of the dependencies and the function
app.config(['$routeProvider',function ($routeProvider) {
$routeProvider
.when('/',
{
templateUrl: "partials/editor.html",
controller: "AppCtrl",
resolve: {
//annotate this to prevent against minification
data: ['dbService', getData]
}
})
function getData (dbService) {
return dbService.getData();
}
}]);
2.
Add your dependencies to a $inject property on your function
app.config(['$routeProvider',function ($routeProvider) {
$routeProvider
.when('/',
{
templateUrl: "partials/editor.html",
controller: "AppCtrl",
resolve: {
//function annotated below
data: getData
}
})
//annotate this function with $inject so proper dependencies injected after minification
getData.$inject = ['dbService'];
function getData (dbService) {
return dbService.getData();
}
}]);
Related
This code works but not when minified... What should I do?
I get this error Error:
$injector:strictdi
Explicit annotation required
// app.js
angular
.module('app', [route, 'templates']);
angular
.module('app')
.config(config);
function config($routeProvider, $locationProvider) {
$routeProvider
.when('/', {
templateUrl: 'home.html',
controller: 'HomeController',
controllerAs: 'vm'
});
$locationProvider.html5Mode(true);
}
angular
.module('app')
.controller('HomeController', HomeController);
function HomeController() {
var vm = this;
vm.header = 'Home';
}
// home.html
{{ vm.header }}
Angular tries to implicit loads dependencies by the arguments name and it works fine as long as the argument name is the same as the dependency you want to load.
For example,
function config($routeProvider, $locationProvider) {
...
}
This will trigger angular to inject the function with the $routeProvider and the $locationProvider but what happens if you minify the code to this:
function config(a, b) {
...
}
Angular will now try to inject the function with a and b (which does not exist). Therefore, you need to explicitly tell angular what dependencies you want to inject. You can either do it with inline bracket notation:
// bracket notation
angular
.module('app')
.config(['$routeProvider', '$locationProvider', config]);
function config($routeProvider, $locationProvider) {
...
}
... or alternatively with the $inject property:
// $inject property
angular
.module('app')
.config(config);
config.$inject = ['$routeProvider', '$locationProvider'];
function config($routeProvider, $locationProvider) {
...
}
Since you're using strict mode (most probably 'use strict' somewhere in code), you must explicitly inject the dependencies.
You can do like this
config.$inject = [$routeProvider, $locationProvider];
You can try this
angular
.module('app')
.config(config);
config.$inject = ['$routeProvider','$locationProvider'];
function config($routeProvider, $locationProvider) {
$routeProvider
.when('/', {
templateUrl: 'home.html',
controller: 'HomeController',
controllerAs: 'vm'
});
$locationProvider.html5Mode(true);
}
})();
(function () {
'use strict';
angular
.module('plunker')
.controller('HomeController',HomeController);
HomeController.$inject = [];
function HomeController() {
var vm = this;
}
})();
I am able to lazy load angularjs with the help of requirejs. But, how can I load modules that needs to be associated to the controller?
My example configuration in app.js looks like the following, loading all the providers and keeping a reference.
var app = angular.module('myApp', ['ui.router'])
var cacheProviders = {};
app.getProvider = function () {
return cacheProviders.$provide;
}
app.getCompileProvider = function () {
return cacheProviders.$compileProvider;
}
app.getControllerProvider = function () {
return cacheProviders.$controllerProvider;
}
app.getFilterProvider = function () {
return cacheProviders.$filterProvider;
}
app.config(['$stateProvider', '$urlRouterProvider', '$controllerProvider', '$compileProvider', '$filterProvider', '$provide',
function ($stateProvider, $urlRouterProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
(function () {
cacheProviders.$controllerProvider = $controllerProvider;
cacheProviders.$compileProvider = $compileProvider;
cacheProviders.$filterProvider = $filterProvider;
cacheProviders.$provide = $provide;
})();
var lazyCtrlLoad = function (controllerName) {
return ["$q", function ($q) {
var deferred = $q.defer();
require([controllerName], function () {
deferred.resolve();
});
return deferred.promise;
}];
}
$stateProvider.state('main.view2b', {
url: '/view2b',
templateUrl: 'forms/empl/searchEmplForm.html',
controllerAs: 'srchC',
controller: 'searchEmplCtrl',
resolve: {
loadOtherCtrl: lazyCtrlLoad('searchEmplCtrl')
}
})
In my other module, I am trying to register controllers, load services..
define([
'angular', 'angularResource'
], function (angular) {
angular.module('myApp')
.getControllerProvider()
.register(ctrl, ...)
But, while loading service below, I need access to $resource which is part of ngResource module in angularResource.
angular.module('myApp')
.getProvider().service('deptService', ['$resource', function ($resource) {
return $resource('/dept/:dept', {dept: '#_dept'});
}])
How can I load ngResource while initalizing the javascript controllers/services lazily?
Take a look to AngularAMD here. It allows you to load controllers in the ui-router without using lazyload. This AngularAMD is used to integrate requireJs and Angular.
$stateProvider
.state('home', {
url: '',
views: {
'#': angularAmd.route({
templateUrl: 'ngApplication/application/shared/layouts/basic/basicTplView.html',
controllerUrl: 'ngApplication/application/shared/layouts/basic/basicTplCtrl.js',
controller: 'basicTplCtrl'
}),
'header#home': angularAmd.route({
templateUrl: 'ngApplication/application/shared/layouts/header/headerView.html',
controllerUrl: 'ngApplication/application/shared/layouts/header/headerCtrl.js',
controller: 'headerCtrl'
})
},
});
Also, you are using requirejs, you can load all the dependencies for an specific controller using the define syntax of requireJs. Let's say you want to create a loginCtroller in a separately file, and this controller depends on another angular service:
define(['app', 'transformRequestAsFormPostService'], function (app) {
app.controller('loginCtrl', ['$scope', '$rootScope', '$sce', '$http', '$state', 'transformRequestAsFormPostService', function ($scope, $rootScope, $sce, $http, $state, transformRequestAsFormPost) {
$scope.login = function () {
/*do something here using the service*/
};
}]);
});
Here, the dependency called transformRequestAsFormPostService is another file, I defined it in the main.js (requireJs confifguration file) and it's defined using the same approach than the loginCtrol. Now I am using it in my project and its working so far so good.
Regards,
Ernesto
I'm just messing around with angular a bit and I built a simple task API. This api has assigned and accepted tasks. Now when building the app I have these routes:
TaskManager.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/:username/assigned-tasks', {
templateUrl: 'app/partials/assigned-tasks.html',
controller: 'TaskController'
}).
when('/:username/accepted-tasks', {
templateUrl: 'app/partials/assigned-tasks.html',
controller: 'TaskController'
}).
otherwise({
redirectTo: '/'
});
}]);
And here is the task controller I started building and then realized this was not going to work
TaskManager.controller('TaskController', ['$scope', 'AssignedTasksService', function($scope, AssignedTasksService)
{
$scope.tasks = [];
loadAssignedTasks();
function applyRemoteData( Tasks ) {
$scope.tasks = Tasks;
}
function loadAssignedTasks() {
AssignedTasksService.getAssignedTasks()
.then(
function( tasks ) {
applyRemoteData( tasks );
}
);
}
}]);
The getAssignedTasks funciton is just a function that runs a http get request to the api url and either returns and error or the api data
now as you can see the assigned tasks are automatically loaded once it hits the TaskController which is obviously a problem since I need to also be able to get accepted tasks. Now do I need to create a separate controller for accepted tasks or is there a way for maybe me to check the url from the controller and from there I can decide if I want to run the loadAssignedTasks function or the loadAcceptedTasks (which I haven't created yet). but it would just do the same thing as the loadAssignedTasks function but for the accepted tasks
As mentioned in the comments there are multiple ways to solve. All depending on current use case. But you should probably use seperate controllers to solve this problem. Also inject the data(tasks) into the controller rather than fetching them inside the controller. Consider the following example:
var resolveAssignedTasks = function(AssignedTasksService) {
return AssignedTasksService.getAssignedTasks();
};
var resolveAcceptedTasks = function(AcceptedTasksService) {
return AcceptedTasksService.getAcceptedTasks();
};
TaskManager.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/:username/assigned-tasks', {
templateUrl: 'app/partials/assigned-tasks.html',
controller: 'TaskController',
resolve: {
tasks: resolveAssignedTasks
}
}).
when('/:username/accepted-tasks', {
templateUrl: 'app/partials/assigned-tasks.html',
controller: 'TaskController',
resolve: {
tasks: resolveAssignedTasks
}
}).
otherwise({
redirectTo: '/'
});
}]);
Controllers:
TaskManager.controller('AssignedTaskController', ['$scope', 'tasks', function($scope, tasks)
{
$scope.tasks = tasks;
}]);
TaskManager.controller('AcceptedTaskController', ['$scope', 'tasks', function($scope, tasks)
{
$scope.tasks = tasks;
}]);
You could also by doing this use a single controller by merging the resolveFunctions into one function that returns the appropriate tasks depending on the current route. Hope this helps.
Hi I have following module, route and controller defined in one file called main.js
var mainApp = angular.module("mainApp", ["ngRoute", "ngResource", "ui"]).
config(function ($routeProvider) {
$routeProvider.
when('/addEmp', { controller: EmpCtrl, templateUrl: 'addEmp.html' }).
when('/addLoc', { controller: LocCtrl, templateUrl: 'newLocation.html' }).
otherwise({ redirectTo: '/' });
});
mainApp.factory("addEmp", ['$resource', function ($resource) {
return $resource('/api/addEmp/:id', { id: '#id' }, { update: { method: 'PUT' } });
}]);
mainApp.factory("addLoc", ['$resource', function ($resource) {
return $resource('/api/newLoc/:id', { id: '#id' }, { update: { method: 'PUT' } });
}]);
//Controllers
var EmpCtrl = function ($scope, $location, addEmp) {
//code here
};
var LocCtrl = function ($scope, $location, newLocation) {
//code here
};
What I am trying to do is organize this one file code into different files. I created script/controller folder where I want to have individual files for controller like
EmpCtrl.js and LocCtrl.js.
When I created the controller files and copied the controller code in it i get error of EmptCtrl and LocCtrl not defined.
Can you please tell me how I can set it up in different folders with appropriate path settings?
Thanks
try this
angular.module("mainApp", ["ngRoute", "ngResource", "ui"]).
config(function ($routeProvider) {
$routeProvider.
when('/addEmp', { controller: 'empCtrl', templateUrl: 'addEmp.html' }).
when('/addLoc', { controller: 'locCtrl', templateUrl: 'newLocation.html' }).
otherwise({ redirectTo: '/' });
});
//in empCtrl.js file
angular.module('mainApp').controller('empCtrl', ['$scope', '$location', 'addEmp',
function ($scope, $location, addEmp) {
//code here
}]);
// same for locCtrl
In your html make sure to include your mainApp.js file before you empCtrl and locCtrl scripts
the key is that you use string names to refer to your controller in your rout configs
{ controller: 'empCtrl' ... }
instead of
{ controller: EmpCtrl ... }
I would recommend that you use require.js to modularize your Angular project, you won't have to worry about order of script includes and it will result in a cleaner project
I'd like to have my angularFire collection resolve on route loading. Something like:
App.config ($routeProvider, angularFireProvider) ->
$routeProvider.when '/test',
templateUrl: 'views/test.html'
controller: 'TestCtrl'
resolve: angularFireProvider.resolve 'testItems'
Is it possible to do this?
I'm not exactly sure why you want the collection to resolve on route loading, as opposed to in the controller - could you elaborate? For example, the following would work too:
App.config ($routeProvider, angularFireProvider) ->
$routeProvider.when '/test',
controller: 'TestCtrl'
function TestCtrl($scope, angularFire) {
angularFire("https://<example>.firebaseio.com", $scope, "collection").
then(function() {
// The collection has been resolved and available in $scope.collection
});
}
Is it mainly a matter syntactic convenience or am I missing functionality you want in the above?
Update: For the value to be resolved before the $routeChangeSuccess event is fired:
App.config(['$routeProvider', 'angularFire', function($routeProvider, angularFire) {
$routeProvider.when("/test", {
templateUrl: 'views/test.html'
controller: 'TestCtrl',
resolve: {collection: angularFire("https://<example>.firebaseio.com")}
});
}]);
function TestCtrl(collection) {
// collection has already been resolved to its value.
}