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

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

Related

AngularJS module.controller vs $controllerProvider.register

I'm trying to add controller dynamically in my AngularJS application.
On sub-domain, I have anotherController.js file.
Here's anotherController.js content:
function anotherControllerWrapper() {
return ['$scope', '$state', function ($scope, $state) {
$scope.doWork = function () {
//...doing some work...
alert('work done');
};
$scope.doWork();
}];
};
Also I have wrote runtimeController provider to be able to use $controllerProvider in runtime:
app.provider('runtimeController', function () {
var controllerProvider = null;
this.setControllerProvider = function (cp) {
controllerProvider = cp;
};
this.$get = function () {
return {
registerController: function (controllerName, controllerConstructor) {
if (!controllerProvider.has(controllerName)) {
controllerProvider.register(controllerName, controllerConstructor);
}
}
};
};
});
Here's config section of application:
app.config(function($controllerProvider, runtimeControllerProvider) {
runtimeControllerProvider.setControllerProvider($controllerProvider);
});
I'm receiving controller's code over http (inside another controller), so it looks like this:
app.controller('testController', ['$scope', '$state', '$http', 'runtimeController',
function ($scope, $state, $http, runtimeController) {
$http.get('http://someUrl/anotherController.js')
.then(
function(sucess){
var evaluated = new Function('return ' + success.data)();
var ctrl = evaluated();
// routing to ui state with specified 'anotherController' works
// no 'anotherController' in app._invokeQueue
runtimeController.registerController('anotherController', ctrl);
// routing to ui state with specified 'anotherController' constanly fails
// 'anotherController' appears in app._invokeQueue
//app.controller('anotherController', ctrl);
//--registering new UI route with 'anotherController' as controller here
$state.go('anotherState');
},
function(error){ alert('something went wrong!'); },
);
}]);
Ui states are also added dymanically, after I'm adding controller.
Can someone explain me please, what's happening and what's difference between $controllerProvider.register and module.controller?
Module methods (controller, directive, etc) result in adding a config block (_configBlocks) that is executed on application initialization. Once the application has passed config phase, it won't execute newly added config blocks, so app.controller(...) has no effect during run phase.
As this example shows, runtimeController implementation can be simplified to
app.config(($provide, $controllerProvider) => {
$provide.value('$controllerProvider', $controllerProvider);
});
eval should be avoided for numerous reasons. Considering that the script is loaded from a domain that is allowed by CORS and doesn't require to be evaled, a suitable alternative is to load it as a script. This will require to patch AngularJS API to allow late component registrations, similarly to how ocLazyLoad does - or just use ocLazyLoad, because it already does that.

Loading html and Controller from server and creating dynamic states UI - router

