Load Angularjs controllers in ngView - javascript

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

Related

Angular: A controller with this name is not registered

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.

How to include one angularjs based SPA into another angularjs based SPA using nested ng-app which are attached to html tag?

Angular module is defined as :
angular.module('module1',['ngRoute']);
Route for module1 is defined as
module1.config(function($routeProvider)
{
$routeProvider
.when('/Application1',
{
templateUrl: 'Application1/Application1_HomePage.html',
})
.otherwise({
templateUrl: 'MainPage.html',
controller: 'ctrl'
)}
});
And the controller ctrl is defined as:
module1.controller('ctrl',function($scope,$location)
{
$scope.goToApp1 = function()
{
$location.path('/Application1',true);
}
});
The shell page is as :
<html ng-app="module1">
<body>
<div ng-view> </div>
</body>
</html>
Application1_HomePage.html contains another ng-app than the shell page ng-app module1.
From the shell page shown above, when a call to Application1_HomePage.html is made through routeProvider, initialization of ng-app written inside Application1_HomePage.html is failing. The browser inspector clearly shows nothing out of the DOM.
Considering the above scenario how can I make a navigation from one ng-app to another ng-app?
Assuming both of the modules have an entrance page you can just put a link to the other app page, for example:
you load module1Index.html and on that page you put an link to module2Index.html:
If you need both modules to sit on the same page , just include one in the other:
angular.module('module1', ['module2']);

HTML avoid coping blocks of code

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.

How to register existing elements with Angular?

Fairly new to Angular and working inside of an existing code base.
Basically, there's an element that exists within the root document (index.html) that already exists in the html before the Angular library loads. Because of this, the ng-click directive isn't registered.
Is there an easy way that I can pass Angular a reference to the element in question and have it register that as one of its own?
Sample code (obviously missing parts, just to illustrate Angular loads after):
<html>
<body ng-app="allMyCookiesApp">
<a ng-click="giveGeuisACookie()">GIMME</a>
<script src="angular.js"></script>
</body>
</html>
I'd like to get a cookie when I click GIMME.
ng-app will bootstrap everything inside it once angular loads. This includes compiling and linking the ng-click in your example. So I think the real problem may be elsewhere.
The biggest omission from this example is any controller. I expect you are missing a controller that can place the giveGeuisACookie method on the correct scope to be used by ng-click. For example
angular.module('allMyCookiesApp', [])
.controller('geuisCtrl', function($scope) {
$scope.giveGeuisACookie = function() {
// Cookie time
};
});
would define your module for ng-app and register a controller for it. This controller will add the giveGeuisACookie function to its scope.
<html>
<body ng-app="allMyCookiesApp" ng-controller="geuisCtrl">
<a ng-click="giveGeuisACookie()">GIMME</a>
<script src="angular.js"></script>
</body>
</html>
tells angular to use the controller so that ng-click will have access to the correct method.
If this is not the problem it may be worth adding a jsfiddle with a working (or not) example of what you are doing.

Javascript single page architecture with module autonomy

