I've been trying to figure this out for a few hours now, and I can't seem to find the problem. I've read some other questions with similar problems, but they don't have any solutions that have worked for me.
I am having trouble registering my controllers. I am not able to register controllers outside of the file in which I declare the app. Originally, I set up the 'MainController' in a separate file, which failed. I was getting an error saying that "The controller with the name 'MainController' is not registered". Once I put MainController in the same file as the app is declared, there were no problems. However, when I have a lot of code, I don't want all the controllers in the same file, as it will become too difficult to read. Here are examples of my code:
angular.module('myApp', ['ngRoute']);
angular.module('myApp')
.controller('MainController', MainController);
I am keeping other controllers in different files, and they are not registering. For example, in home.controller.js:
angular.module('myApp')
.controller('HomeController', HomeController);
function HomeController(HomeService) {
}
This controller will not register, and I don't know why. Each HTML partial in ng-view has its own controller, and the ng-view is within the MainController. Here is the app.config.js file:
angular.module('myApp')
.config(function($routeProvider, $locationProvider) {
$routeProvider.when('/home', {
templateUrl: 'views/home.html',
controller: 'HomeController as home'
}).when('/profile', {
templateUrl: 'views/profile.html',
controller: 'ProfileController as profile'
});
$locationProvider.html5Mode(true);
});
Here is index.html:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="utf-8">
<title>My App</title>
<script src="vendors/angular.min.js"></script>
<script src="vendors/angular-route.min.js"></script>
<script src="scripts/app.module.js"></script>
<script src="scripts/app.config.js"></script>
<scripts src="scripts/home.controller.js"></scripts>
<scripts src="scripts/profile.controller.js"></scripts>
<script src="scripts/main.service.js"></script>
<script src="scripts/home.service.js"></script>
<script src="scripts/profile.service.js"></script>
<base href="/" />
</head>
<body ng-controller="MainController as main">
<header>
<h1>My App</h1>
</header>
<!-- Content varies -->
<div class="container">
<ng-view></ng-view>
</div>
</body>
</html>
I have successfully built projects like this in the past without problem, but I can't find any issue compared to those projects. Any help is appreciated!
When I've had this issue in the past, it was related to script loading order, especially with using async script loading. You don't appear to be doing that.
To troubleshoot:
Fire a console log statement inside the controller's function body (console.log('registering controller x')). This statement will either not show up, or will show up after the error.
Angular used to (and I presume it still does) try to wait for app to load and all controllers to register to app before running the code. Either Angular isn't waiting on this controller, or this controller isn't running.
From there, you would verify that the reference to the file is correct (put a console.log at the top of the file), or determine how Angular decides when it believes all controllers are loaded and why it doesn't wait on your controller.
I haven't dealt with Angular since 1.2, because I think it's a pretty bad framework. But that was my experience then, and it seems like the same basic architecture for this. Back then it was relying on Document.ready. I really hope they don't do that anymore (that's where I ran into my async script loader problems).
Best of luck.
Related
I'm new to Angular and just started to build a test project to learn it, now have a problem with loading controllers OnDemand.
let's code a bit, I have the following HTML:
index.html
<body>
<div ng-app="MyApp">
CLICK ME
<div ng-view></div>
</div>
<script>
angular.module("MyApp",['ngRoute'])
.config(function($routeProvider) {
$routeProvider.when('/child', {
templateUrl: 'somwhere/child.html',
controller: 'childCtrl' <!-- will remove -->
});
}) <!-- will remove contoller defination below -->
.controller('childCtrl', function($scope) {
$scope.data = DoHeavyCalc();
});
</script>
</body>
what is obvious to me is that I should define my controller exactly where I config my module (MyApp), which doing this depends on DoHeavyCalc() which is not needed right now! (think this method does a big calculation, but it should be run only when the user clicks on the link, not at the beginning of the app).
Now I want to load the and define the controller inside child.html instead of my index.html. OK, so I removed the sections marked in above code and tried to write the child.html like this:
child.html
<div ng-controller="childCtrl">
{{data}}
</div>
<script>
angular.module("MyApp")
.controller('childCtrl', function($scope) {
$scope.data = DoHeavyCalc();
});
</script>
but is causes an error:
[ng:areg] `childCtrl` is not a function. got undefined.
also i tried to put script tag in child.html before the div tag, but it didn't affect anything.
Now my question is, how can i define and load the controller OnDemand and do my heavy work just when the user routes to a certain location not at the beginning of app?
You are asking about lazy loading ! By default angular doesn't support lazy loading, what you can do ? you can use any third party library like requirejs or others.
Currently angularjs doesn't execute any javascript inside templateUrl or template, more details
Here is a working example of lazy loading using requirejs.
Also there is some discussion regarding onDemand script loading
Lazy loading angularjs
In the child.html page do not include ng-controller attribute. Already child.html is associated with the childCtrl controller. Just remove the attribute ng-controller from the child.html page
I trying to learn web technologies (HTML, JS, angular, etc. what is needed to create pages). For practice I downloaded some kind of website template and noticed that there is a lot of same code in different html files(pages).
For example we have pages: login,main,about. Each of this page has same <header/> and <footer> and difference is only in <section/> between them.
So is it possible to have such structure: Main page with header and footer and on menu click changes only section between them? It will look like single page app.
As I use angularjs I know that there is such attribute as a ng-view but I am not sure if it will be suitable here, when, lets call, inner pages have this attribute inside also. (I tried I got RangeError: Maximum call stack size exceeded not sure probably some kind of infinitive loop appeared).
So what would be best solution in particular situation, I am not sure that my suggested structure is good, but I do not want to have same blocks of code in each page or it should be in HTML?
I use HTML, angular, JS, bootsrap for theme.
Examle
I copied two pages as example to plunker, so As you can see a lot of code is same, so I want to show login.html inside index.html (line: 172), but in login.html I also have ng-view (line:177). Maybe someone can with small code sample can show me how it is made?
http://plnkr.co/edit/iJrg2FJgwr9xxKTWMouX?p=preview
Yes, it's possible with AngularJs, and in fact very easy using ng-view or ui-view (from ui-router) .
Depending on your level of expertise I would suggest taking a look at ui-router which has all the ng-router functionalities and even more.
You can use ui-router to achieve this. You can define your states and templates using ui-router. Then for each state you can inject your views into sections marked as ui-view in your templates. Check out the example.
var myApp = angular.module("myApp", ["ui.router"]);
myApp.config(function($stateProvider) {
$stateProvider
.state('main', {
url: '/',
template: 'Main Page'
})
.state('list1', {
url: 'list1',
template: '<div>List1 {{test}}</div>',
controller: function($scope) {
$scope.test = "List 1";
}
})
.state('list2', {
url: 'list2',
template: '<div>List2 {{test}}</div>',
controller: function($scope) {
$scope.test = "List 2";
}
})
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js"></script>
<html ng-app="myApp">
<body>
<div><a ui-sref="main">Header</a> <a ui-sref="list1">List1</a> <a ui-sref="list2">List2</a>
</div>
<div ui-view=""></div>
<div>Footer</div>
</body>
</html>
A simple routing setup for an app using ngRoute would be something like this.
angular.module('stack-app', ['ngRoute']);
// set up the views at configure time
angular.module('stack-app').configure(['$routeProvider',
function($routeProvider) {
$routeProvider.when('/view1', { // http://website.com/view1
controller: 'View1Controller', // the controller for view1
templateUrl: 'view1.html' // this is a partial view
});
$routeProvider.when('/view2', { // http://website.com/view2
controller: 'View2Controller', // controller for view 2
template: '<div>inline template should do just fine</div>'
});
$routeProvider.otherwise('/view1'); // http://website.com/anythingelse points to /view1
}
]);
angular.module('stack-app').controller('StackMainController', function() {});
angular.module('stack-app').controller('View1Controller', function() {});
angular.module('stack-app').controller('View2Controller', function() {});
index.html here you would put the common parts, headers etc.
<html ng-app="stack-app">
<body ng-controller="StackMainController">
<!-- this will be replaced by the partial views -->
<ng-view></ng-view>
</body>
</html>
You will have to configure whatever server you are using to serve index.html when receiving requests for /view1 or /view2.
Also don't forget to import the angular-route.js file in your project.
You can also have other modules register routes in their own configure method, there's no need to have a central config.
However after writing up a few examples to play around with the controllers would not load. I was getting an error:
firstController is not a function
After some googling I found that Angular 1.3.x no longer supports global controllers. All the examples I have seen of the new way of creating controllers seem to create them in my app.js file. I am confused, does this now mean that I must create all my controllers here rather than having a dedicated file for each controller. I have tried this to create the controller and still no luck:
UPDATE: I changed my controller to match jedanput's answer but changed $scope to this.
app.controller('firstController', [function(){
this.name = "Tim";
}]);
Also I find it very annoying that all that the majority of the example out there still reference the way it was done in Angular 1.2.
Any help would be greatly appreciated as I am having trouble understanding this issue.
EDIT: Here is my index.html file. Hopefully this will help you guys understand what is going wrong.
<!DOCTYPE html>
<html lang="en" xmlns:ng="http://angularjs.org" id="ng-app" ng-app="myApp">
<head >
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>ControllerAs</title>
<meta name="description" content="">
</head>
<body>
<div class="content" ng-view=""></div>
<!-- jQuery -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<!-- AngularJS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.6/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.6/angular-route.js"></script>
<script type="text/javascript" src="../js/app.js"></script>
<!--Directives-->
<script type="text/javascript" src="../js/directives/it-works.js"> </script>
<!--Controllers-->
<script type="text/javascript" src="../js/controllers/firstController.js"></script>
<script type="text/javascript" src="../js/controllers/secondController.js"></script>
</body>
</html>
So far I have avoided Controllers as everything I have been doing could be done with directives and services but it is time I understood more about controllers. I think it may be something fundamental I am missing. Again any help is greatly appreciated.
UPDATE: still getting the same error. This is my app.js file. Maybe it can shed some light on the problem.
var app = angular.module('myApp',[
'ngRoute'
]);
app.config(function($routeProvider) {
$routeProvider.when('/', {
templateUrl: "../partials/test-skeleton.html"
})
});
It should be
app.controller('firstController', ['$scope', function($scope){
$scope.name = "Tim";
}]);
Also, controllerAs syntax is synthetic sugar for the scope simply, you avoid using this:
<div ng-controller="oneCtrl">
{{name}}
</div>
And instead use this:
<div ng-controller="oneCtrl as one">
{{one.name}}
</div>
Which helps tremendously when you have nested controllers.
You're right, Angular allows for multiple different notations and that can be annoying and confusing. I would recommend you to stick with the guidelines from John Papas Angular Style Guide. He uses this:
(function() {
'use strict';
// Get reference to your application
angular.module('myapp')
// Add the controller
.controller('mycontroller',controller);
// This makes the injection of the controller arguments
// explicit
controller.$inject = ['$scope', '$http'];
// Here the actual controller is defined - where
// the arguments are injected on the same location as
// in the previous array
function controller($scope, $http) {
// Controller logic
});
})();
You want to keep stuff out of the global space. Really - you do. That's why he wraps everything in an Immediately-Invoked Function Expression (IIFE).
Also - you want to explicitly define what you're injecting ( the $inject array ). If not, you will not be able to minify later.
So I'm sorry - I just added another way of defining your AngularJS artefacts. From what I understand, this is one the more well known style guides out there. I've heard that he's working closely with the Angular guys to make sure his style guide will also make it easier to transition to the new Angular version.
And no - you do not need to put everything in 1 file - just make sure you have a file with angular.module('myapp',[]) loaded before any of the other files. This will declare the myapp module and will append the controller to it.
As I'm writing this - I realize that there's also another way: you create a new module in this file, append the controller and then load that module into your application. But yeah ... it's confusing.
I've just started using AngularJS for a new app I'm looking at putting together but I've run into a problem when using routes and views.
I've stripped this example down to the bare minimum but the issue remains. All this example is doing is hitting the server and returning the index.html page, which then sources Angular etc.
index.html
<!doctype html>
<html lang="en" ng-app="main">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" src="css/style.css" />
<script type="text/javascript" src="js/ext/angular.min.js"></script>
<script type="text/javascript" src="js/ext/angular-route.min.js"></script>
<script type="text/javascript" src="js/main.js"></script>
<script type="text/javascript" src="js/test.js"></script>
<base href="/ui/">
</head>
<body>
<div ng-view></div>
</body>
</html>
main.js
(function() {
var app = angular.module('main', ['ngRoute', 'test']);
app.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider
.when('/test', {
templateUrl: 'html/test.html',
controller: 'TestCtrl'
})
.otherwise({
redirectTo: '/test'
});
$locationProvider.html5Mode(true);
}]);
})();
test.js
(function() {
var app = angular.module('test', []);
// get hierarchy
app.controller('TestCtrl', ['$scope', function($scope) {
alert('here');
}]);
})();
test.html
<div>FooBar!</div>
The alert gets fired infinitely but I just don't know why. I've seen other examples where ng-view and routing appear to be used exactly the same way, so I'm not sure what I'm doing wrong...
I had same problem sometime ago. Please, use firebug or some network control in the same browser at the developers tools panel where you can see the requests to the server for resources and then check that test.html file is requested and is correctly retrieved. It seems like the only one that is retrieved is the index.html and due this, the loop.
Probably you have to use this templateUrl value "/html/test.html" with "/" before. To localize this resource.
This is the idea that I'm proposing you. Localize the test.html resource with the correct way. I hope this can help you.
I had this issue today in March 2016. I have just found out what was causing the infinite loop when ng-view is present in the html (index.html in my case which is the initial html loaded at the start).
Ok, the problem was after all very simple one. I had this route provider setting in my app.js
angular.module('myapp',['ngRoute'])
.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl:'/index.html',
controller:'MyAppCtrl'
})
Since the initial html loaded is index.html, and the url at that point is '/', and the routeProvider invokes the code at when '/'. Yes, it loads index.html again, and again and again and again... till it dies. The solution is not to set index.html as the templateUrl for the '/' route. The html (template) should not include <div ng-view></div>.
Here's how I've done it, example here
Code
.config(['$routeProvider', '$locationProvider',function($routeProvider, $locationProvider) {
$routeProvider
.when('/test', {
template: '<div>test</div>',
controller: 'testCtrl'
})
.when('/other', {
template: '<div>Delete</div>',
controller: 'otherCtrl'
})
.otherwise({
redirectTo: '/test'
});
$locationProvider.html5Mode(true);
}]);
Ok, I solved my problem. I've accepted sergio's as it was closest to how I realised what the problem was - my app was requesting the html file from the application server, which is set up to return the index.html file as a default action. As the html request had no associated action, the default response of returning index.html was kicking in instead of the test.html file.
Once I changed the url so it was getting the html file from the web server, everything worked great. If I'd taken a moment earlier to actually think through what was happening, it would've been obvious.
Thanks for the responses!
Very new to AngularJS, I am guessing the term for what I am trying to do is lazy load. I have looked at several different blogs and I have not found a complete working solution that is purely using AngularJS.
I understand that if I put the <script src="js/process1.js"></script> in index.html, all works fine, I am trying to cut down on the amount of js that is pulled down on the initial load.
With the script tag sitting in the partial, it is never loaded so the P1Ctrl is never created. So currently, if a user go into the application and never goes to process55, the user still has the code there for process55 even though it was never used.
Is there a way to load the file and inject the objects created in the process1.js into the app defined in main, at the time process1 route is executed?
index.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Large Angular App</title>
<link rel="stylesheet" href="lib/foundation/css/foundation.min.css" />
</head>
<body ng-app="largeApp" ng-controller="LargeAppController">
<div>
Home | Process1
</div>
<br/>
<br/>
<br/>
<ng-view>Test</ng-view>
<script type="text/javascript" src="lib/jquery/jquery.min.js"></script>
<script type="text/javascript" src="lib/angular/angular.js"></script>
<script type="text/javascript" src="lib/angular/angular-route.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</body>
</html>
js/main.js:
var app = angular.module("largeApp", ['ngRoute']);
var appCtrl = app.controller("LargeAppController", function(){});
app.config(function ($routeProvider, $controllerProvider) {
// save references to the providers
app.registerCtrl = $controllerProvider.register,
$routeProvider.when('/', {templateUrl: 'partials/home.html'});
//Thinking I need to set up a resolve to fire off a script loader to load js.
$routeProvider.when('/process1', {templateUrl: 'partials/process1/process1.html'});
$routeProvider.otherwise({redirectTo: '/'});
});
partials/home.html:
<div>
Home Page
</div>
partials/process1.html:
<script type="text/javascript" src="js/process1/Process1Controller.js"></script>
Process 1 {{process1data}}
js/process1.js:
console.log("I made it here");
app.registerCtrl('Process1Controller',function($scope){
$scope.process1data = "Hello!";
}
]);
To implement lazy loading of controllers in simple way, you have to do the following:
Save $controllerProvider.register (which is the only method to add a controller into already bootstrapped AngularJS app) to variable in your app (main.js):
var app = angular.module('app',["ngRoute"]);
app.config(['$routeProvider', '$controllerProvider',
function($routeProvider, $controllerProvider) {
// remember mentioned function for later use
app.registerCtrl = $controllerProvider.register;
//your routes
$routeProvider.when('/', {templateUrl: 'partials/home.html'});
$routeProvider.when('/process1', {templateUrl: 'partials/process1.html'});
$routeProvider.otherwise({redirectTo: '/'});
}
]);
process1.html:
<script src="js/process1.js"></script>
<div ng-controller="P1Ctrl">
{{content}}
</div>
And now, in process1.js you use our registerCtrl:
app.registerCtrl('P1Ctrl', function($scope)
{
$scope.content = '...';
});
index.html probably remains the same. Check if your process1.js is being loaded (simply using console.log() right in the body of process1.js, not in P1Ctrl controller). If it isn't, include jQuery before Angular:
<script src="lib/jquery/jquery.js"></script>
<script src="lib/angular/angular.js"></script>
IMPORTANT: This method doesn't work with angular 1.2.0-rc.2 and 1.2.0-rc.3, because this little trick with jQuery doesn't work.
For more complex (and prettier) solution, with .js files as dependencies in route definitions, check that article: http://ify.io/lazy-loading-in-angularjs/ - it also works with rc.2 and rc.3. Here is plunk implementing described method: http://plnkr.co/edit/ukWikO5TVDtQ1l9WlrGD