All AngularJS Controllers Not Loading with LazyLoad - javascript

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

Related

Getting error on AngularJS (controller not registered) using ocLazyLoad + ui-router

I've been trying to make ocLazyLoad work on my project, but I keep getting this Angular error
Error: $controller:ctrlreg
A controller with this name is not registered
The controller with the name 'eventoCtrl' is not registered.
NOTICE: I'm also using ui-router to define my app's states.
NOTICE #2: Any suggestion on other methods of using routes or lazy loads will also be apreciated
app.js
(function(){
angular
.module('kulchr', [
'ui.router',
'oc.lazyLoad'
]);})();
config.js
angular
.module('kulchr')
.config(function ($stateProvider, $urlRouterProvider, $ocLazyLoadProvider) {
$stateProvider
.state('eventos', {
url: '/eventos',
views: {
'top-menu-nav': {
templateUrl: 'templates/top-menu-nav.html',
},
'main': {
templateUrl: 'templates/eventos.html',
controller: 'eventoCtrl as listaEvento',
resolve: {
eventoCtrl: function ($ocLazyLoad) {
return $ocLazyLoad.load(
{
files: ['controller/listaEventoController.js'
,'service/eventoService.js']
});
}
}
}
}
});
$urlRouterProvider.otherwise('/');
});
controller
(function() {
'use strict';
angular
.module('kulchr')
.controller('eventoCtrl', ListaEventoController);
ListaEventoController.$inject = ['servicoEvento'];
function ListaEventoController(evento){
var vm = this;
var promise = evento.buscaDados();
promise.then (function(response){
vm.eventos = response.data;
})
}
})();
service
(function() {
'use strict';
angular
.module('kulchr')
.service('servicoEvento', Evento);
function Evento($http, $q) {
var d = $q.defer();
var self = this;
$http.get('/mockup-data/eventos.json')
.then(function (response){
d.resolve(response);
}, function(reason) {
console.log("Motivo: " + reason.data +
"Status: " + reason.status +
" - " + reason.statusText);
return $q.reject(reason);
});
self.buscaDados = function(){
return d.promise;
}
}
})();
What am I missing here? I've reached the ui-router documentation but it just made more confused
BTW, Everything is working fine when adding the files directly on the index.html file using .
Currently what happening is, your listaEventoController is haven't goad loaded when named view is rendering. The reason being is resolve object has been used in wrong place. It doesn't work on named view level. It should taken out and keep it after url(flatten property) inside state definition object.
By taking resolve out oc-lazyLoad module will take care of downloading listaEventoController & eventoService file from server and would make download service register inside angular context and available to use inside angular application.
Code
$stateProvider
.state('eventos', {
url: '/eventos',
views: {
'top-menu-nav': {
templateUrl: 'templates/top-menu-nav.html',
},
'main': {
templateUrl: 'templates/eventos.html',
controller: 'eventoCtrl as listaEvento'
}
},
//moved resolve out from "named view" object.
resolve: {
eventoCtrl: function ($ocLazyLoad) {
return $ocLazyLoad.load({
files: [
'controller/listaEventoController.js',
'service/eventoService.js'
]
}
);
}
}
})

AngularJs directive not working/visible

I'm just getting started with AngularJs. And I'm trying to implement a login directive. But I don't see the output? I've no errors in my console.
My application structure:
(index.html is not visible)
login.directive.js :
(function() {
'use strict';
angular
.module('lnjapp.login',[])
.directive('login', login);
function login() {
var directive = {
template: '<p>test</p>',
//restrict: 'E',
Controller: LoginController,
controllerAs: 'vm'
};
return directive;
}
})();
app.js :
(function () {
'use strict';
angular.module('lnjapp', ['ngRoute', 'ngCookies', 'angular.filter','lnjapp.login','lnjapp.config'])
.constant('GLOBALS', {
url:'http://domain.dev/api/v1/'
});
$(document).ready(function() {
$.material.init();
});
})();
app/pages/login.html:
<login></login>
--EDIT--
login.controller.js:
(function() {
'use strict';
angular.module('lnjapp.login',[])
.controller('LoginController', LoginController);
function LoginController()
{}
})();
route-config.js:
angular
.module('lnjapp.config',[])
.config(config);
function config($routeProvider) {
$routeProvider
.when('/', {
templateUrl: '/app/pages/login.html'
});
}
What am I doing wrong here?
You are creating your lnjapp.login twice, once in login.directive.js and again in login.controller.js. The second time the module is created, it overwrites the first, and whatever was created in the first file will no longer be accessible.
You should always only create a module once, and get the already created module to add additional features in all other cases.
Set (create): angular.module('lnjapp.login',[])
Get (consume): angular.module('lnjapp.login')
For more info and other best practices, see John Papa's excellent Angular Style Guide.

