AngularJS: weird controller call with module organization - angularjs

I've made a module code structure, here is my index.html:
<!doctype html>
<html>
<head>
<!-- AngularJS -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.18/angular.min.js"></script>
<script src="https://code.angularjs.org/1.3.0-beta.18/angular-route.min.js"></script>
<!-- Lazy loading module -->
<script src="assets/libs/ocLazyLoading.min.js"></script>
<!-- App -->
<script src="app/app.js"></script>
<script src="app/config.js"></script>
<script src="app/article/article.js"></script>
<base href="http://localhost/angularjs-blog/">
</head>
<body data-ng-app="Blog">
<div data-ng-view></div>
</body>
</html>
app.js:
var app = {};
app.main = angular.module('Blog', [
// Tools
'ngRoute',
'oc.lazyLoad',
// Modules
'Blog.Article'
]);
config.js:
app.main.config(['$routeProvider', function ($routeProvider) {
$routeProvider.otherwise({
redirectTo: '/'
});
}]);
article.js:
app.article = angular.module('Blog.Article', ['ngRoute', 'oc.lazyLoad']);
app.article.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'app/article/views/articles.html',
controller: 'ArticlesController',
resolve: {
lazy: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'Blog',
files: [
'app/article/controllers/articles.controller.js',
'app/article/article.service.js',
'common/directives/button.directive.js'
]
});
}]
}
})
.when('/article/:id', {
templateUrl: 'app/article/views/article.html',
controller: 'ArticleController',
resolve: {
lazy: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'Blog',
files: [
'app/article/controllers/article.controller.js',
'app/article/article.service.js'
]
});
}]
}
});
}]);
article.controller.js:
// Controller
app.article.controller('ArticleController', ['$scope', 'ArticleService', function ($scope, ArticleService) {
}]);
This is the problem, I can't write app.article.controller as my router does not find "ArticleController". I have to write app.main.controller instead. And it seems weird for me. I've created an object property for this module, and I don't want to see the controller in the main property, right?
I really don't understand what's going on. I'm using AngularJS version 1.3.0-beta.18.
ANSWER: As I my button.directive.js was in app.main, the right routing is:
app.article = angular.module('Blog.Article', ['ngRoute', 'oc.lazyLoad']);
app.article.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'app/article/views/articles.html',
controller: 'ArticlesController',
resolve: {
lazy: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load([{
name: 'Blog',
files: ['common/directives/button.directive.js']
}, {
name: 'Blog.Article',
files: [
'app/article/controllers/articles.controller.js',
'app/article/article.service.js'
]
}]);
}]
}
})
.when('/article/:id', {
templateUrl: 'app/article/views/article.html',
controller: 'ArticleController',
resolve: {
lazy: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'Blog.Article',
files: [
'app/article/controllers/article.controller.js',
'app/article/article.service.js'
]
});
}]
}
});
}]);

