Angular: Adding custom script/css to partials via controller - javascript

I am trying to add custom page CSS/JS to my partials via controllers, i have created a custom directive for it but when i am loading the CSS using it it gets unnecessary parameters which i don't want, and when i load JS using it my JS does not load the the right time(meaning the init functions written in the page gets called before, leaving the error)
DIRECTIVE
app.directive('head', ['$rootScope','$compile',
function($rootScope, $compile){
return {
restrict: 'E',
link: function(scope, elem){
var html = '<link rel="stylesheet" ng-repeat="(routeCtrl, cssUrl) in routeStyles" ng-href="{{cssUrl}}" />';
elem.append($compile(html)(scope));
scope.routeStyles = {};
$rootScope.$on('$routeChangeStart', function (e, next, current) {
if(current && current.$$route && current.$$route.css){
if(!Array.isArray(current.$$route.css)){
current.$$route.css = [current.$$route.css];
}
angular.forEach(current.$$route.css, function(sheet){
delete scope.routeStyles[sheet];
});
}
if(next && next.$$route && next.$$route.css){
if(!Array.isArray(next.$$route.css)){
next.$$route.css = [next.$$route.css];
}
angular.forEach(next.$$route.css, function(sheet){
scope.routeStyles[sheet] = sheet;
});
}
});
}
};
}
]);
APP CONTROLLER
app.config(['$routeProvider', function($routeProvider){
$routeProvider
.when('/some/route/1', {
templateUrl: 'partials/partial1.html',
controller: 'Partial1Ctrl',
css: 'css/partial1.css'
})
.when('/some/route/2', {
templateUrl: 'partials/partial2.html',
controller: 'Partial2Ctrl',
css: ['css/partial2_1.css','css/partial2_2.css']
})
}]);
Output I am getting
CSS
<link rel="stylesheet" ng-repeat="(routeCtrl, cssUrl) in routeStyles" ng-href="css/createtroll.css" class="ng-scope" href="css/partial2.css">
HTML
<script ng-repeat="(routeCtrl, scriptUrl) in routeScript" ng-src="js/page.css" class="ng-scope" src="css/scroll.js"></script>
I want know how to remove the ng-directives form the output and how to load js before document.ready so that I don't get the error. Any help is much appreciated.

it seems that you are looking for a way to lazy-load some aspect of our AngularJS application by loading JS and CSS files asynchronously.
You cannot use controllers/directives for loading your files because your app has already been bootstrapped.
You'll need some kind of file/module loader, for example you can use RequireJS - a JavaScript file and module loader.
there are already a lot of examples and implementation of RequireJS and AngularJS.
Example:
require.config({
baseUrl: "js",
paths: {
'angular': '.../angular.min',
'angular-route': '.../angular-route.min',
'angularAMD': '.../angularAMD.min'
},
shim: { 'angularAMD': ['angular'], 'angular-route': ['angular'] },
deps: ['app']
});
Reference:
http://marcoslin.github.io/angularAMD/#/home
http://www.startersquad.com/blog/angularjs-requirejs/
http://www.bennadel.com/blog/2554-Loading-AngularJS-Components-With-RequireJS-After-Application-Bootstrap.htm
http://requirejs.org/

Related

How to load Angular translate with external modules loaded with oclazyload?

I'm working with oclazyload for the load by demand of external modules and I need to translate the content, so I'm using angular-translate but the translation does not work because the code that adds the part is within the controller that is loaded asynchronously.
angular
.module('main')
.controller('invoicesCtrl', invoicesCtrl);
function invoicesCtrl($scope, $translatePartialLoader, $translate) {
$translatePartialLoader.addPart('invoices');
$translate.refresh();
...
}
The main app loads the translations here:
(function () {
angular.module('main', [
'ui.router', // Angular flexible routing
'oc.lazyLoad',
'ngResource',
'pascalprecht.translate' // Angular-translate
]);
angular.module('main').config(function ($translateProvider, $translatePartialLoaderProvider) {
$translateProvider.useLoader('$translatePartialLoader', {
urlTemplate: './i18n/{part}/{lang}.json'
});
$translateProvider.preferredLanguage('en_US');
$translateProvider.forceAsyncReload(true);
$translatePartialLoaderProvider.addPart('secure');
// Enable escaping of HTML
$translateProvider.useSanitizeValueStrategy('escape');
});
})();
And for the lazy load for invoicesCtrl and other controllers I use (this is working fine):
function configState($stateProvider, $urlRouterProvider, $compileProvider) {
function resolveUrl(path){
var loadUrl = { // Any property in resolve should return a promise and is executed before the view is loaded
loadModule: ['$ocLazyLoad', function($ocLazyLoad) {
// you can lazy load files for an existing module
return $ocLazyLoad.load(path);
}]
};
return loadUrl;
}
}
I need to load this addPart before the controller itself.
$translatePartialLoader.addPart('invoices');
$translate.refresh();
Maybe an approximation can be to use an event but I'm new in javascript and I do not know how to implement it.
I solved using in the main app controllert a new function called when the link in the navigation is clicked:
<li ng-class="active" ng-click="getModuleLang('invoices')">
main.js file:
angular
.module('main')
.controller('appCtrl', appCtrl);
function appCtrl($scope, $translate, $translatePartialLoader) {
// Secure translate module added to language lazy load
$translatePartialLoader.addPart('secure');
$translate.refresh();
// Function that changes the language based on the headers language buttons
$scope.changeLanguage = function (langKey) {
$translate.use(langKey);
};
// Load the language modula based on the navigation element clicked
$scope.getModuleLang = function(module){
$translatePartialLoader.addPart(module);
$translate.refresh();
}
};

