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'
]
}
);
}
}
})
Related
I am trying to create a service to use throughout my Angular app that pulls in data from a .json file using $http. This is what the factory looks like:
var trooNewsServices = angular.module('trooNewsServices', []);
trooNewsServices.factory('Articles', ['$http',
function($http){
$http.get('resources/articles.json').success(function(data) {
return data;
});
}]);
I passed in the trooNewsServices dependency into my module declaration. Any controller that I try to pass in my new Articles service, I get a
"Could not instantiate controller HomeController"
error in the console. Not sure what I am missing/what is wrong with this code. Should I be using $resource instead of $http?
Here is how I am passing the 'trooNewsServices' into my main module:
var TrooNews = angular
.module('TrooNews', ['ngMaterial', 'ngNewRouter', 'trooNewsServices'])
.config(function($mdThemingProvider) {
$mdThemingProvider
.theme('default')
.primaryPalette('indigo')
.accentPalette('pink');
})
.config(function($locationProvider) {
$locationProvider.html5Mode({
enabled: false,
requireBase: false
});
});
Here is how I try to inject 'Articles' into one of my controllers:
TrooNews.controller('HomeController', ['Articles',
function(Articles) {
this.name = 'Troo News';
this.articles = Articles.query();
}]);
And here is how I set up routing in my 'AppController':
TrooNews.controller('AppController', function($router, $mdSidenav, $mdToast, $parse, $http) {
$router.config([{
path: '/',
component: 'home'
}, {
path: '/home',
component: 'home'
}, {
path: '/about',
component: 'about'
}, {
path: '/settings',
component: 'settings'
}, {
path: '/article/:id',
component: 'article'
}]);
this.toggleSidenav = function(menuId) {
$mdSidenav(menuId).toggle();
};
this.navigateTo = function(link) {
var parts = link.match(/^(.+?)(?:\((.*)\))?$/);
var url;
if (parts[2]) {
url = '.' + $router.generate(parts[1], $parse(parts[2])());
} else {
url = '.' + $router.generate(parts[1]);
}
$mdToast.show($mdToast.simple().content('Navigate To: ' + url).position('bottom right'));
$router.navigate(url);
this.toggleSidenav('left');
};
});
Inside your HomeController, you are executing this.articles = Articles.query();, but your Articles service doesn't define any query function.
Instead, your service is just immediately executing an HTTP GET request upon creation. Not sure why this would lead to your error, but it is a red flag.
Try changing your Articles service to the following:
trooNewsServices.factory('Articles', ['$http',
function Articles($http){
this.query = function() {
return $http.get('resources/articles.json')
.then(function(response) { return response.data; });
};
}]);
I was experiencing the same error message under different conditions. In my case, it was because I was referencing $scope in my dependencies (old habit that I'm trying to break). In my case, I wasn't using $scope and could easily remove the reference. That cleared up my error. Check your code for $scope references and see if that fixes it.
https://github.com/angular/router/issues/313
and
How can we watch expressions inside a controller in angular 1.4 using angular-new-router
I've been trying to get my Angular app to populate the controller with data from a database (mongodb) before the page loads. I can't quite get it to work. I'm trying to use the "resolve" property of angular ui-router but it's not working and I can't figure it out!!
Here's the full code for my app:
var freezerApp = angular.module('freezerApp', ['ui.router']);
freezerApp.config([
'$stateProvider','$urlRouterProvider',function($stateProvider,$urlRouterProvider) {
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/partials/home.html',
controller: 'freezerCtrl',
});
$stateProvider
.state('freezer', {
url: '/freezers',
templateUrl: 'partials/freezers.html',
controller: 'freezerCtrl',
//not working:
resolve: {
freezerPromise: function($scope){
return $scope.getAll();
}
};
});
$urlRouterProvider.otherwise('home');
}]);
freezerApp.controller('freezerCtrl', ['$scope', '$http', function ($scope,$http) {
$scope.freezer =
{'freezername':'Freezer Name',
'building':'Building',
'floor':'Floor',
'room':'Room',
'shelves': 0,
'racks': 0
};
$scope.add_freezer = function() {
$scope.freezers.push(
{'freezername': $scope.freezer.freezername,
'building':$scope.freezer.building,
'floor':$scope.freezer.floor,
'room':$scope.freezer.room,
'shelves': $scope.freezer.shelves,
'racks': $scope.freezer.racks
}
);
};
$scope.freezers = [
{
}
];
$scope.default_freezer = $scope.freezers[0];
$scope.getAll = function() {
return $http.get('/freezers').success(function(data){
angular.copy(data, $scope.freezers);
});
};
}]);
According to the Ui-Router Resolve Documentation:
You can use resolve to provide your controller with content or data that is custom to the state. resolve is an optional map of dependencies which should be injected into the controller.
If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the $stateChangeSuccess event is fired.
It looks like you are trying to provide $scope object from your freezerCtrl to your resolve property. This is incorrect.
I would recommend you create a factory like so for your api call.
angular.module.('freezerApp').factory('freezerFact',function($http){
return {
getAll: $http.get('/freezers')
}
});
Then inside of your freezer $state deceleration you could do it like this:
$stateProvider
.state('freezer', {
url: '/freezers',
templateUrl: 'partials/freezers.html',
controller: 'freezerCtrl',
resolve: {
freezerPromise: function(freezerFact){
return freezerFact.getAll;
}
};
});
Then you would pass the freezerPromise object into your freezerCtrl and manipulate the promise after that.
I have looked through all the similar questions I can find but none was helpful in resolving this error:
Error: [ng:areq] Argument 'loginController' is not a function, got undefined.
Below are my files:
LoginController.js
"use strict";
define(['app-config', 'loginService'], function (app) {
app.controller('LoginController', ['$scope', '$location', 'loginService',
function (sc, loc, loginService) {
sc.login = function () {
console.log("Email Address = " + sc.EmailAddress);
console.log("Password = " + sc.Password);
}
}]);
app-config.js
"use strict";
define(['angularAMD', 'angular-route', 'angular-resource'], function (angularAMD) {
var app = angular.module("openInterviewApp", ['ngRoute', 'ngResource']);
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider
/* .when('/login', angularAMD.route({
templateUrl: 'Accounts/Login.html',
controllerUrl: 'Accounts/LoginController'
})) */
.when("/:section/:tree", angularAMD.route({
templateUrl: function (rp) { return rp.section + '/' + rp.tree + '.html'; },
resolve: {
load: ['$q', '$rootScope', '$location', function ($q, $rootScope, $location) {
var path = $location.path();
var parsePath = path.split("/");
var parentPath = parsePath[1];
var controllerName = parsePath[2];
var loadController = parentPath + "/" + controllerName + "Controller";
console.log ("loadController=" + loadController);
var deferred = $q.defer();
require([loadController], function () {
$rootScope.$apply(function () {
deferred.resolve();
});
});
return deferred.promise;
}]
}
}))
.otherwise({ redirectTo: '/' });
}]);
angularAMD.bootstrap(app);
return app;
});
main.js
require.config({
//By default load any module IDs from baseUrl
//baseUrl is normally set to the same directory as the script used in a data-main attribute
baseUrl: "",
// paths config is relative to the baseUrl
// never includes a ".js" extension since the paths config could be for a directory.
// ex: app: 'app' means if the module ID starts with "app", load it from the /app directory
paths: {
'app-config': 'scripts/app-config',
'angular': 'scripts/angular-1.3.15',
'angular-route': 'scripts/angular-route-1.3.15',
'angular-resource': 'scripts/angular-resource-1.3.15',
'angularAMD': 'scripts/angularAMD-4.13.2015',
'jquery': 'scripts/jquery-2.1.3.min',
'loginService': 'services/loginService'
},
// Add angular modules that does not support AMD out of the box, put it in a shim
shim: {
'angularAMD': ['angular'],
'angular-route': ['angular'], //angular-route depends on angular module
'angular-resource': ['angular']
},
// kick start application
deps: ['app-config']
});
Login.html
<div class="container" ng-controller="LoginController" ng-cloak>
loginService.js
define(['app-config'], function (app) {
app.factory('loginService', ['$resource',
function ($resource) {
return $resource(
'jbossews-1.0/login',
{}, {
login: { method: 'POST', cache: false, isArray: false }
});
}]);
});
Your controller should like this,Try this
"use strict";
define(['app-config', 'loginService'], function (app) {
app.register.controller('LoginController', ['$scope', '$location', 'loginService',
function (sc, loc, loginService) {
console.log("LoginController calling");
sc.login = function () {
console.log("Email Address = " + sc.EmailAddress);
console.log("Password = " + sc.Password);
};
}]);
});
and loginService.js should be like this
define(['app-config'], function (app) {
app.register.factory('loginService', ['$resource',
function ($resource) {
return $resource(
'jbossews-1.0/login',
{}, {
login: { method: 'POST', cache: false, isArray: false }
});
}]);
});
Note: This is something which you are missing, Let me try to explain here in more detail.(Considering you have a basic knowledge of require and angularAMD)
Controllers and services in the application rely on RequireJS to
access the object representing the application’s module and then
access the register property to register a controller script with
AngularJS.
This type of registration is required since using the standard
angular.module(“ModuleName”).controller() code won’t work properly
with dynamically loaded controller scripts.
RequireJS’s define() function to get to the app object and then uses
it to register the controller.
The app.register.controller() function points to AngularJS’s
$controllerProvider.register() function behind the scenes with app.js.
All of the controllers,services in the application follow this
pattern.
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
}]);
});
$stateProvider
.state('root', {
url: '',
views: {
'root.base': { templateUrl: '/templates/root/root.html' },
'root.sidebar': {
templateUrl: '/templates/root/root-sidebar.html',
controller: 'SomeDataController',
resolve: {
someData: function(DataService) { // DataService is an angular service for data retrieval
return DataService.getDataList(); // returns an array
}
},
}
}
});
If I attempt to run a resolve on a subview with angular-ui-router, the page simply comes up blank. If I omit the resolve, it loads fine -- but I need to resolve for some data before my controller is instantiated. Do I have my syntax correct? I've searched for examples high and low but can't seem to find any that match my situation.
Given my above example, shouldn't the "root-sidebar.html" have the scope of "SomeDataController", and shouldn't SomeDataController have the data resolved in the route definition declared above?
Your concept is working, maybe just some small settings are broken... I created working plunker
Here is the controller consuming someData
.controller('SomeDataController', function($scope, someData) {
$scope.data = someData
})
And here is our service loading some JSON and returning that as async:
.factory('DataService', ['$http',
function($http) {
return {
getDataList: function() {
return $http
.get("data.json")
.then(function(response) {
return response.data;
});
},
};
}
]);
The state defintion is unchanged (just adjusted to display the data):
$stateProvider
.state('root', {
url: '',
views: {
'root.base': {
template: '<div>root base view</div>',
},
'root.sidebar': {
template: '<div>root sidebar view' +
'<h5>the resolve data</h5>' +
'<pre>{{data | json}}</pre>' +
'</div>',
controller: 'SomeDataController',
resolve: {
someData: function(DataService) { // DataService is an angular service for data retrieval
return DataService.getDataList(); // returns an array
}
},
}
}
});
You can test that all here