from documentation
controller – {(string|function()=} – Controller fn that should be associated with newly created scope or the name of a registered controller if passed as a string.
when you are writing ArticleController angular don't know in which namespace to look, because your controller stored in other module therefore you must provide the right reference (namespace) for your controller
UPDATE 1:
the problem was that what you embedded the angular lib after body and your script (jsffidlle script) worked before angular had been attached let me update example
This is an updated example

Related

How to start my asp.net mvc application with the Angular hashbang url?

When I start my asp.net mvc application (www.mysite.com), the home controller index action is loaded.
This action serves a view, and in that view my AngularJS app is loaded.
However, my AngularJS app wants to be started at www.mysite.com/#!/
Is there a way to force this, that when you go to the root that it gets redirected to /#!/ ?
What I have now is this:
<system.webServer>
<defaultDocument enabled="true">
<!-- this line enables default documents for a directory -->
<files>
<clear/>
<!-- removes the existing default document list -->
<add value="/#!/"/>
</files>
</defaultDocument>
but as you can guess this doesn't work.
I tested it at my local IIS and at Azure.
EDIT
To clarify: If I don't put the /#!/ at the end or the url, Angular does not kick in. Maybe that is the problem, that I do not have my config right?
my home state is this:
.state(appConfig.routes.home, {
url: "/",
views: {
'content': { templateUrl: "/Js/views/home/home.html", controller: 'ikvhomecontroller as vmhome' },
'homeheader#index.home': { templateUrl: "/Js/views/shared/sharedheader.html", controller: 'sharedheadercontroller as vmshhe' }
}
})
This is my config:
(function () {
'use strict';
angular.module(Main.config.name, [
// Angular modules
'ngResource',
'ngSanitize',
// Custom modules
// 3rd Party Modules
'ui.router',
'ngAnimate'
])
.constant('appConfig', Main)
.config(['$locationProvider', '$stateProvider', 'appConfig', '$httpProvider', '$urlRouterProvider', function ($locationProvider, $stateProvider, appConfig, $httpProvider, $urlRouterProvider) {
console.log('yep, I am at .config');
if (appConfig.useHtml5Mode)
$locationProvider.html5Mode(true);
else {
$locationProvider.html5Mode(false);
$locationProvider.hashPrefix('!');
};
$urlRouterProvider.otherwise("/");
$urlRouterProvider.when('/', 'index.home');
$stateProvider
.state('index', {
url: "",
views: {
"#": { templateUrl: "/Js/views/main.html" },
"header#index": { templateUrl: "/Js/views/header.html", controller: "menucontroller as vm" },
'content#index': { templateUrl: "/Js/views/content.html" },
"footer#index": { templateUrl: "/Js/views/footer.html" }
}
})
.state(appConfig.routes.home, {
url: "/",
views: {
'content': { templateUrl: "/Js/views/home/home.html", controller: 'homecontroller as vmhome' },
'homeheader#index.home': { templateUrl: "/Js/views/shared/sharedheader.html", controller: 'sharedheadercontroller as vmshhe' }
}
});
}])
.run([
'$http',
'$state',
'$rootScope',
'$templateCache',
function ($http, $state, $rootScope, $templateCache) {
$rootScope.$on("$stateChangeError", console.log.bind(console));
$rootScope.$on('$locationChangeSuccess', function (evt) {
console.log(evt);
});
$templateCache.removeAll();
}]);
})();
This is the html in the browser:
<div id="main" ui-view>
<div class="imgwrapper"><img src="/Images/iloading.gif" /></div>
</div>
<script src="/Scripts/jquery-2.2.0.js"></script>
<script src="/Scripts/modernizr-2.8.3.js"></script>
<script src="/Scripts/moment-with-locales.js"></script>
<script src="/Scripts/uihelpers/currencykeydown.js"></script>
<script src="/Scripts/uihelpers/datekeydown.js"></script>
<script src="/Scripts/uihelpers/labeltitleclick.js"></script>
<script src="/Scripts/uihelpers/textareagrow.js"></script>
<script src="/Scripts/respond.js"></script>
<script src="/Scripts/angular.js"></script>
<script src="/Scripts/angular-route.js"></script>
<script src="/Scripts/angular-resource.js"></script>
<script src="/Scripts/angular-sanitize.js"></script>
<script src="/Scripts/angular-animate.js"></script>
<script src="/Scripts/angular-locale_nl-nl.js"></script>
<script src="/Scripts/angular-ui-router.js"></script>
<script src="/Scripts/config.js"></script>
<script src="/Scripts/App.js"></script>
<script src="/Scripts/helpers/calcutils.js"></script>
<script src="/Scripts/helpers/dateutils.js"></script>
<script src="/Scripts/helpers/SelectHelper.js"></script>
and the rest of my custom code goes here
you should modify your 'route.config.js' in angularjs like as -
angular.module('yourModuleName').config(['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
$stateProvider.
state('login', {
url: 'login',
templateUrl: '/path/to your login page',
});
$urlRouterProvider.otherwise("/");
$urlRouterProvider.when('/', 'login');
}
]);
This will automatically route your page to localhost/#!/login when you start your application.

appCtrl is not a function, got undefined error triggered when trying to load appCtrl (ON THE FLY)