I just started this HTML5 project where we decided to make it a single page architecture by leveraging jQuery $.load() method. Unfortunately, as soon as the JS started to grow, we quickly started running into issues where the modules loaded into the master dashboard have no knowledge of their parent.
The architecture looks like this:
dashboard.html (master file)
moduleA.html
moduleA.js
moduleB.html
moduleB.js
moduleC.html
moduleC.js
Since we decided to also keep the JS as separate files, we are having to load all JS files through dashboard.html in order to invoke them individually when modulex is loaded.
So when loading moduleA.html into the dashboard we have to call its corresponding JS. To do this we simply wrote the JS using a Module Pattern so we can easily invoke it by doing a function call, like:
<script>
moduleA
</script>
or this if we want to access a specific property of this member.
<script>
moduleA.someMethod();
</script>
Now, I know there are is gotta be a nicer way of doing this, right? I hate having to have script tags in the HTML modules in order to load its corresponding JS file.
Another limitation of this is the fact that we no longer can work on modules individually, since the scripts and CSS invocation happens on the parent (dashboard.html) so certainly when moduleA.html is loaded directly, it is pure HTML with no script or CSS.
I looked through the other questions but I didn't see anyone with the same problem.
I looked at AngularJS, EmberJS, KO.JS and BoilerPlateJS but none of them addresses what we are trying to accomplish. The only one that has a similar single page concept is jQuery Mobile but I don't know if you can switch from jQuery to jQuery Mobile and everything remains working.
Has anyone face this issue yet? Is there a solution or would I have to go with a custom solution.
Thanks!
I could argue about AngularJS with you. It is exactly what you need
dashboard.html is layout with some directives attached, but power lies in AngularJs if you use ng-view directive
here is example:
dashboard.js
var app = angular.module("modularApp",[]);
app.config(['$routeProvider', "$locationProvider", function routes($routeProvider, $locationProvider) {
$routeProvider.when('/dashboard', {
controller:'HomeCtrl',
templateUrl:'templates/home.html'
});
$routeProvider.when('/moduleA', {
controller:'ModuleACtrl',
templateUrl:'templates/moduleA.html'
});
$routeProvider.when('/moduleB', {
controller:'ModuleBCtrl',
templateUrl:'templates/moduleB.html'
});
$routeProvider.otherwise({redirectTo: "/dashboard"});
}]);
templates/dashboard.html
<html ng-app="modularApp">
<head>
<!--.... include angular minified js file and what else you need...-->
<script type="text/javascript" src="dashboard.js"></script>
<script type="text/javascript" src="moduleACtrl.js"></script>
<script type="text/javascript" src="moduleBCtrl.js"></script>
</head>
<body>
<a ng-href="#/moduleA">Open Module A View</a>
<a ng-href="#/moduleB">Open Module B View</a>
<!-- Add widgets header menus .... -->
<ng-view></ng-view>
</body>
</html>
moduleACtrl.js
var app=angular.module("modularApp");
app.controller("ModuleACtrl",function($scope){
$scope.scopeValue="Hellow from view";
});
moduleBCtrl.js
var app=angular.module("modularApp");
app.controller("ModuleBCtrl",function($scope){
$scope.scopeValue="Hellow from another view";
});
templates/moduleA.html
<div>{{scopeValue}} in module A</div>
templates/moduleB.html
<div>{{scopeValue}} in module B</div>
You can do more complex things with angular then just this. All depends on your needs. Do you have any special requirements :)
Also, you could create your own directive, like ng-view and use your own $route service and $routeProvider so you can add css and javascript you want to dynamically load when some rute match url.
so instead of above routing table, you could have
app.config(['$myRouteProvider', "$locationProvider", function routes($routeProvider, $locationProvider) {
$routeProvider.when('/dashboard', {
javascript:'javascript/dashboard.js',
templateUrl:'templates/dashboard.html',
cssfile: 'css/dashboard.css'
});
$routeProvider.when('/moduleA', {
javascript:'javascript/moduleA.js',
templateUrl:'templates/moduleA.html',
cssfile: 'css/moduleA.css'
});
$routeProvider.when('/moduleB', {
javascript:'javascript/moduleB.js',
templateUrl:'templates/moduleB.html',
cssfile: 'css/moduleB.css'
});
$routeProvider.otherwise({redirectTo: "/dashboard"});
}]);
But that is, pardon on my French, stup. There are couple libs I tried in ruby on rails to acheive similar, but backend is rendering content, or just part of content. But I'm not sure which backend you are using and are you interested to switch to rails anyway.
DomController in BoilerplateJS does what you need, without using any custom HTML attributes. Your dashboard.html can just have place holders where you want to inject your components. I'm just pulling out some html below from BoilerplateJS index.html to show how it works:
<body>
<section id="page-content">
<header>
<section class="theme"></section>
<section class="language"></section>
</header>
<aside>
<section class="main-menu"></section>
</aside>
</section>
</body>
theme, language and main-menu sections above are just place holders in to which relavant components would be injected by the DomController. The DomController can be now used to register the components with appropriate selectors as below:
//scoped DomController that will be effective only on $('#page-content')
var controller = new Boiler.DomController($('#page-content'));
//add routes with DOM node selector queries and relavant components
controller.addRoutes({
".main-menu" : new MainMenuRouteHandler(context),
".language" : new LanguageRouteHandler(context),
".theme" : new ThemeRouteHandler(context)
});
controller.start();
Above code is extracted from "/boilerplatejs/src/modules/baseModule/module.js"

Categories