Angular Foundation Directives templateUrl Property

I'm using angular-foundation (http://pineconellc.github.io/angular-foundation/) that leverages angular directives for implementing Foundation things like Modals, Tabs and other front-end services.
The problem I'm having is that all of the directives have pre-populated "templateUrl" attributes that do not exist on my server. Also, because this is an external dependency managed with Bower, I can't manually update the library. I pulled out the following directive from the library:
angular.module('mm.foundation.tabs', [])
.directive('tabset', function() {
return {
restrict: 'EA',
transclude: true,
replace: true,
scope: {},
controller: 'TabsetController',
templateUrl: 'template/tabs/tabset.html',
link: function(scope, element, attrs) {
scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;
scope.type = angular.isDefined(attrs.type) ? scope.$parent.$eval(attrs.type) : 'tabs';
}
};
})
from my module I claim a dependency on the mm.foundation module:
angular.module('foundationDemoApp', ['mm.foundation.tabs']).controller('TabsDemoCtrl', function ($scope) {
$scope.tabs = [
{ title:"Dynamic Title 1", content:"Dynamic content 1" },
{ title:"Dynamic Title 2", content:"Dynamic content 2" }
];
$scope.alertMe = function() {
setTimeout(function() {
alert("You've selected the alert tab!");
});
};
})
I'm currently using Gulp with gulp-html2js to bundle my templates. On the github README they mention customizing templates (https://github.com/pineconellc/angular-foundation) something about the $templateCache, but at this point I am not sure how to change the templateUrl defined in the library to my template's location.
Any guidance on this would be greatly appreciated!
Since no one answered, I'll give it a shot, it'll may help you, ossys (note to any moderator, feel free to remove my answer if I'm wrong).
If you bower installed the library, those templates should be somewhere in bower_components/angular-foundation, you should follow their example https://github.com/pineconellc/angular-foundation#customize-templates and get something like :
html2js: {
options: {
base: '.',
module: 'ui-templates',
rename: function (modulePath) {
var moduleName = modulePath.replace('bower_components/angular-foundation', '').replace('.html', '');
return 'template' + '/' + moduleName + '.html';
}
},
main: {
src: ['bower_components/angular-foundation/**/*.html'],
dest: '.tmp/ui-templates.js'
}
}
//or in gulp
gulp.src('bower_components/angular-foundation/**/*.html')
.pipe(html2js({
outputModuleName: 'ui-templates'
}))
.pipe(concat('ui-templates.js'))
.pipe(gulp.dest('./'))
With $templateCache i think it is possible like this to replace the template with ur own
for tab:
<script type="text/ng-template" id="template/tabs/tabset.html"
src="/path/to/your/template.html"></script>
Hey thank you guys for your answer and actually you both are correct.
My problem is I was thinking of the solution backwards. Rather than replacing the templateUrl parameter for each directive, in my case I instead need to rename the name of the template I'm compiling. For example....
I am using gulp and html2js to bundle my template files. Before importing angular-foundation my templates task looked something like this:
gulp.task('templates', function (cb) {
gulp.src('path/to/tempalates/**/*.html')
.pipe(html2js({
outputModuleName: 'my.templates',
base: './src/web/',
}))
.pipe(concat('templates.js'))
.pipe(insert.prepend("var angular = require('angular');"))
.pipe(gulp.dest('./src/web/'))
.on('end', done);
};
});
I went ahead and uploaded the html template directory from angular-foundation github repository, and put that in the path 'path/to/foundation/template'. I then updated the templates gulp task so that it would rename the 'path/to/foundation/template' path to the path that is already coded in the directive. For example, if a directive is expecting a templateUrl of 'template/tab/tabs.html' and the location of the actual template is 'path/to/foundation/template/tab/tabs.html', then the rename function would replace 'path/to/foundation' with an empty string to make the path 'template/tab/tabs.html' during the build. My final gulp task now looks like:
gulp.task('templates', function (cb) {
gulp.src('path/to/tempalates/**/*.html','path/to/foundation/template/**/*.html'])
.pipe(html2js({
outputModuleName: 'my.templates',
base: './src/web/',
rename : function (modulePath) {
var moduleName = modulePath.replace('path/to/foundation/', '').replace('.html', '');
return moduleName + '.html';
}
}))
.pipe(concat('templates.js'))
.pipe(insert.prepend("var angular = require('angular');"))
.pipe(gulp.dest('./src/web/'))
.on('end', done);
};
});
So now all my templates are built with the path that angular-foundation expects them to be at, and I'm not stuck trying to manage template paths during run time.
I hope this helps others!

