Handling multiple controllers for same template in AngularJS? - javascript

I have a requirement wherein a single page contains lot of data. All these data are though different but needs to be displayed in one page only (with vertical scroll).
I have made various collapsible divs on my page and they are controlled usig ng-if for avoiding too much of DOM.
Initially for prototype, I had JS logics for all these divs in one controller. However, now I am following a different approach wherein I want one controller for the page and various child controller for the collapsible divs.
I kept one object each for a div in page controller and made several other controller on my main module (app.js). I expect each of them to be child of pageCtrl and as such I should be accessing data for each div in 'pageCtrl`.
However, this is not working. No errors though on consoles, but no divs are visible.
My structure is as:
<body ng-app ="mainModule">
<div ng-controller ="pageCtrl">
<div ng-controller = "collapse1Ctrl" ng-include="collapse1.tpl" ng-if="collapse1reqd">
<div ng-controller = "collapse2Ctrl" ng-include="collapse2.tpl" ng-if="collapse2reqd">
<div ng-controller = "collapse3Ctrl" ng-include="collapse3.tpl" ng-if="collapse3reqd">
<div ng-controller = "collapse4Ctrl" ng-include="collapse4.tpl" ng-if="collapse4reqd">
<div ng-controller = "collapse5Ctrl" ng-include="collapse5.tpl" ng-if="collapse5reqd">
</div>
JS is like:
angular.module("mainModule", []);
angular.module("mainModule").controller("pageCtrl", pageCtrlFn);
angular.module("mainModule").controller("collaspse1Ctrl", collaspse1CtrlFn);
angular.module("mainModule").controller("collaspse2Ctrl", collaspse2CtrlFn);
angular.module("mainModule").controller("collaspse3Ctrl", collaspse3CtrlFn);
angular.module("mainModule").controller("collaspse4Ctrl", collaspse4CtrlFn);
angular.module("mainModule").controller("collaspse5Ctrl", collaspse5CtrlFn);
Rest assured, all templates and conditions are at correct places.
Since my code as such is too large, I am avoiding to post it currently. May be I will post a similar fiddle soon.
But currently I wonder if is there any problem with having such a structure at all ??

Based on the OP's question and the problem's faced here's an example demo on how nested controllers work and how the scope behaves.
HTML looks like
<body ng-app="scopeInheritance">
<div class="spicy">
<div ng-controller="MainController">
<p>Good {{timeOfDay}}, {{name}}!</p>
<p>{{display.showName}} - {{age}} --> New Age - {{display.age}}</p>
<input type="text" ng-model="name">
<button ng-click="resetTime()">CHANGE</button>
<div ng-controller="ChildController">
<p>Good {{timeOfDay}}, {{name}}!</p>
<button ng-click="setTime()">SET</button>
<input type="text" ng-model="display.showName"> Age - {{age}}
<input type="text" ng-model="age">
<button ng-click="setAge()">Change Age</button>
<div ng-controller="GrandChildController">
<p>Good {{timeOfDay}}, {{name}}!</p>
</div>
</div>
</div>
</div>
JS looks like below
(function(angular) {
'use strict';
var myApp = angular.module('scopeInheritance', []);
myApp.controller('MainController', ['$scope', function($scope) {
$scope.timeOfDay = 'morning';
$scope.files ='';
$scope.name = 'Nikki';
$scope.display={};
$scope.display.showName ='Vikram';
$scope.age ='20';
// $scope.display.age ='20';
$scope.resetTime = function(){
$scope.timeOfDay = 'chaged day';
};
}]);
myApp.controller('ChildController', ['$scope', function($scope) {
$scope.name = 'Mattie';
$scope.$parent.timeOfDay ='';
$scope.setTime = function(){
$scope.timeOfDay = 'new day';
};
$scope.setAge = function(){
$scope.display.age ='25';
};
}]);
myApp.controller('GrandChildController', ['$scope', function($scope) {
$scope.timeOfDay = 'evening';
$scope.name = 'Gingerbread Baby';
}]);
})(window.angular);
This example is an extension of Angular Js org nested controllers doc's based example.

Better to wrap all your templates and controllers into components and hide them from outside controller
<component1 ng-if="req1"></component1>
<component2 ng-if="req2"></component2>
<component3 ng-if="req3"></component3>
and use ControllerAs of course.

Related

Is there any way to use two ng-app's like parent and clild app in AngularJs?