Angularjs controller not working, throws error (newbie)

i'm working with angular-seed project.
I'm trying to retreive data from mysql database.
I need to know how to define different controller for each view.
For example, I have this structure:
js
|_modules
|_companies
|_controller.js
|_data.js
|_app.js
|_base.js
I have added this route to app.js
.state('app.companies', {
url: '/companies',
title: 'Companies',
templateUrl: helper.basepath('companies.html'),
controller: 'companiesCtrl' //THIS THROWS THE ERROR BELOW
})
companies.html has scripts added to botom of the page
<script src="app/js/modules/companies/data.js"></script>
<script src="app/js/modules/companies/controller.js"></script>
and this is the code for controller.js (also tested the commented part)
(function() {
'use strict';
angular
.module('appname')
.controller('companiesCtrl', companiesCtrl);
companiesCtrl.$inject = ['$scope','companiesData','$log'];
function companiesCtrl($scope, companiesData, $log) {
console.log('asd'); //NEVER REACH THIS LOG
};
});
/*var app = angular
.module('appname')
.controller('companiesCtrl', ['$scope','companiesData','$log', function($scope, companiesData, $log){
console.log('asd'); //NEVER REACH THIS LOG
$scope.companies = {};
Data.get('companies').then(function(data){
$scope.companies = data.data;
console.log('($scope.companies)');
});
}]);
*/
But I keep getting
Error: [ng:areq] Argument 'companiesCtrl' is not a function, got undefined
Same if I script ng-controller="companiesCtrl" on my view.
change your function to:
(function() {
'use strict';
angular
.module('appname')
.controller('companiesCtrl', companiesCtrl);
companiesCtrl.$inject = ['$scope','companiesData','$log'];
function companiesCtrl($scope, companiesData, $log) {
console.log('asd'); //NEVER REACH THIS LOG
};
})();// execute this function then it will work
See this example if you remove () breaket then it will give you the error.
If possible then create controller like this:
angular.module('appname')
.controller('companiesCtrl', ['$scope', function($scope) {
console.log('asd'); //NEVER REACH THIS LOG
}]);
Please change your controller as :
(function() {
'use strict';
function companiesCtrl($scope, companiesData, $log) {
console.log('asd'); //NEVER REACH THIS LOG
};
angular
.module('appname')
.controller('companiesCtrl', companiesCtrl);
companiesCtrl.$inject = ['$scope','companiesData','$log'];
})();
The problem was on script loading.
I had to use lazy loading of the files within my app.js
Here's the code:
app.js companies route
.state('app.companies', {
url: '/companies',
title: 'Companies',
templateUrl: helper.basepath('companies.html'),
resolve: helper.resolveFor('companiesCtrl'),
controller: 'companiesCtrl'
})
lazy load code
.constant('APP_REQUIRES', {
scripts: {
'modernizr': ['vendor/modernizr/modernizr.custom.js'],
'icons': ['vendor/fontawesome/css/font-awesome.min.css',
'vendor/simple-line-icons/css/simple-line-icons.css'],
'companiesCtrl': ['app/js/modules/companies/data.js','app/js/modules/companies/controller.js']
},
modules: []
});

Loading external modules while lazy loading in angular