I want to load controllers on the fly when needed rather than loading them in one go. So, for the I've implemented dynamic approach which works fine without any error. It also works well with Ui-Router.
But the problem is in Index.html page. I want to put global (super parent) controller name "appCtrl". As this appCtrl should be initialized when I run my app. For that I need to write like ng-controller="appCtrl" or ng-controller="appCtrl as vm" at body tag.
But when I do it, it gives error that appCtrl is a function, got undefined. I tried sever ways but still unable to identify exact error. I have working on this issue since two to three days but still not able to identify it.
I have made this plunker.
look at body tag of index.html.
main.js
require.config({
paths: {
"angular": "//cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.0-rc.1/angular",
"ui-router": "//rawgit.com/angular-ui/ui-router/0.2.15/release/angular-ui-router"
},
shim: {
"angular": {
exports: 'angular'
},
"ui-router": {
deps: ['angular']
}
}
});
define(
['angular',
'app',
'controllers/appCtrl'],
function (angluar, app) {
angular.bootstrap(document, ['MyApp'])
});
app.js
define([
'angular',
'ui-router'
], function (angular) {
var app = angular.module('MyApp', ['ui.router']);
function lazy () {
var self = this;
this.resolve = function (controller) {
return {
ctrl: ['$q', function ($q) {
var defer = $q.defer();
require(['controllers/' + controller], function (ctrl) {
app.register.controller(controller, ctrl);
defer.resolve();
});
return defer.promise;
}]
};
};
this.$get = function (){
return self;
};
}
function config ($stateProvider, $urlRouterProvider,
$controllerProvider, $compileProvider,
$filterProvider, $provide, lazyProvider) {
$stateProvider
.state("home", {
url: "/",
controller: 'homeCtrl',
controllerAs: 'vm',
templateUrl: 'views/homeView.html',
resolve: lazyProvider.resolve('homeCtrl')
});
app.register = {
controller: $controllerProvider.register,
directive: $compileProvider.directive,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service,
constant: $provide.constant
};
}
app.provider('lazy', lazy);
app.config(config);
return app;
});
Index.html
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.17/require.js" data-main="main.js"></script>
</head>
<body ng-controller="appCtrl as vm"> // I want this to work correctly but it is not getting loaded dynamically. I don't know why. Help me to resolve it.
<a ui-sref="home">go home</a>
<ui-view></ui-view>
{{vm.appVar}}
</body>
</html>
You are actually missing the controller statement in your controller code.
Use the following code
define(['app'], function (app){ //Updated Line
console.log('app controller loaded');
app.controller('appCtrl',ctrl); //New Line added
ctrl.$inject = ['$http'];
function ctrl ($http) {
this.appVar = 'hi from appCtrl';
};
return ctrl;
});
See the plunker

I can't get where that "Error: $injector:unpr Unknown Provider" is from

All works fine if I don't inject into controller "getChildrenNumber", after then I get the error from $injector.
I've read even this https://docs.angularjs.org/error/$injector/unpr, but it seems not be one of that cases
HTML (index.html):
<html class="no-js" ng-app="myApp" ng-strict-di>
.
.
.
<script src="jsLib/angular_v1.4.js" type="text/javascript"></script>
<script src="jsLib/angular-ui-router.min.js" type="text/javascript"></script>
<script src="jsLib/angular-ui-router-statehelper.min.js" type="text/javascript"></script>
<script src="app.js" type="text/javascript"></script>
<script src="routes.js" type="text/javascript"></script>
routes.js:
var config = ["$urlRouterProvider", "$stateProvider", "$locationProvider", function($urlRouterProvider, $stateProvider, $locationProvider){
$urlRouterProvider.otherwise("/multi");
$stateProvider
.state("multi",{
url: "/multi",
views: {
"": {
templateUrl: "multipleView.htm",
controller: "MainCTRL",
controllerAs: "ctrl"
},
"viewA#multi": {
templateUrl: "testingBlock.htm",
controller: "MainCTRL",
controllerAs: "ctrl"
},
"viewB#multi": {
templateUrl: "app/templates/login.htm",
controller: ["$scope", "getChildrenNumber", function($scope, getChildrenNumber){
$scope.nameApp = "nameAppChanged";
$scope.children = getChildrenNumber + " Miao";
}]
}
},
resolve: {
getChildrenNumber: ["$http", function($http){
return "Some response from an API";
}]
}
});
}];
angular
.module("myApp")
.config(config);
app.js:
angular
.module("myApp", ["ui.router"])
.controller("MainCTRL", ["$scope", "getChildrenNumber", function($scope, getChildrenNumber){
this.nameApp = "myApp";
$scope.children = getChildrenNumber;
}]);
You are implementing it the wrong way..
Firstly, in routes.js
$stateProvider
.state("multi",{
url: "/multi",
views: {
// Views Code here //
},
resolve: {
getChildrenNumber: getChildrenNumber
}
});
}];
then you have to create your factory:
angular.module('myApp')
.factory('children', ['$http', function ($http) {
return {
getChildrenNumber: getChildrenNumber
};
function getChildrenNumber() {
return "Some response from an API";
}
}]);
After this you can inject it in your controller as children