HTML
chech out the below sample html, I require something like this.
<div id="parentApp" ng-app="parentApp" ng-cloak="" ng-controller="mainController">
<div id="someContent">
{{$scope.parentName}}
***some parent app actions***
</div>
<div id="childApp" ng-app="childApp">
<div id="someContent" ng-controller="secondController">
{{$scope.childName}}
***some child app actions***
</div>
</div>
</div>
Script
2 simple app and controllers for better understanding purpose.
var parentApp = angular.module('parentApp', []);
parentApp.controller('mainController', function($scope, $http) {
$scope.parentName = 'Parent!!'
});
var childApp = angular.module('childApp', []);
childApp.controller('secondController', function($scope, $http) {
$scope.childName = 'Child!!'
});
You cannot use 2 ng-app attribute on a page.
However, looking at your requirement, I think you need to use 2 controllers and do some stuff with it while maintaining the appearance of parent-child in the HTML structure. To, do that you can make use of the angular.bootstrap method.
So, you can modify the html as below:
<div id="parentApp" ng-cloak="" ng-controller="mainController">
<div id="someContent">
{{$scope.parentName}}
***some parent app actions***
</div>
<div id="childApp" ng-app="childApp">
<div id="someContent" ng-controller="secondController">
{{$scope.childName}}
***some child app actions***
</div>
</div>
</div>
And in your code, you can initialize it as below:
var parentAppDivId = document.getElementById('parentApp');
angular.bootstrap(parentAppDivId, ['parentApp']);
var parentApp = angular.module('parentApp', []);
parentApp.controller('mainController', function($scope, $http) {
$scope.parentName = 'Parent!!'
});
var childAppDivId = document.getElementById('childApp');
angular.bootstrap(childAppDivId, ['childApp']);
var childApp = angular.module('childApp', []);
childApp.controller('secondController', function($scope, $http) {
$scope.childName = 'Child!!'
});
If you already have two separate apps and are trying to leverage parts of one in another app, you can inject the module from one app into another. It's the the same structure as you've outlined above, but it will make everything from the first app available in the second app.
angular.module('childApp', ['parentApp'])...

How to let the angularjs controller run only once even many ng-controllers defined in DOM?

I wish I can handle this, but in the bad way...namely I need to use $cookieStore to check either the function called or not.
Every time to use push array then I need to use $cookieStore. But it seems not practical.
Here was my DOM:
<div ng-controller="MyCtrl">
<div>
<div ng-include="'temp2.html'">
Hello, {{name}}!
</div>
</div>
</div>
<script type="text/ng-template" id="temp2.html">
<div ng-controller="MyCtrl">Another View</div>
</script>
And my angularjs controller:
var myApp = angular.module('myApp',[]);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
$scope.name = 'Superhero';
alert(1);
}
alert(1) function will be called 2 times every times the page was called.
How to avoid this problem without using watcher?
My fiddle for your convenience. Thanks!
The controller would called be twice for both the views, i would suggest you to move controller specific code to init function and use ng-init in one of your views.
var Controller = function($scope) {
$scope.init = function () {
};
};
Your View
<div ng-controller="Controller" ng-init="init()"/>
Yo don't need to specify the Controller name again in the include.... if its the same as the parent one(same as controller of main page).
just go with this
<div ng-controller="MyCtrl">
<div>
<div ng-include="'temp2.html'">
Hello, {{name}}!
</div>
</div>
</div>
<script type="text/ng-template" id="temp2.html">
<div>Another View {{name}}</div>
</script>
Js Fiddel
name will be accessible in the view you included.
Hope it will help you..

understanding the controller scope in angularJS

Consider two div areas as follows, in html file
<div class="divArea1" ng-controller="myController">
<input ng-click="updateName()" type="button" value="button"/>
</div>
<div class="divArea1" ng-controller="myController">
<p>{{name}}</p>
</div>
Following is the angular js example
productApp.controller("myController", [ '$scope', function($scope) {
$scope.name= "XYZ";
$scope.updateName= function() {
$scope.name = "ABC";
};
} ]);
problem is when I am trying to update the name, upon click on update button it is not visible in the second in the div area. Is there any mistake i am doing.
What you have is two different controllers (with two separate scopes) with the same name.
You need to put the controller in the parent controller to keep the name in the same scope as the button:
<div id="container" ng-controller="myController">
<div class="divArea1">
<input ng-click="updateName()" type="button" value="button"/>
</div>
<div class="divArea1">
<p>{{name}}</p>
</div>
</div>
Controllers are not singletons. Each time you have a new controller, you're having a new instance of this controller, with a new isolated scope.
If your need is to share data between controllers, you should use a factory (which is a singleton).
angular.module('app').factory('mySharedData', function(){
var service = {
object : objectToShare
};
var objectToShare = {};
return service;
});
And from your controller :
angular.module('app').controller('myController',
['$scope','mySharedData',
function($scope, mySharedData){
$scope.someObject = mySharedData.object;
$scope.updateName= function() {
$scope.someObject.name = "ABC";
};
}
]);
And from your view :
<div class="divArea1" ng-controller="myController">
<input ng-click="updateName()" type="button" value="button"/>
</div>
<div class="divArea1" ng-controller="myController">
<p>{{someObject.name}}</p>
</div>
Note : I've encapsulated the name property into an object because objects are passed by reference, and strings by value. This allows you to make it easier to share your values and to have it automatically updated into the service and other controllers, without having to access your property through accessors.
here is demo http://jsfiddle.net/wg7pb1yu/3/
inject $rootScope so that it will do from global scope
productApp.controller("myController", [ '$scope','$rootScope', function($scope, $rootScope) {
$rootScope.name= "XYZ";
$scope.updateName= function() {
$rootScope.name = "ABC";
};} ]);
Hope this will help you

How to render HTML Tags from ngModel?