I am able to lazy load angularjs with the help of requirejs. But, how can I load modules that needs to be associated to the controller?
My example configuration in app.js looks like the following, loading all the providers and keeping a reference.
var app = angular.module('myApp', ['ui.router'])
var cacheProviders = {};
app.getProvider = function () {
return cacheProviders.$provide;
}
app.getCompileProvider = function () {
return cacheProviders.$compileProvider;
}
app.getControllerProvider = function () {
return cacheProviders.$controllerProvider;
}
app.getFilterProvider = function () {
return cacheProviders.$filterProvider;
}
app.config(['$stateProvider', '$urlRouterProvider', '$controllerProvider', '$compileProvider', '$filterProvider', '$provide',
function ($stateProvider, $urlRouterProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
(function () {
cacheProviders.$controllerProvider = $controllerProvider;
cacheProviders.$compileProvider = $compileProvider;
cacheProviders.$filterProvider = $filterProvider;
cacheProviders.$provide = $provide;
})();
var lazyCtrlLoad = function (controllerName) {
return ["$q", function ($q) {
var deferred = $q.defer();
require([controllerName], function () {
deferred.resolve();
});
return deferred.promise;
}];
}
$stateProvider.state('main.view2b', {
url: '/view2b',
templateUrl: 'forms/empl/searchEmplForm.html',
controllerAs: 'srchC',
controller: 'searchEmplCtrl',
resolve: {
loadOtherCtrl: lazyCtrlLoad('searchEmplCtrl')
}
})
In my other module, I am trying to register controllers, load services..
define([
'angular', 'angularResource'
], function (angular) {
angular.module('myApp')
.getControllerProvider()
.register(ctrl, ...)
But, while loading service below, I need access to $resource which is part of ngResource module in angularResource.
angular.module('myApp')
.getProvider().service('deptService', ['$resource', function ($resource) {
return $resource('/dept/:dept', {dept: '#_dept'});
}])
How can I load ngResource while initalizing the javascript controllers/services lazily?
Take a look to AngularAMD here. It allows you to load controllers in the ui-router without using lazyload. This AngularAMD is used to integrate requireJs and Angular.
$stateProvider
.state('home', {
url: '',
views: {
'#': angularAmd.route({
templateUrl: 'ngApplication/application/shared/layouts/basic/basicTplView.html',
controllerUrl: 'ngApplication/application/shared/layouts/basic/basicTplCtrl.js',
controller: 'basicTplCtrl'
}),
'header#home': angularAmd.route({
templateUrl: 'ngApplication/application/shared/layouts/header/headerView.html',
controllerUrl: 'ngApplication/application/shared/layouts/header/headerCtrl.js',
controller: 'headerCtrl'
})
},
});
Also, you are using requirejs, you can load all the dependencies for an specific controller using the define syntax of requireJs. Let's say you want to create a loginCtroller in a separately file, and this controller depends on another angular service:
define(['app', 'transformRequestAsFormPostService'], function (app) {
app.controller('loginCtrl', ['$scope', '$rootScope', '$sce', '$http', '$state', 'transformRequestAsFormPostService', function ($scope, $rootScope, $sce, $http, $state, transformRequestAsFormPost) {
$scope.login = function () {
/*do something here using the service*/
};
}]);
});
Here, the dependency called transformRequestAsFormPostService is another file, I defined it in the main.js (requireJs confifguration file) and it's defined using the same approach than the loginCtrol. Now I am using it in my project and its working so far so good.
Regards,
Ernesto

Loading config file in Angular, before everything else

I want to load config file in Angular application, only the first time when application is opened. Then config variables become globally available across the application. Here is what I come up so far in my app.js:
angular
.module('myApp', [
...
])
.run(
[ '$rootScope', '$http',
function ($rootScope, $http) {
$http.get('config/config.json').success(function(data) {
$rootScope.config = data;
});
}
]
)
Here I load the config.js and when I try to use it in my controller like so:
angular.module('myApp')
.controller('MainCtrl', function ($rootScope) {
console.log($rootScope, $rootScope.config);
})
For $rootScope I can see all properties including config, but $rootScope.config returns undefined.
How to load config file on first page load and then make it accessible throughout the app?
Thanks!
As described in Mark Colemans blog post about this, you can get your config and then load the application. Pasting Mark's code for convenience:
var urlToCheck = '/echo/json/';
var myApp = angular.module('myApp', []);
myApp.controller("MyCtrl", ["$scope", "config", function ($scope, config) {
$scope.url = config.url;
}]);
$.ajax({
url: urlToCheck
}).fail(function () {
myApp.constant('config', {
url: '/fail-url'
});
}).done(function () {
myApp.constant('config', {
url: '/done-url'
});
}).always(function () {
angular.bootstrap(document, ['myApp']);
});
You can initialize it to a promise and then access it in the controller when the promise is resolve. reject the promise too.
http://plnkr.co/edit/EIBJhZg80lpeyhkMD3FD
$q.when($rootScope.config).then(function (config) {
console.log($rootScope.config, config);
});

Categories

Resources