I am looking for a Solution to load my App Content dynamically from the Server.
My Scenario:
Lets say we have 2 Users (A and B), my App consists of different Modules like lets say a shoppingList and a calculator, now my goal would be the User logs into my App from the Database I get the User rights and depending what rights he has, i would load the html for the views and the controller files for the logic part from the Server, while doing that I would create the states needed for the html and ctrl. So basically my App is very small consistent of the Login and everything else is getting pulled from the Server depending on the Userrights.
What I use:
Cordova
AngularJs
Ionic Framework
Why I need it to be all dynamic:
1)The possiblity to have an App that contains just the login logic, so when fixing bugs or adding Modules I only have to add the files to the server give the User the right for it and it is there without needing to update the app.
2)The User only has the functionality he needs, he doesnt need to have everything when he only has the right for 1 module.
3)The App grows very big at the moment, meaning every Module has like 5-10 states, with their own html and Controllers. currently there are 50 different Modules planned so you can do the math.
I looked at this to get some inspiration:
AngularJS, ocLazyLoad & loading dynamic States
What I tried so far:
I created 1 Html file which contains the whole module so I only have 1 http request:
Lets say this is my response from the server after the User logged in
HTML Part:
var rights= [A,B,C,D]
angular.forEach(rights, function (value, key) {
$http.get('http://myServer.com/templates/' + value + '.html').then(function (response) {
//HTML file for the whole module
splits = response.data.split('#');
//Array off HTMl strings
for (var l = 1; l <= splits.length; l++) {
//Putting all Html strings into templateCache
$templateCache.put('templates/' + value +'.html', splits[l - 1]);
}
}
});
Controller Part:
var rights= [A,B,C,D]
angular.forEach(rights, function (value, key) {
$http.get('http://myServer.com/controller/' + value + '.js').then(function (response) {
// 1 file for the whole module with all controllers
splits = response.data.split('#');
//Array off controller strings
for (var l = 1; l <= splits.length; l++) {
//Putting all Controller strings into templateCache
$templateCache.put('controllers/' + value +'.js', splits[l - 1]);
}
}
});
After loading the Controllers I try to register them:
$controllerProvider.register('SomeName', $templateCache.get('controllers/someController));
Which is not working since this is only a string...
Defining the Providers:
.config(function ($stateProvider, $urlRouterProvider, $ionicConfigProvider, $controllerProvider) {
// turns of the page transition globally
$ionicConfigProvider.views.transition('none');
$stateProviderRef = $stateProvider;
$urlRouterProviderRef = $urlRouterProvider;
$controllerProviderRef = $controllerProvider;
$stateProvider
//the login state is static for every user
.state('login', {
url: "/login",
templateUrl: "templates/login.html",
controller: "LoginCtrl"
});
//all the other states are missing and should be created depending on rights
$urlRouterProvider.otherwise('/login');
});
Ui-Router Part:
//Lets assume here the Rights Array contains more information like name, url...
angular.forEach(rights, function (value, key) {
//Checks if the state was already added
var getExistingState = $state.get(value.name)
if (getExistingState !== null) {
return;
}
var state = {
'lang': value.lang,
'params': value.param,
'url': value.url,
'templateProvider': function ($timeout, $templateCache, Ls) {
return $timeout(function () {
return $templateCache.get("templates" + value.url + ".html")
}, 100);
},
'ControllerProvider': function ($timeout, $templateCache, Ls) {
return $timeout(function () {
return $templateCache.get("controllers" + value.url + ".js")
}, 100);
}
$stateProviderRef.state(value.name, state);
});
$urlRouter.sync();
$urlRouter.listen();
Situation so far:
I have managed to load the html files and store them in the templateCache, even load them but only if the states were predefined.What I noticed here was that sometimes lets say when I remove an item from a List and come back to the View the item was there again maybe this has something to do with cache I am not really sure...
I have managed to load the controller files and save the controllers in the templateCache but I dont really know how to use the $ControllerPrioviderRef.register with my stored strings...
Creating the states did work but the Controller didnt fit so i could not open any views...
PS: I also looked at require.js and OCLazyLoad as well as this example dynamic controller example
Update:
Okay so I managed to load the Html , create the State with the Controller everything seems to work fine, except that the Controller does not seem to work at all, there are no errors, but it seems nothing of the Controller logic is executed. Currently the only solution to register the controller from the previous downloaded file was to use eval(), which is more a hack then a proper solution.
Here the code:
.factory('ModularService', ['$http', ....., function ( $http, ...., ) {
return {
LoadModularContent: function () {
//var $state = $rootScope.$state;
var json = [
{
module: 'Calc',
name: 'ca10',
lang: [],
params: 9,
url: '/ca10',
templateUrl: "templates/ca/ca10.html",
controller: ["Ca10"]
},
{
module: 'SL',
name: 'sl10',
lang: [],
params: 9,
url: '/sl10',
templateUrl: "templates/sl/sl10.html",
controller: ['Sl10', 'Sl20', 'Sl25', 'Sl30', 'Sl40', 'Sl50', 'Sl60', 'Sl70']
}
];
//Load the html
angular.forEach(json, function (value, key) {
$http.get('http://myserver.com/' + value.module + '.html')
.then(function (response) {
var splits = response.data.split('#');
for (var l = 1; l <= value.controller.length; l++) {
$templateCache.put('templates/' + value.controller[l - 1] + '.html', splits[l - 1]);
if (l == value.controller.length) {
$http.get('http://myserver.com//'+value.module+'.js')
.then(function (response2) {
var ctrls = response2.data.split('##');
var fullctrl;
for (var m = 1; m <= value.controller.length; m++){
var ctrlName = value.controller[m - 1] + 'Ctrl';
$controllerProviderRef
.register(ctrlName, ['$scope',...., function ($scope, ...,) {
eval(ctrls[m - 1]);
}])
if (m == value.controller.length) {
for (var o = 1; o <= value.controller.length; o++) {
var html = $templateCache
.get("templates/" + value.controller[o - 1] + ".html");
var getExistingState = $state.get(value.controller[o - 1].toLowerCase());
if (getExistingState !== null) {
return;
}
var state = {
'lang': value.lang,
'params': value.param,
'url': '/' + value.controller[o - 1].toLowerCase(),
'template': html,
'controller': value.controller[o - 1] + 'Ctrl'
};
$stateProviderRef.state(value.controller[o - 1].toLowerCase(), state);
}
}
}
});
}
}
});
});
// Configures $urlRouter's listener *after* your custom listener
$urlRouter.sync();
$urlRouter.listen();
}
}
}])
Any help appreciated
Ok, so let's start from the beginning.
All the application logic should be contained on the server and served via API-calls through REST, SOAP or similar. By doing so, you reduce the amount of logic built into the UI, which reduces the stress on the client. This basically makes your client app a rendering agent, containing only models and views for the data and logic served by the backend API.
As foreyez stated in his/her comment, this isn't an issue for any modern (or half-modern) device.
If you insist on not loading all of the layouts at once, you could of course separate them into partials, which you load after the login based on the user privileges. By doing so, you reduce the amount of in-memory data, even though the improvement would be doubtable, at best.
Can I suggest you to do some changes to the way you load the states?
Write a script that give you back a json with the states the user can access.
Ex.
resources/routing-config.yourLangage?user=user-id-12345
this will return a json file that depends on the user logged in. The structure can be something like this:
[
{
"name": "home",
"url": "/home",
"templateUrl": "views/home.html",
"controller": "HomeController",
"dependencies": ["scripts/home/controllers.js", "scripts/home/services.js", "scripts/home/directives.js"]
},
{
"name": "user",
"url": "/user",
"templateUrl": "views/user.html",
"controller": "UserController",
"dependencies": ["scripts/user/controllers.js", "scripts/user/services.js", "scripts/home/directives.js"]
}
]
Then let's write a service that will read the states the user is allowed to access:
app.factory('routingConfig', ['$resource',
function ($resource) {
return $resource('resources/routing-config.yourLangage', {}, {
query: {method: 'GET',
params: {},
isArray: true,
transformResponse: function (data) {
// before that we give the states data to the app, let's load all the dependencies
var states = [];
angular.forEach(angular.fromJson(data), function(value, key) {
value.resolve = {
deps: ['$q', '$rootScope', function($q, $rootScope){
// this will be resolved only when the user will go to the relative state defined in the var value
var deferred = $q.defer();
/*
now we need to load the dependencies. I use the script.js javascript loader to load the dependencies for each page.
It is very small and easy to be used
http://www.dustindiaz.com/scriptjs
*/
$script(value.dependencies, function(){ //here we will load what is defined in the dependencies field. ex: "dependencies": ["scripts/user/controllers.js", "scripts/user/services.js", "scripts/home/directives.js"]
// all dependencies have now been loaded by so resolve the promise
$rootScope.$apply(function(){
deferred.resolve();
});
});
return deferred.promise;
}]
};
states.push(value);
});
return states;
}
}
});
}]);
Then let's configure the app:
app.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', '$filterProvider', '$provide', '$compileProvider',
function ($stateProvider, $urlRouterProvider, $locationProvider, $filterProvider, $provide, $compileProvider) {
// this will be the default state where to go as far as the states aren't loaded
var loading = {
name: 'loading',
url: '/loading',
templateUrl: '/views/loading.html',
controller: 'LoadingController'
};
// if the user ask for a page that he cannot access
var _404 = {
name: '_404',
url: '/404',
templateUrl: 'views/404.html',
controller: '404Controller'
};
$stateProvider
.state(loading)
.state(_404);
// save a reference to all of the providers to register everything lazily
$stateProviderRef = $stateProvider;
$urlRouterProviderRef = $urlRouterProvider;
$controllerProviderRef = $controllerProvider;
$filterProviderRef = $filterProvider;
$provideRef = $provide;
$compileProviderRef = $compileProvider;
//redirect the not found urls
$urlRouterProvider.otherwise('/404');
}]);
Now let's use this service in the app.run:
app.run(function ($location, $rootScope, $state, $q, routingConfig) {
// We need to attach a promise to the rootScope. This will tell us when all of the states are loaded.
var myDeferredObj = $q.defer();
$rootScope.promiseRoutingConfigEnd = myDeferredObj.promise;
// Query the config file
var remoteStates = routingConfig.query(function() {
angular.forEach(remoteStates, function(value, key) {
// the state becomes the value
$stateProviderRef.state(value);
});
// resolve the promise.
myDeferredObj.resolve();
});
//redirect to the loading page until all of the states are completely loaded and store the original path requested
$rootScope.myPath = $location.path();
$location.path('/loading'); //and then (in the loading controller) we will redirect to the right state
//check for routing errors
$rootScope.$on('$stateChangeError',
function(event, toState, toParams, fromState, fromParams, error){
console.log.bind(console);
});
$rootScope.$on('$stateNotFound',
function(event, unfoundState, fromState, fromParams){
console.error(unfoundState.to); // "lazy.state"
console.error(unfoundState.toParams); // {a:1, b:2}
console.error(unfoundState.options); // {inherit:false} + default options
});
});
Eventually, the LoadingController:
app.controller('LoadingController', ['$scope', '$location', '$rootScope',
function($scope, $location, $rootScope) {
//when all of the states are loaded, redirect to the requested state
$rootScope.promiseRoutingConfigEnd.then(function(){
//if the user requested the page /loading then redirect him to the home page
if($rootScope.myPath === '/loading'){
$rootScope.myPath = '/home';
}
$location.path($rootScope.myPath);
});
}]);
In this way everything is super flexible and lazy loaded.
I wrote 3 different user portals already and I can easily scale to all of the user portal I want.
I have developed an application with keeping those things in mind. Here is my architecture.
Folder Structure:
WebApp
|---CommonModule
|---common-module.js //Angular Module Defination
|---Controllers //Generally Nothing, but if you have a plan to
//extend from one CommonController logic to several
//module then it is usefull
|---Services //Common Service Call Like BaseService for all $http
//call, So no Module Specific Service will not use
//$http directly. Then you can do several common
//things in this BaseService.
//Like Error Handling,
//CSRF token Implementation,
//Encryption/Decryption of AJAX req/res etc.
|---Directives //Common Directives which you are going to use
//in different Modules
|---Filters //Common Filters
|---Templates //Templates for those common directives
|---index.jsp //Nothing, Basically Redirect to
//Login or Default Module
|---scripts.jsp //JQuery, AngularJS and Other Framworks scripts tag.
//Along with those, common controlers, services,
//directives and filtes.
|---templates.jsp //Include all common templates.
|---ng-include.jsp //will be used in templates.jsp to create angular
//template script tag.
|---ModuleA
|---moduleA-module.js //Angular Module Definition,
//Use Common Module as Sub Module
|---Controllers
|---Services
|---Directives
|---Filters
|---Templates
|---index.jsp
|---scripts.jsp
|---templates.jsp
|---ModuleB
|--- Same as above ...
Note: Capital Case denotes folder. Beside ModuleA there will a LoginModule for your case I think or You could Use CommonModule for it.
Mehu will be as follows.
Module A <!--Note: index.jsp are indexed file
//for a directive -->
Module B
Each of those JSP page are actually a independent angular application. Using those following code.
ModuleA/index.jsp
<!-- Check User Permission Here also for Security
If permission does not have show Module Unavailable Kind of JSP.
Also do not send any JS files for this module.
If permission is there then use this following JSP
-->
<!DOCTYPE HTML>
<html lang="en" data-ng-app="ModuleA">
<head>
<title>Dynamic Rule Engine</title>
<%# include file="scripts.jsp" %>
<%# include file="templates.jsp" %> <!-- Can be cached it in
different way -->
</head>
<body>
<%# include file="../common.jsp" %>
<div id="ngView" data-ng-view></div>
<%# include file="../footer.jsp" %>
</body>
</html>
ModuleA/scripts.jsp
<%# include file="../CommonModule/scripts.jsp" %> <!-- Include Common Things
Like Jquery Angular etc -->
<scripts src="Controlers/ModlueAController1.js"></script>
.....
ModuleA/templates.jsp
<%# include file="../CommonModule/templates.jsp" %>
<!-- Include Common Templates for common directives -->
<jsp:include page="../CommonModule/ng-include.jsp"><jsp:param name="src" value="ModuleA/Templates/template1.jsp" /></jsp:include>
.....
CommonModule/ng-include.jsp
<script type="text/ng-template" id="${param.src}">
<jsp:include page="${param.src}" />
</script>
But main problem of this approach is When user will change Module, Page will get refreshed.
EDIT:
There is a ModuleA.module.js file which actually contain module deceleration as follows.
angular.module('ModuleA.controllers', []);
angular.module('ModuleA.services', []);
angular.module('ModuleA.directives', []);
angular.module('ModuleA.filters', []);
angular.module('ModuleA',
['Common',
'ModuleA.controllers' ,
'ModuleA.services' ,
'ModuleA.directives' ,
'ModuleA.filters'])
.config(['$routeProvider', function($routeProvider) {
//$routeProvider state setup
}])
.run (function () {
});
I think I'm doing what you're asking. I achieve this by using UI-Router, ocLazyLoad and ui-routers future states. Essentially our setup allows us to have 50+ modules, all in the same code base, but when a user opens the app. its starts by only loading the base files required by the app. Then, as the user moves around between states, the application will load up the files required for that part, as their needed. (apologies for the fragmented code, I've had to rip it out of the code base, but tried to only provide the stuff thats actually relevant to the solution).
Firstly, the folder structure
Core App
config.js
Module 1 (/module1)
module.js
controllers.js
Module 2 (/module2)
module.js
controllers.js
etc
Config.js:
The first thing we do is create the base state, this is an abstract state, so the user can never actually just hit it.
$stateProvider.state('index', {
abstract: true,
url: "/index",
views: {
'': {
templateUrl: "views/content.html" // a base template to have sections replaced via ui-view elements
}
},
...
});
Then we configure the modules in ocLazyLoad. This allows us to just tell ocLazyLoad to load the module, and it loads all the required files (although in this instance, its only a single file, but it allows each module to have varying paths).
$ocLazyLoadProvider.config({
loadedModules: ['futureStates'],
modules: [
{
name: 'module1',
files: ['module1/module.js']
},
{
name: 'module2',
files: ['module2/module.js']
}
]
});
Next we create a function to allow ui-router to load the modules when requested (through future states).
function ocLazyLoadStateFactory($q, $ocLazyLoad, futureState) {
var deferred = $q.defer();
// this loads the module set in the future state
$ocLazyLoad.load(futureState.module).then(function () {
deferred.resolve();
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
}
$futureStateProvider.stateFactory('ocLazyLoad', ['$q', '$ocLazyLoad', 'futureState', ocLazyLoadStateFactory]);
Then we configure the actual future states. These are states that may be loaded in the future, but we don't want to configure them right now.
$futureStateProvider.futureState({
'stateName': 'index.module1', // the state name
'urlPrefix': '/index/module1', // the url to the state
'module': 'module1', // the name of the module, configured in ocLazyLoad above
'type': 'ocLazyLoad' // the future state factory to use.
});
$futureStateProvider.futureState({
'stateName': 'index.module2',
'urlPrefix': '/index/module2',
'module': 'module2',
'type': 'ocLazyLoad'
});
If you want the list of future states to be provided asynchronously:
$futureStateProvider.addResolve(['$http', function ($http) {
return $http({method: 'GET', url: '/url'}).then(function (states) {
$futureStateProvider.futureState({
'stateName': 'index.module2',
'urlPrefix': '/index/module2',
'module': 'module2',
'type': 'ocLazyLoad'
});
});
}]);
Then we configure the modules as follows:
module1/module.js
$stateProvider.state('index.module1', {
url: "/module1",
abstract: true,
resolve: {
loadFiles: ['$ocLazyLoad', function($ocLazyLoad){
return return $ocLazyLoad.load(['list of all your required files']);
}]
}
})
$stateProvider.state('index.module1.sub1', {
url: "/sub1",
views: {
// override your ui-views in here. this one overrides the view named 'main-content' from the 'index' state
'main-content#index': {
templateUrl: "module1/views/sub1.html"
}
}
})

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

Angularjs : can i inject dependency in app dynamically

I have defined drawerApp on my js . On click of button i am loading partial and there require js file. I need to inject some loaded js into my current drawerApp is there any way to inject in app?
Currentlt config. is as below.
JavaScript:
drawerApp.config([ '$interpolateProvider','$controllerProvider','$compileProvider', '$filterProvider', '$provide',
function ($interpolateProvider,$controllerProvider,$compileProvider, $filterProvider, $provide) {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
drawerApp.register =
{
controller: $controllerProvider.register,
directive: $compileProvider.directive,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service
};
}]);

AngularJS: Inject dependencies while $provide

I am a newbie to AngularJS. I am trying to develop a single page application in salesforce CRM plarform. I wanted to load the dependencies on demand i.e., only when the user click a particular link. I have also achieved this by using https://github.com/matys84pl/angularjs-requirejs-lazy-controllers
The above link has examples for directives alone. I have added services and filter in the same route configuration file.
function registerServices(service) {
if(service) {
if (!$provide){ throw new Error("Error: $setProvide is not set!"); }
$provide.value(service[0], service[1]);
} else {
$provide.value = null;
}
}
I'm registering a service like the above and it can be used as,
service = ['serviceName', function(){
return {
a: 1, b:2
}
}]
Everything is working great, But my doubt is, how to inject the dependencies such $q, $rootScope, etc in the above code.
I don't know about lazy loading but, if lazy loading work then the standard method of injection may work
service = ['serviceName', '$rootScope','$q', function($rootScope, $q){
return {
a: 1, b:2
}
}]
I have a solution for this problem, I am not sure whether the way is proper.
Controller
var controller = function($scope, customService){
$scope.name = "hello world";
customService($q, $rootScope).then(function(data){
console.log(data);
/*Received data can be manipulated*/
});
}
Service
'use strict';
define(function () {
return ['customService', function($q, $rootScope){
var deferred = $q.defer();
/*Your code*/
return deferred.promise;
}];
})

Categories

Resources