Load Dojo module within Angular app - javascript

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.

Related

How to have controllers in separate file in AngularJS

I look around a bit and didn't find an answer that fit with my current situation. I have a app.js file:
'use strict';
var demoApp = angular.module('demoApp', [
// Dépendances du "module" <-- demoApp
'ngRoute',
'routeAppControllers',
'todoList'
]);
demoApp.config(['$routeProvider', '$locationProvider',
function($routeProvider, $locationProvider) {
// Système de routage
$routeProvider
.when('/home', {
templateUrl: 'views/home.html',
controller: 'homeCtrl'
})
.when('/contact/:msg?', {
templateUrl: 'views/contact.html',
controller: 'contactCtrl'
})
.when('/todolist', {
templateUrl: 'views/todolist.html',
controller: 'listeCtrl'
})
.when('/testfiltre', {
templateUrl: 'views/testfiltre.html',
controller: 'demoFiltreCtrl'
})
.when('/testCreationfiltre', {
templateUrl: 'views/testcreationfiltre.html',
controller: 'demoCreationFiltreCtrl'
})
.otherwise({
redirectTo: '/home'
});
}
]);
var routeAppControllers = angular.module('routeAppControllers', []);
routeAppControllers.controller('homeCtrl', ['$scope',
function($scope){
$scope.message = "Bienvenue sur la page d'accueil";
}
]);
routeAppControllers.controller('contactCtrl', ['$scope','$routeParams',
function($scope, $routeParams){
$scope.message = "Laissez-nous un message sur la page de contact !";
$scope.msg = $routeParams.msg || "Bonne chance pour cette nouvelle appli !";
}
]);
routeAppControllers.controller('listeCtrl', [function(){}]);
I have todolist module in todolist_controller.js:
var todoList=angular.module('todoList',[]);
todoList.controller('todoCtrl', ['$scope',
function ($scope) {
var todos = $scope.todos = [];
$scope.addTodo = function () {
var newTodo = $scope.newTodo.trim();
if (!newTodo.length) {
return;
}
todos.push({
title: newTodo,
completed: false
});
$scope.newTodo = '';
};
$scope.removeTodo = function (todo) {
todos.splice(todos.indexOf(todo), 1);
};
$scope.markAll = function (completed) {
todos.forEach(function (todo) {
todo.completed = completed;
});
};
$scope.clearCompletedTodos = function () {
$scope.todos = todos = todos.filter(function (todo) {
return !todo.completed;
});
};
}
]);
I have my index.html page:
<!DOCTYPE html>
<html lang="fr" ng-app="demoApp">
<head>
<meta charset="utf-8" />
<title>Demo App</title>
<link rel="stylesheet" href="styles/style.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular-route.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-i18n/1.4.2/angular-locale_fr-ca.min.js"></script>
<script src="scripts/controllers/app.js"></script>
<script src="scripts/controllers/todolist_controllers.js"></script>
</head>
<body>
<div ng-view>
</div>
<nav>
Page d'accueil
Page de contact
Todo list
test filtre
test création filtre
</nav>
</body>
</html>
I read that I'm suppose to call, for example, my main module App and all my other module file should start with: angular.module('App').controller(...
But, this would imply that if I change the name of my app from 'app' to 'my_app' for example, I would have to go trough all my controllers and change 'app' for 'my-app'.
I would like to avoid that and just be able to import my file in index.html and just have to declare it in the dependencies of my 'app' module.
Angular Structure
When you build an angular app you should separate as much as possible to give your code readability. You should create a module for each page/part of your web app.
Example
Here is an example of this type of structure, I wrote this and use it as a base for angular apps.
app folder
This folder holds all of your components and routes.
routes.js
This file has the states of your project and is its own module
app.js
This file is just the base where you can call all your other modules as dependencies.
var app = angular.module("myApp", [
'ui.bootstrap',
'ngAnimate',
'myAppRouter',
'myAppHomeCtrl',
'myAppHomeService',
'myAppNavbarDirective',
'myAppNavbarService',
'myAppLoginCtrl',
'myAppLoginService'
]);
You can see all of the different modules written and added here. See the way this is called myApp? well we call that part in the html
<html ng-app="myApp">
components
this will contain things like "home" and "contact" these folders should have everything then need in little self contained html, controllers and services.
controller/module
This is the bit that really answers your question, to add a new module for a controller you do as follows.
angular.module('myAppHomeCtrl', []).controller('homeCtrl', ['$scope', 'homeContent', function($scope, homeContent){
$scope.dataset = homeContent.getContent();
$scope.header = homeContent.getHeader();
$scope.subheading = homeContent.getSubheader();
}]);
service/factory
So you can see that in the module we call a factory, this is also in this folder and looks like this.
angular.module('myAppHomeService', [])
.factory('homeContent', function(){
return {
getHeader: function(){
return "Welcome Human";
},
getSubheader: function(){
return "To Joe's Awesome Website";
},
};
});
Back to index.html
So back in our index we can add all of these modules in <script> tags like this.
<!-- Angular Modules -->
<script type="text/javascript" src="app/app.module.js"></script>
<script type="text/javascript" src="app/app.routes.js"></script>
<script type="text/javascript" src="app/components/home/homeCtrl.js"></script>
<script type="text/javascript" src="app/components/home/homeService.js"></script>
<script type="text/javascript" src="app/shared/navigation-bar/navbarDirective.js"></script>
<script type="text/javascript" src="app/shared/navigation-bar/navbarService.js"></script>
<script type="text/javascript" src="app/components/login/loginCtrl.js"></script>
<script type="text/javascript" src="app/components/login/loginService.js"></script>
In production you will minify all these but you can just call them alll like this whilst in dev.
Conclusion
To conclude I'll do a summery to make sure you have everything you need to get your modules to work
go to your app.js (main angular module) and app the name to it.
go to your components and create the new module
go to the index.html and add your script tag that links to the new module
now you can use the controllers and all the components as you wish
I hope this guid to angular structure helps you. Good Luck
instead of defining the app as var app = angular.module('myApp', []); use angular.module('myApp', []); var not necessary. Then in separate files like somethingCtrl.js you can define controllers like:
angular.module('myApp')
.controller('somethingCtrl', ['$scope', function ($scope) {
}]);`
then you can add the script tags in order on html. start with the module definition first. Use these style guidelines for angular
OPTION 2
define your first module. Then you define various other modules and use them as dependencies like so
index.js:
angular.module('mainApp', ['otherApp']);
other.js
angular.module('otherApp', []);
This gives you the flexibility to add all of your controllers to one module and all of your directives to another. You can mix the functionality around by using modules as dependencies. Just make sure the mainApp is loaded first in the script tag order.
This is where requirejs or browserify would help you enormously. However as your question doesn't relate to either of them you can use good ol' JS to accomplish something similar.
Take for example your app.js file. Build in a global config object, then use that when referencing the app name wherever you are. Then it's a case of just changing the app name in the config object to something else without breaking any other components.
var config = {
app : "myApp"
}
angular.module(config.app, ["ngAnimate"]);
Now a controller in controller.js can use that same config object.
angular.module(config.app)
.controller("myController", function($scope) { ... }
As long as you load in app.js first you'll have the config object available.
<script src="/js/app.js"></script>
<script src="/js/controller.js"></script>
This isn't pretty and I'd advise you to use browserify or requirejs (or any other alternative) in the future to build out your front end if you want this kind of functionality without the need for horrible global objects/variables.
Say this is your directory layout:
├── controllers
│   └── home.js
├── directives
└── index.html
You define your controller in home.js like you usually would, and then include it in index.html with:
...
<script src="./controllers/home.js"></script>
...
P.S.: Using the script tag should be the preferred way to do this, however should you wish to use Require.js, there is a fantastic library angularAMD

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

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

Angular: Adding custom script/css to partials via controller

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/

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