All AngularJS Controllers Not Loading with LazyLoad

I am trying to lazy load my controllers for my AngularJS app I built along side with requireJS. I have created a custom "lazyLoad" library that creates a resolve object in app.config() routes (also I am using ui-router). If I code the state (without my library) to look like so it works
define(['angular', 'lazyLoader', 'uiRouter'], function(angular, lazyLoader, uiRouter){
var app = angular.module('myApp', ['ui.router']);
app.config(function ($stateProvider, $urlRouterProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
window.lazy = {
controller: $controllerProvider.register,
directive: $compileProvider.directive,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service
};
$urlRouterProvider.otherwise('/');
$stateProvider
.state('campaigns', {
url:'/campaigns',
views: {
"top-nav" : {
templateUrl: 'views/home/top-nav.html',
resolve : {
load : ['$q', '$rootScope', function($q, $rootScope){
var d = $q.defer();
require(['../app/controllers/header-controller'], function() {
$rootScope.$apply(function(){
d.resolve();
});
});
return d.promise;
}]
}
},
"fullpage": {
templateUrl: 'views/home/index.html',
resolve : {
load : ['$q', '$rootScope', function($q, $rootScope){
var d = $q.defer();
require(['../app/controllers/home-controller'], function() {
$rootScope.$apply(function(){
d.resolve();
});
});
return d.promise;
}]
}
//controller: 'home-controller'
}
}
});
});
return app;
});
If I attempt to replace the resolve object with my library function it looks would look like this:
define(['angular', 'lazyLoader', 'uiRouter'], function(angular, lazyLoader, uiRouter){
and
.state('home', lazyLoader.route({
url:'/',
views: {
"top-nav" : {
templateUrl: 'views/home/top-nav.html',
controllerUrl: '../app/controllers/header-controller'
},
"fullpage": {
templateUrl: 'views/home/index.html',
controllerUrl: '../app/controllers/home-controller'
}
}
}));
lazyLoader.js
define(function () {
'use strict';
function LazyLoader() {}
LazyLoader.prototype.route = function(config){
var controllerPath;
if(config && config.views){
var singleView = Object.keys(config.views);
for(var i in singleView){
var viewName = singleView[i];
controllerPath = config.views[viewName].controllerUrl;
delete config.views.controllerUrl;
config.views[viewName].resolve = {
load : ['$q', '$rootScope', function($q, $rootScope){
var d = $q.defer();
require([controllerPath], function() {
$rootScope.$apply(function(){
d.resolve();
});
});
return d.promise;
}]
};
}
}
return config;
}
return new LazyLoader();
});
Example Controller
define(['app/module'], function (module) {
lazy.controller('header-controller', ['$scope', function ($scope) {
// stuff here
}]);
});
On a side note I plan on implementing something better than attaching lazy variable to window.
When I code the router like the first example it works. When I use my lazyLoader the one of the two views loads it's controller, the second view's controller's file is started to load (console.logs at the beginning show this) but it cannot resolve "module" in the example above.
link to error: AngularJS Error
Again this issue only happens when using my lazyloader which is producing the same resolve object that I have hard coded in for the version that works.
I have searched high and low and there are a lot of resources out there but I could not find anything that addressed this issue.
Any advice is appreciated!
You are taking too much pain to do lazy loading of controllers & services. There is simple approach to lazy load files with ocLazyLoad. This article might help you resolve the same issue.
https://routerabbit.com/blog/convert-angularjs-yeoman-spa-lazyload/
What you should do is
Add a reference of ocLayzLoad & updated JS files’ reference to load on demand from app.js or .html file of their views.
`bower install oclazyload --save-dev`
Now load the module ‘oc.lazyLoad’ in application. Update app.js file
angular
.module('helloWorldApp', [
'ngCookies',
'ngResource',
'ngRoute',
'ngSanitize',
'oc.lazyLoad',
])
Load JS file by adding reference of JS in .html file
<div oc-lazy-load="['scripts/controllers/about.js', 'scripts/services/helloservice.js']">
<div ng-controller="AboutCtrl as about">
Your html goes here
</div>
</div>
If you using Grunt, update Gruntfile to uglyfy, renamed file name & update references in the final .html or .js file.
On the 'myApp' module definition, shouldn't you be returning app variable instead of myApp?
And to avoid exposing lazy to window, you could define it as a property of app variable, this way when you define new functions, you require app first and you can use it:
app.js:
app.lazy = {
controller: $controllerProvider.register,
directive: $compileProvider.register,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service
};
...
return app;
controller.js:
define(['app'], function (app) {
app.lazy.controller('header-controller', ['$scope', function ($scope) {
// stuff here
}]);
});

Load Dojo module within Angular app

How to use a Dojo module within an Angular app while avoiding race conditions or other conflicts of the two module loading mechanisms of the two frameworks?
Basically you load Dojo first, then bootstrap Angular manually in the require-callback.
<script>
var dojoConfig = {
async: true,
baseUrl: '.',
packages: ['dojo', 'dojox']
};
</script>
<script src="dojo/dojo.js"></script>
Then load the following:
require([
'dojox/json/query', //or any other dojo module
], function (dojoJsonQuery) {
//dojo loaded
var app = angular.module('app', ['ngRoute'], function($routeProvider) {
$routeProvider.when('/', {
templateUrl: '/partials/main.html',
controller: MyCtrl
});
});
app.factory('jsonQuery', function jsonQuery() {
return dojoJsonQuery;
});
angular.bootstrap(document.body, ['app']);
});
function SetupCtrl($scope, jsonQuery) {
//...
}
Now, the important thing when using angular.bootstrap is to remove the ng-app="app"-attribute from your HTML.

Lazyload external JS/CSS files based on controller namespace convention?

Does angularJS have anything in place to lazyload external JS/CSS files based on ng-controller namespaces? So that the following would append com.myApp.SomeClass.js and com.myApp.SomeClass.css to the document head?
<div ng-controller="com.myApp.SomeClass"></div>
Not yet, but it is in the works post v1.0.
Is your app so big that you need it? We have some impressively big apps, and have not run into this need yet, since the controllers are much more dense then when writing the same behavior without angular.
how about using slowscript? It's really easy to lazyload on angularjs
Example:
https://github.com/flrngel/slowscript-angular-require-lazyload
Slowscript:
https://github.com/flrngel/slowscript
core code from website
app.js
app = angular.module("mainApp", ["ui.router"]).run(function($rootScope){
$rootScope.$on('$viewContentLoaded',function(){
slowscript.execute();
});
});
app.config(function($stateProvider, $urlRouterProvider) {
$stateProvider.state("sexy", {
url: "/sexy",
views: {
"contents": {
templateUrl: "/views/test.html",
controller: "Sexy",
reloadOnSearch: true
}
}
});
});
app.controller("Sexy", function($scope) {
slowscript.queue(function(){
slowscript.$global.sexy($scope);
});
});
test.html (angular view template)
<div ng-controller="Sexy">
<input list="univ">
<datalist id="univ">
<option ng-repeat="univ in univs" value="{{univ.name}}"></option>
</datalist>
</input>
<input type="submit"></input>
<noscript src="test.js" type="text/slowscript"></noscript>
</div>
test.js
require(['angular','slowscript'],function(angular,slowscript){
slowscript.$global.sexy=(function($scope){
console.log("tada~");
$scope.univs = [{"idx": 1,"name": "asdf"}, {"idx": 2,"name": "bsdf"}];
$scope.$apply();
});
slowscript.queue_execute();
});
It not possible with stock AngularJS 1.x
However, you can use $oclazyload to defer the loading of code files (js, HTML, CSS)
$ocLazyLoad works perfectly with routers like UI-Router. It returns a promise and uses resolve property to make sure that files are loaded before the view is resolved.
angular.module('myApp', ['ui.router', 'oc.lazyLoad'])
.config(function($stateProvider, $urlRouterProvider, $ocLazyLoadProvider) {
$stateProvider
.state('stateA', {
url: '/stateA',
controller: 'StateACtrl',
templateUrl: 'stateA.html',
resolve: {
load: [ '$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load([
{
name: 'stateA',
files: [
'stateA.css',
'stateA.js',
],
},
]);
},
],
},
})
.state('stateB', {
url: '/stateB',
controller: 'StateBCtrl',
templateUrl: 'stateB.html',
resolve: {
load: [ '$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load([
{
name: 'stateB',
files: [
'stateB.css',
'stateB.js',
],
},
]);
},
],
},
});
});
I have also created a plunkr to demo a working model.
Documentation to know more about possible config options.
Hope it helps!

Categories

Resources