Angular controller is not instantiated after minification - javascript

My original source code looks like this:
(function(angular) {
'use strict';
/* #ngInject */
function config($stateProvider) {
$stateProvider
.state('bar.foo', {
url: '/foo',
templateUrl: 'foo-view.html',
controller: 'FooController',
controllerAs: 'vm'
});
}
/* #ngInject */
function FooController() {
window.alert('foo');
}
angular
.module('my.foo', [])
.config(config)
.controller('FooController', FooController);
}(angular));
It works fine this way.
Then I minify it using ngAnnotate and UglifyJS, and get this code (formatted to easier reading):
function (a) {
'use strict';
function b(a) {
a.state('bar.foo', {
url: '/foo',
templateUrl: 'foo-view.html',
controller: 'FooController',
controllerAs: 'vm'
})
}
function c() {
window.alert('foo')
}
b.$inject = ['$stateProvider'],
a.module('my.foo', [])
.config(b)
.controller('FooController', c)
}(angular),
And, it does not work. When I go to the state bar.foo the controller do not get called. Others controllers and router states works fine. This one seems to get muted, the code inside the controller function is not called.
The console do not show any errors.
I do not know what to do or where to look at.
The entire JavaScript is bigger and is a concatenation of several small files. The single minified JS file has controllers before and after this one that works fine.
Debugging the minified file shows that the JavaScript is issuing the module definition calls to AngularJS, so the module, config and controller methods of Angular are called.
What should I search or check in the code? Am I missing something?

My problem was that I'm also compiling Jade templates into HTML. And my build settings for production environment is a little different than for development. Jade were outputting <div ui-view="ui-view"> instead of an empty attribute, preventing the ui-router to load the template into the parent view, causing the controller to not being instantiated.
The minification of the JavaScript was not the problem. Sorry.
These questions helped me out:
ui-router nested route controller isn't being called
Grunt jade compiler filling out empty attributes

Related

AngularJS component with externally hosted templateUrl?

I have an AngularJS application which I am loading as a plug in into another page with a different path. Therefore, my template URLs must be fully qualified in order for them to resolve to the correct file. However, I am receiving Error: $sce:insecurl
Processing of a Resource from Untrusted Source Blocked.
I tried using resourceUrlWhitelist, but this didn't make my error go away, so I thought I would try trustAsResourceUrl. However, I don't know how to combine that with my component's definition.
Here is my component:
angular
.module('synthApp')
.component('foo', {
templateUrl: 'http://example.com/app/components/main.template.html',
controller: MainController
});
function MainController() {
...
}
I tried the following but received an error that $sce is unknown:
angular
.module('synthApp')
.component('foo', ['$sce', {
templateUrl: $sce.trustAsResourceUrl('http://example.com/app/components/main.template.html'),
controller: MainController
}]);
function MainController() {
...
}
What is the proper way to use trustAsResourceUrl in this situation? Thanks.
component accepts a plain object. It cannot be used for DI like directive. .component('foo', ['$sce', { ... }]) isn't a correct syntax for any kind of DI (not just Angular) because it doesn't involve a function where a dependency could be injected.
As explained in this answer, templateUrl can be DI-enabled function.
It should be:
angular
.module('synthApp')
.component('foo', {
templateUrl: ['$sce', function ($sce) {
return $sce.trustAsResourceUrl('http://example.com/app/components/main.template.html');
}],
controller: MainController
});

$routeParams not populating

Some I'm new to routing and single page web apps, but I've been trying to learn Angular correctly. There's some trouble I'm experiencing with it however and a few weird questions. I followed a guide on structuring your directory and mine looks something like this:
app/
components/
profile/
profile.html
ProfileModel.js
ProfileController.js
ProfileFactory.js
app.module.js
app.routes.js
My main module is located in app.module.js and is dependency injected with ngRoute and profile.app (the module for profile view from ProfileModel.js). It is declared like this:
angular
.module('app', ['ngRoute', 'profile.app'])
.controller('MainController', MainController);
function MainController() {
this.message = 'This is the home page';
}
Then in my app.routes.js file, I have declared all the routes the applications needs (so far only one, which is profile):
angular
.module('app')
.config(routeConfig);
routeConfig.$inject = ['$locationProvider', '$routeProvider'];
function routeConfig ($locationProvider, $routeProvider) {
$routeProvider
.when('/user/:user_id', {
templateUrl: '/app/components/profile/profile.html',
controller: 'ProfileController',
controllerAs: 'profile'
}
)
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
}
This is my ProfileController.js:
angular
.module('app.profile')
.controller('ProfileController', ProfileController);
ProfileController.$inject = ['ProfileFactory', '$routeParams'];
function ProfileController(ProfileFactory, $routeParams) {
var vm = this;
vm.user_id = $routeParams.user;
console.log($routeParams.user_id);
vm = ProfileFactory.userProfile(vm.user_id); //Gets the profile of the user and sets to to vm
}
So I have two main questions. $routeParams.user_id is logged as nothing despite I have defined the route in app.routes.js. This is weird because I have an ng-view directive in my index.html (the HTML file that includes every single .js file). Which means that I should have immediate access to the routing parameters once the controller and its dependencies are instantiated. However, when I go to http://example.com/user/1, I get nothing logged (undefined).
My second question is I included ngRoute as a dependency in profile.app and my main module (in which profile.app is a dependency). However, I later removed ngRoute from profile.app as a dependency, yet I left the injection of $routeParams inside my ProfileController and AngularJS didn't complain at all. This seems weird because the dependency is no longer explicitly present inside profile.app. So how come I can still seemingly inject $routeParams despite not having ngRoute in my profile.app module? Is it because it is getting ngRoute from the main module?
I believe the problem is that you are setting controllerAs to 'profile' but then in the controller you write var vm = this;
These two need to be the same so you could write controllerAs: 'vm', or var profile = this;