I'm using AngularJS for binding JS variables to my HTML content, and it works fine.
JS
var app = angular.module("Tabs", [])
.controller("TabsController", ['$scope', function($scope){
$scope.events = my_JS_object;
})
HTML
<div>{{events.test}}</div>
It works as long as my_JS_object.test is a simple string, like "Hello World", but once I try to put HTML tag in there, such as Hello <b>World</b> It doesn't use the tags as HTML elements, but as simple text. Which makes sense, only I have no idea how to make the HTML tags work.
As stated by Angular documentation, you can use inbuilt ng-bind-html directive to evaluate model string and insert resulting HTML into element.
Example:
If you have model value like:
$scope.myHTML =
'I am an <code>HTML</code>string with ' +
'links! and other <em>stuff</em>';
Use ng-bind-html like:
<p ng-bind-html="myHTML"></p>
For detailed information go through: https://docs.angularjs.org/api/ng/directive/ngBindHtml
Note: Don't forget to inject ngSanitize service in your app.
You need to use the ngBindHtml directive that properly evaluates the expression and inserts the resulting HTML into the element in a secure way. To do this, you must include a reference to angular-sanitize.js in your HTML and then in your angular module, inject ngSanitize.
Like so
var app = angular.module("Tabs", ['ngSanitize'])
.controller("TabsController", ['$scope', function($scope){
$scope.events = my_JS_object;
})
<div ng-controller="TabsController">
<div ng-bind-html="events.test"></div>
</div>
Here is a full working example:
(function(angular) {
'use strict';
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', function($scope) {
$scope.myHTML = 'Hello This is <b>BOLD<b/>';
}]);
})(window.angular);
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.1/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.1/angular-sanitize.js"></script>
</head>
<body ng-app="bindHtmlExample">
<div ng-controller="ExampleController">
<p ng-bind-html="myHTML"></p>
</div>
</body>
Refer to the official angular documentation for details:
https://docs.angularjs.org/api/ng/directive/ngBindHtml
If you want to insert HTML into page you shouldn't do it this way.
There is sanitize for this task.
For example in your controller:
$scope.trustedHtml = "<b>Hello World</b>"
And in your html:
<div ng-bind-html="trustedHtml "></div>
Always check html if using a user given text before inserting.
Also don't forget to add ngSanitize as dependency while creating controller
It's easier to use transclusion if you want to embed custom HTML into your DOM tree.
angular.module('myApp', [])
.controller('MainCtrl', function ($scope) {
$scope.overwrite = false;
$scope.origin = 'parent controller';
})
.directive('myDirective', function() {
return {
restrict: 'E',
templateUrl: 'my-directive.html',
scope: {},
transclude: true,
link: function (scope) {
scope.overwrite = !!scope.origin;
scope.origin = 'link function';
}
};
});
<script src="https://code.angularjs.org/1.3.2/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MainCtrl">
<my-directive>
<p>HTML template</p>
<p>Scope from {{origin}}</p>
<p>Overwritten? {{overwrite}}</p>
</my-directive>
</div>
<script type="text/ng-template" id="my-directive.html">
<ng-transclude></ng-transclude>
<hr />
<p>Directive template</p>
<p>Scope from {{origin}}</p>
<p>Overwritten? {{overwrite}}</p>
</script>
</div>

Does angular.equals() work as an angular expressions?

I'm trying to display a div if an object is non-empty. Using this answer, Im trying to use angular.equals to check emptyness, but its not behaving as expected
var test = angular.module('test',[]);
test.controller('testCtrl', ['$scope', function($scope){
$scope.foo={};
$scope.bar="bam"
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="testCtrl">
<div ng-show="!angular.equals(foo,{})">{{bar}}</div>
</div>
</div>
The expectation here is that the value of bar will only show if foo is not equal to an empty object. However, foo is clearly set to {} and yet bar still shows.
If you want to access the angular object from templates or expressions, you have to make it available on the scope of where you want to use it. In this case you can put it on the testCtrl's scope.
var test = angular.module('test',[]);
test.controller('testCtrl', ['$scope', function($scope){
$scope.angular = angular;
$scope.foo={};
$scope.bar="bam"
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="testCtrl">
<div ng-show="!angular.equals(foo,{})">{{bar}}</div>
</div>
</div>
I generally put utility objects on $rootScope, so they're available from everywhere.
A cleaner way would be to only add the angular equals method to the $scope:
var test = angular.module('test',[]);
test.controller('testCtrl', ['$scope', function($scope){
$scope.angularEquals = angular.equals;
}
Then you can use the equals method in the template, like:
<div ng-show="!angularEquals(foo,{})">{{bar}}</div>
Your view is looking for a function on the scope, and $scope.angular.equals does not exist. You need to write one like this:
var test = angular.module('test', []);
test.controller('testCtrl', ['$scope',
function($scope) {
$scope.foo = {};
$scope.bar = "bam";
$scope.isEmpty = function(obj) {
return angular.equals(obj,{});
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="testCtrl">
<div ng-hide="isEmpty(foo)">{{bar}}</div>
</div>
</div>
Angular functions can't be used inline, AFAIK.
You could do the equal check with a function inside the controller instead and return the result.

Categories

Resources