Implement RequireJs in AngularJs UI-Router

I have application in angularJs and it will have different modules with different JS files.for js file optimization I am going to implement requireJS.
There is (broken) plunker
My angularJs code is like this in app.js:
var app = angular.module("webapp", ['ngRoute']);
app.run(['$rootScope', '$state','$urlRouterProvider',
function ($rootScope, $state,$urlRouterProvider) {
$urlRouterProvider.otherwise('/index.html');
$stateProvider
.state('root.home',{
url: '/index.html',
views: {
'header': {
templateUrl: 'modules/header/html/header.html',
controller: 'headerController'
},
'content-area': {
templateUrl: 'modules/home/html/home.html',
controller: 'homeController'
},
'footer': {
templateUrl: 'modules/common/html/footer.html',
controller: 'footerController'
}
},
data: {
displayName: 'Home',
}
})
.state('root.about',{
url: '/index.html',
views: {
'header': {
templateUrl: 'modules/header/html/header.html',
controller: 'headerController'
},
'content-area': {
templateUrl: 'modules/home/html/about.html',
controller: 'aboutController'
},
'footer': {
templateUrl: 'modules/common/html/footer.html',
controller: 'footerController'
}
},
data: {
displayName: 'About',
}
})
}]);
I added the following code in my main.js file
require.config({
baseUrl: "",
// alias libraries paths. Must set 'angular'
paths: {
'angular': 'http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min',
'angular-route': 'http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-route.min',
'angularAMD': 'http://cdn.jsdelivr.net/angular.amd/0.2.0/angularAMD.min'
},
// Add angular modules that does not support AMD out of the box, put it in a shim
shim: {
'angularAMD': ['angular'],
'angular-route': ['angular']
},
// kick start application
deps: ['app']
});
and also added requirejs in html
<head>
<link rel="stylesheet" href="style.css">
<script data-main="main.js" src="http://marcoslin.github.io/angularAMD/js/lib/requirejs/require.js"> </script>
<script src="js/app.js"></script>
</head>
how can I define requirejs module or implement with my angularjs UI-Rooter?
EXTEND:
In your Example You added the above code in app.js
define([], function() {
var app = angular.module('webapp');
return app;
})
I added above code to script.js.Also In my app.js file contain all the UI router things and I changed the main.js with following code
require.config({
//baseUrl: "js/scripts",
baseUrl: "",
// alias libraries paths
paths: {
// here we define path to NAMES
// to make controllers and their lazy-file-names independent
"testController" : "modules/test/js/controller/testController",
},
deps: ['js/script'] // changed section
});
but in my browser console I am gettnig this error "NetworkError: 404 Not Found .../default/app.js" .how can I solve this issue.I directly added this js file through the html.but I am getting this error.
As discussed in comments and related to this plunker: http://plnkr.co/edit/iV7fuG5mkoTQ2JoKk9e0?p=preview
I followed the Q & A:
angular-ui-router with requirejs, lazy loading of controller
And updated that plunker and make it working.
There are many changes. E.g. we should keep a reference to controller providers:
var app_cached_providers = {};
app.config(['$controllerProvider',
function(controllerProvider) {
app_cached_providers.$controllerProvider = controllerProvider;
}
]);
And inside of our mapped controllers (e.g. controller_home.js) register that:
define(['app'], function (app) {
// the Content Controller
// is added into the 'app' module
// lazily, and only once
app_cached_providers
.$controllerProvider
.register('HomeCtrl', function ($scope) {
$scope.message = "Message from HomeCtrl";
});
});
Also, this would be a helper method to make other stuff a bit simplier
var loadController = function(controllerName) {
return ["$q", function($q) {
var deferred = $q.defer();
require([controllerName], function() {deferred.resolve(); });
return deferred.promise;
}];
}
And here we will use it to extend state definitions. Firstly the root state:
$urlRouterProvider.otherwise('/root');
$stateProvider
.state('root',{
url: '/root',
templateUrl: 'view_root.html'
});
Now states loading controller async way:
var root_home = {
//url: '/index.html',
url: '/home',
views: {
'' : {templateUrl: 'view_home.html', controller: 'HomeCtrl' },
},
data: {
displayName: 'Home',
},
resolve : { }
};
root_home.resolve.loadTopMenuCtrl = loadController("HomeCtrl");
var root_about = {
//url: '/about.html',
url: '/about',
views: {
'' : {templateUrl: 'view_view1.html', controller: 'View1Ctrl' },
},
data: {
displayName: 'About',
},
resolve : { }
};
root_about.resolve.loadContentCtrl = loadController("View1Ctrl");
$stateProvider
.state('root.home', root_home)
.state('root.about', root_about)
Check it all in action here