resolve not running before entering route

this is a jsbin showing the problem, however, I will explain it here. I am trying to use resolve to run some code before entering a route, however, it doesn't run (as the console.log statement provides more detail about). (Since it doesn't run, I'm not sure if I'm setting what would be the return value from resolve in the template correctly). Why isn't resolve running?
var app = angular.module('jsbin', ['ngRoute'])
.config(function ($routeProvider) {
'use strict';
console.log("this runs");
var routeConfig = {
controller: 'DemoCtrll',
resolve: {
store: function (demoStorage) {
console.log("this doesn't log");
return demoStorage;
}
}
};
$routeProvider
.when('/', routeConfig)
.otherwise({
redirectTo: '/'
});
});
app.controller('DemoCtrl', function($scope, $routeParams, store) {
this.name = store;
});
app.factory('demoStorage', function () {
'use strict';
return "World";
});
html
<body ng-app="jsbin">
<div ng-controller="DemoCtrl as demo">
<h1>Hello {{name}}</h1>
</div>
Some changes you would need:
You cannot use ng-controller directive along with ng-view's route bound controller, i.e set controller alias in the controller property of the route config controller: 'DemoCtrl as demo',.
When you use ng-controller directive to instantiate the controller it does not know what is store provider, because no such provider/service exists. It is a special dependency injected by the angular router and only the router knows how to inject store as dependency in your controller. That is the reason for the error when you used ng-controller.
You need to define a template html or a template and bind it to templateUrl property on the routeConfig. i.e templateUrl:'demo.html',
Remove ng-controller from the view.
You need to use ng-view directive to render the view. i.e in your index.html <ng-view></ng-view>
Since you are using controller alias you would refer to the property as demo.name in the view rendered by the route.
You also had a typo in the controller name #DemoCtrl1.
Overall it will look like this.
Define your template in say Demo.html (In the demo example i have used the convenient script type="text/ng-template")
<div>
<h1>Hello {{demo.name}}</h1>
</div>
Set the routeConfig:
var routeConfig = {
templateUrl:'demo.html',
controller: 'DemoCtrl as demo',
resolve: {
store: function (demoStorage) {
console.log("this doesn't log");
return demoStorage;
}
}
};
JSBin
Also see this answer though it uses angular ui-router, but the concept is the same.

AngularJS - Why are dependencies with modules necessary?

