AngularJS component with externally hosted templateUrl? - javascript

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
});

Related

How to separate a component's controller function into a different file?

I have been searching and searching, and maybe I have been using the wrong terms on my search, but I couldn't find exactly what I was looking for. I am using Angular 1.5 with components.
Say I have a component A:
var module = angular.module('myApp');
module.component('A', {
templateUrl: 'template.html',
controller: function(){}
});
Now this is very simple. I also know that I can do the following, as long as 'myControllerFunction' is in the same file.
var module = angular.module('myApp');
module.component('A', {
templateUrl: 'template.html',
controller: myControllerFunction
});
function myControllerFunction(){}
Now what do I do if I want to have a file only for 'myControllerFunction'? How do I access that function inside that file from within my component?
I know that using Node.js is easy to use exports and require(), but I am not quite familiar what to do using AngularJS.
Any help would be very much appreciated! Thank you!
you can refer the controller from your module like below :
module.component('A', {
templateUrl: 'template.html',
controller: 'YourController'
});
Then in the another file you can define controller with the method.
function() {
'use strict';
angular.module('app').controller('YourController',
YourController);
YourController.$inject = [ '$scope', '$http', '$window',
'$rootScope' ];
function YourController($scope, $http, $window, $rootScope) {
function yourcontrollerfunction() {
}
yourcontrollerfunction();
}
})();
yourcontrollerfunction function will automatically called when controller is loaded. Controller will be loaded when your module is loaded.

AngularJS 1.3.13 - Service Provider Passed to UI-Router Config Not Resolving

I know I can't dependency inject a factory into a config as they only take providers, which as I understand it are just a fully customizeable services that factories and services inherit. So I thought I'd try creating a bunch of small route providers so I didn't have to add commonly reused resolve actions as anonymous functions in variables within the config, which just gets messy after awhile.
Provider
angular.module('project.app.service', [])
.provider('UserAuthService', function () {
this.$get = ['UserResource', function (UserResource) {
return {
getUser: getUser
};
function getUser() {
return UserResource.auth().$promise;
}
}];
}),
...
Routes Including Provider Module
angular.module('project.app.route', ['project.app.service'])
.config(['$stateProvider', '$urlRouterProvider', 'UserAuthServiceProvider',
function ($stateProvider, $urlRouterProvider, UserAuthServiceProvider) {
$stateProvider
...
.state('dashboard.index', {
url: "/map",
templateUrl: "templates/dashboard/map/map.html",
controller: 'MapController',
controllerAs: 'mapCtrl',
resolve: {
UserAuth: UserAuthServiceProvider.getUser()
}
})
...
});
The docs state that only the $get function can be injected, but it throws this error even though it appears like it should work:
TypeError: UserAuthService.getUser is not a function
This seems like it should work from the docs and examples, and with credit to the comments below for providing help and brainstorming the issue.
Is there a single line resolve parameter solution to make this more modular, or should I just drop anonymous functions at the top of the ui-route config, that inject a factory, and forget about this?
You are only supposed to use Providers to configure your services while your application is configuring/initializing (during module.config) with anything it needs prior to being created. You should not use it anywhere else.
If there's nothing to configure on your provider (you supply nothing but $get, which is used by Angular to actually create the singleton when it's needed at runtime), then there's no reason to inject it into .config (and you also just simplify by using module.factory instead of module.provider).
UI-Router will resolve dependencies required by any items in a state's resolve. When the resolve item is a function, it'll try to resolve any dependencies in that function by param name (known as implicit annotation). Or you can use the inline array annotation.
Example (using implicit annotation)
angular.module('project.app.route', ['project.app.service'])
.config(['$stateProvider', '$urlRouterProvider', 'UserAuthServiceProvider',
function ($stateProvider, $urlRouterProvider, UserAuthServiceProvider) {
// configure your UserAuthServiceProvider, if there is anything.
$stateProvider
.state('dashboard.index', {
url: "/map",
templateUrl: "templates/dashboard/map/map.html",
controller: 'MapController',
controllerAs: 'mapCtrl',
resolve: {
UserAuth: function(UserAuthService) { return UserAuthService.getUser(); }
}
});
});
Example (using inline array annotation)
angular.module('project.app.route', ['project.app.service'])
.config(['$stateProvider', '$urlRouterProvider', 'UserAuthServiceProvider',
function ($stateProvider, $urlRouterProvider, UserAuthServiceProvider) {
// configure your UserAuthServiceProvider, if there is anything.
$stateProvider
.state('dashboard.index', {
url: "/map",
templateUrl: "templates/dashboard/map/map.html",
controller: 'MapController',
controllerAs: 'mapCtrl',
resolve: {
UserAuth: ['UserAuthService', function(UserAuthService) { return UserAuthService.getUser(); }]
}
});
});
Which should you use, implicit or inline array annotation? Well, it depends on how your project is built. If your source is not minified (it should be, for production builds) or you have some module in your build pipeline that automatically converts implicit to inline array annotations, use implicit. Otherwise, use inline array.

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.

Angular controller is not instantiated after minification

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

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