Angular lazy loading controller not found exception

I have tried to lazy load a controller in angularjs app, but got stuck with some problems i.e. view loads after the controller but in console throws exception controller not found.
Here i have attched the sample code.
index.html:
Click here
<div data-ng-view>
</div>
<script src="js/angular.min.js"></script>
<script src="js/angular-route.min.js"></script>
<script src="js/ocLazyLoad.min.js"></script>
<script src="js/app.js"></script>
<script src="js/cofig.js"></script>
</body>
</html>
app.js:
var testapp = angular.module('test',['ngRoute','oc.lazyLoad']);
config.js:
testapp.config(['$routeProvider',function($routeProvider){
$routeProvider.when('/test1',{
templateUrl: 'partials/test.html',
controller: 'testController',
resolve: {
lazy: ['$ocLazyLoad',function($ocLazyLoad) {
$ocLazyLoad.load([{
name: 'test',
files: ['js/testController.js']
}]);
}]
}
})
}]);
console.log error:-
enter code here`Error: [ng:areq] http://errors.angularjs.org/1.3.7/ng/areq?p0=testController&p1=not%20aNaNunction%2C%20got%20undefined
This issue is because of you are loading the controllers lazily. When you load the controller lazily, this will not be available to the main module. So you have to register those controllers.
By attaching the controller provider and provide to the main module, we can use it later to register controller.
In app config,
SampleApp.registerController = $controllerProvider.register;
SampleApp.$register = $provide;
To register controller:
var SampleApp = angular.module('SampleApp');
SampleApp.$register.factory('landingService', ['$http', '$rootScope', function ($http, $rootScope) {
debugger
return {
GetQuestions: function () {
return $http({
url: ""
});
}
}
}]);
SampleApp.registerController('landingController',
['$scope', '$http', 'landingService', function (scope, http, landingService) {
scope.GetQuestions = function () {
landingService.GetQuestions().success(function (data) {
}).
error(function (data) {
});
}
}]);
The following tutorial will help you to implement the Angular Lazy Loading using RequireJS with OcLazyLoad.
Angular Lazy Load implementation
You should add the lazyLoad service to your config injection:
testapp.config(['$routeProvider', '$ocLazyLoad', function($routeProvider, $ocLazyLoad){
$routeProvider.when('/test1',{
templateUrl: 'partials/test.html',
controller: 'testController',
resolve: {
lazy: ['$ocLazyLoad',function($ocLazyLoad) {
$ocLazyLoad.load([{
name: 'test',
files: ['js/testController.js']
}]);
.......
[UPDATED]
I guess you defined your test controller in the js file and want to lazy load the controller. But your code is not correct. Try this:
testapp.config(['$routeProvider', '$ocLazyLoad', function($routeProvider, $ocLazyLoad){
$routeProvider.when('/test1',{
templateUrl: 'partials/test.html',
controller: 'testController',
resolve: {
lazyTestCtrl: ['$ocLazyLoad',function($ocLazyLoad) {
return $ocLazyLoad.load('js/testController.js');
}]
}
You can get the doc from official site.

Resources