Can you tell me why it is recommended to add dependency modules to the creation of a module?
I've tried to run the code without adding the modules TodoApp.controllers and TodoApp.services. Even though I removed them, everything ran perfectly .. (I call a service via those two files)
app.js
// Injects all the needed modules
var TodoApp = angular.module("TodoApp", [
"ngRoute",
"ngResource",
"TodoApp.controllers",
"TodoApp.services"
]).
config(function ($routeProvider) {
$routeProvider.
when('/', { controller: "listCtrl", templateUrl: 'list.html' }).
otherwise({ redirectTo: '/' });
});
Update
Thank you for your response! :)
Unfortunately, those files don't share a common variable:
That was actually the reason why I didn't understand the set of problem. For me, this code gets stored in nowhere. Could it be because of the dot notation in the naming?
controller.js
angular.module('TodoApp.controllers', []).
controller('listCtrl', function ($scope, $location, todoApiService) {
$scope.todos = todoApiService.getMyTodos().query();
});
services.js
angular.module('TodoApp.services', []).
factory('todoApiService', function ($resource) {
var todoApi = {};
todoApi.getMyTodos = function () {
return $resource('/api/todo/:id', { id: '#id' }, { update: { method: 'PUT' } });
};
return todoApi;
});
You do not need to inject controllers & services since they have to be part of a module anyway.
You could have them labeled like so in the other files:
TodoApp.controllers(/* ... */)
TodoApp.services(/* ... */)
They are already attaching themselves to the module because of the TodoApp variable (which in-turn is angular.module('TodoApp').
It looks like you call the module directly each time:
angular.module('TodoApp.controllers', []).
controller('listCtrl', /* etc etc */ );
Each way, you are simply adding more things (in typical dot notation) to your overall Module, and angular understand it's TodoApp since you are starting it with that.
But sometimes you'll see people inject other modules, in a way inheriting the modules controllers / directives / services / etc.
var HelperModule = angular.module('HelperModule', /*...*/);
HelperModule.directive('headerLinks', /*...*/);
HelperModule.factory('BaseFactory', /*...*/);
// etc etc
var TodoApp = angular.module("TodoApp", ["HelperModule"]); // <-- Module injected
// TodoApp now contains all the directives/etc from that module!
Hope that helps!

How to split AngularJS application into smaller modules and handle routing correctly?

What would be the best way to split AngularJS application into smaller pieces/module? For example if I have a blog post and commenting enabled for that, I think I could break it down to modules like "posts" and "comments"(?) (maybe not the best example, but the idea is to split the application logic into separate modules rather than building a huge one-module-app).
I've tried to bootstrap both modules in the separate DOM nodes and use routing in both of the modules accordingly. There are few problems:
As a "single-page" application I'm bootstrapping comments module to be used even on the front page even though it's not used there.
Since I'm not able to use multiple ng-views inside ng-app, I'm forced to write all the wrappers for my modules in the index.html view and bootstrap them? Should it be like that? Seems a bit wrong. How/where should I bootstrap those?
Are there any tips for the routing? Should I spread those in the modules or should I combine them all together somehow? (creating one "blog" module to include "posts" and "comments" modules as dependencies would still make it hard to define for example the "/post/:id" routing..?)
index.html
<div class="post"><ng-view></ng-view></div>
<div class="comments"><ng-view></ng-view></div>
javascript.js
angular.module('posts', []).config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
'template': 'Showing all the posts',
'controller': 'postCtrl
})
.when('/post/:id', {
'template': 'Showing post :id',
'controller': 'postCtrl
});
}]);
angular.module('comments', []).config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/post/:id', {
'template': 'Showing post :id comments',
'controller': 'CommentsCtrl'
});
}]);
angular.bootstrap($('.post'), ['posts']);
angular.bootstrap($('.comments'), ['comments']);
I would divide the app in "view modules" and these sub modules.
Then I use the $routeProvider to switch between the views. I define the different routing config in each module.
If I need further submodules, I load these with ng-include.
/* App Module */
angular.module('MyApp', ['MyApp.home', 'MyApp.blog'])
.config( function myAppConfig ( $routeProvider ) {
'use strict';
$routeProvider.otherwise({ redirectTo: '/home' });
});
/* home Module */
angular.module('MyApp.home', [])
.config(['$routeProvider', function config( $routeProvider ) {
$routeProvider.when('/home', {
controller: 'HomeController',
template: '<p>This is my Home</p>'
});
}]);
I created a little repository on github to explain this.
You can define routes in the submodules:
angular.module('app', ['ngRoute', 'app.moduleX'])
.config(function($routeProvider, $locationProvider) {
$routeProvider.when('/home', {
templateUrl: 'partials/home.html',
controller: 'HomeCtrl'
});
//Handle all exceptions
$routeProvider.otherwise({
redirectTo: '/home'
});
})
angular.module('app.moduleX', []).config(function($routeProvider) {
$routeProvider.when('/settings', {
templateUrl: 'partials/settings.html',
controller: 'SettingsCtrl'
});
})
I also wrote a blog post about this topic.
We're doing something similar with a portal app and sub-apps. A few things we've discovered:
Only one "app" can have routes and routeParams. Because of this, if the "sub-app" needs access to the $routeParams, you either have to go "old school" for URL parsing, or use an event service.
Speaking of events, there is no Angular service for the apps to communicate, so you'll need to roll your own event service talking to root scope for both apps and inject it into both apps.
I can't see where we used ng-view for the "sub-app". Apparently bootstrapping directly to an element works similarly.
Because only one app can have routes, the apps should be bootstrapped in order. So something like this:
$( function () {
$.when(angular.bootstrap($('.post'), ['posts'])).done( function() {
console.log('POSTS: bootstrapped');
//Manually add the controller to the comments element. (May or may not be
//necessary, but we were doing something that required it to work.)
$('.comments').attr('ng-controller', 'CommentsCtrl');
$.when(angular.bootstrap($('.comments'), ['comments'])).done( function() {
console.log('COMMENTS: bootstrapped');
});
});
});
I hope you can use "ui-router" routing module.
Here is good tutorial for this http://www.ng-newsletter.com/posts/angular-ui-router.html

Categories

Resources