my controller is loaded multiple times in my AngularJS 1.5 code:
<div ng-repeat="conditionForMultipleRows">
<div data-ng-if="$first">
<div co-my-component></div>
</div>
</div>
export function coMyComponent(): ng.IDirective {
return {
template: coMyComponentTemplateHtml,
controller: 'MyComponentController',
controllerAs:'$ctrl'
}
}
export class MyComponentController{
state: MyStateClass;
static $inject = [someServices]
constructor(someServices) {
document.getElementById("myComponent").addEventListener("myEvent", (ev: CustomEvent) => {
doStuff()
}
The HTML Part is only called once, so there should be no issue. Only my controller is loaded multiple times.
The angular.module only loads the controller once and the directive only once, so there is no issue. Also there is no other place where the controller or webcomponent is called in the code.
I'm not very familiar with AngularJS so you can also point out other parts if you see something wrong here. Please refer to a source if it was resolved there. I didnt find any helpful answer
Thanks in advance guys
Each time your directive is instantiated, it will received a brand new controller.
The ng-repeat directive instantiates a template once per item from a collection.
In your case, if conditionForMultipleRows is an array having four items inside, you will instantiate four times the template
<div data-ng-if="$first">
<div co-my-component></div>
</div>
Each template instance gets its own scope and own controller, thus calling the constructor four times.
The answer for my issue here is:
<div ng-repeat="conditionForMultipleRows track by $index ">
<div data-ng-if="$first">
<div co-my-component></div>
</div>
</div>
With adding track by $index, the controller of my component only loaded once.
Also learned that in a ng-repeat it should always be added a "track by". There are only a few egde cases where this isnt required. Correct me if I'm wrong
Related
I have a simple controller
angular.module('datahubApp')
.controller('LoginController',[ '$scope' , '$resource', LoginController]);
function LoginController($scope,$resource){
console.log('Constructor called');
}
I have used the this in HTML in 2 places for two different components, in the same view
<div ng-controller="Logincontroller as ln"> </div>
<div ng-controller="Logincontroller as ln"> </div>
Now when I run the app, i see this in console
Constructor Called
Constructor Called
Constructor Called
Question 1:
So, this is getting called thrice. Is this normal.
Question 2:
In that case, how can I pass the scope variable between components?
Why are you using same controller two times in a view. Use it one time only and it would work perfect.
You can use like:
<div ng-controller="Logincontroller as ln">
<div></div> <!-- Your first div -->
<div> </div> <!-- Your second div -->
</div>
AngularJS will call a controller every time it found in a view. No matter how much time is there in view part.
I have two controllers allocated to two views:
[ResultsView ng-controller="ResultsCtrl"]
[SearchView ng-controller="SearchCtrl"]
The Search View has many complex filters/options and is filled in by the user, then he/she can press "Search" on SearchView and Results should be populated into a Grid.
Now I can send information between two either by a Service or by using $rootScope.$broadcast.
Heres the problem I've run into:
[ResultsView ng-controller="ResultsCtrl"][SearchView ng-controller="SearchCtrl"]
[ResultsView ng-controller="ResultsCtrl"][SearchView ng-controller="SearchCtrl"]
[ResultsView ng-controller="ResultsCtrl"][SearchView ng-controller="SearchCtrl"]
If I were to have multiple Result-Search sections on the same page, how can I ensure they each act independently from each other? Using the Service approach, the ResultsCtrl and SearchCtrl both have the defined service
.controller("searchCtrl", ["$scope", "$searchHttp", function ($scope, $searchHttp) {
.controller("resultsCtrl", ["$scope", "$searchHttp", function ($scope, $searchHttp) {
So I can't change how each instance of the controller behaves regarding the service. Soon as one SearchCtrl calls the service, it will modify every ResultsCtrl instance.
Likewise using broadcasts $rootScope.$broadcast("searchResults"... will be picked up by every ResultsCtrl instance.
So whats the best way around this? I want to reuse the Results and Search View code since its basically the same. But I need to render each pair independently on the same page a few times.
I think the HTML structure you need is something like this.
<!--First-->
<div ng-controller="SearchCtrl">
<div ng-controller="ResultsCtrl">
</div>
</div>
<!--Second-->
<div ng-controller="SearchCtrl">
<div ng-controller="ResultsCtrl">
</div>
</div>
This HTML structure would help you to use independently the search results one's parent SearchCtrl created in ResultsCtrl.
jsfiddle is here.
I hope this would help you. :)
Suppose that I've written a web page that requires the user to key in data into forms. Suppose that there are two parts of the page that are completely separate in the DOM, yet are logically related. I need to bind them to a single model.
The first (bad) solution I can think of is to completely restructure the page so that everything to be bound to the model is contained in a single <div> element (or perhaps even put the controller on the <body> element).
Another solution might be to bind the inputs to a object that could be made a member of two different controllers, but that sounds like a bit of a kludge.
Suppose the HTML looks like this:
<input ng-model='data1' />
<div>
<!-- something complicated -->
</div>
<input ng-model='data2' />
You can create a shared service and inject into both controllers. This can be achieved by either the factory or service pattern. See SO post angular.service vs angular.factory for some insight on the difference/similarity between the two.
This is the Angular way to do this. See the AngularJS services docs for more information. A simple example may include...
<div ng-app="app">
<div ng-controller="ctrlA"></div>
<div ng-controller="ctrlB"></div>
</div>
app.service('sharedService', [function () {
this.someValue = 'yo!'
}]);
app.controller('ctrlB', ['$scope', 'sharedService', function($scope, sharedService) {
console.log(sharedService.someValue) // yo!
}]);
app.controller('ctrlA', ['$scope', 'sharedService', function($scope, sharedService) {
console.log(sharedService.someValue) // yo!
}]);
JSFiddle Link
I have problem with scopes of controllers. I'm using controller as directive and I have code similar to this example:
<div ng-controller="ItemsController as itemCtrl">
<table> .. some data ... </table>
<a ng-click="itemCtrl.createItem()">Create new item</a>
</div>
<div id="create-form" ng-controller="ItemFormController as itemFormCtrl">
<form ng-submit="itemFornCtrl.saveItem()">... form inputs ...</form>
</div>
<div id="edit-items" ng-controller="MultipleItemsEdit as multiEditCtrl">
... table with some data ....
<!-- I need this -->
<a ng-click="itemCtrl.createItem()">Create new item</a>
<!-- -->
</div>
Basically there are 3 isolated scopes. But I need to break this isolation and call methods from one scope on another.
I'm currently using ugly "delegate" kind of hack.
Controllers and their methods are not so interesting, only interesting methods are ItemsController.createItem():
this.createItem = function(dataCollection) {
angular.element( $("#create-form) ).controller().createNewItem(dataCollection);
}
and ItemFormController.createNewItem(dataCollection):
this.createNewItem = function(dataCollection) {
... some initialization ....
$("#add-item").dialog( "open" );
}
I need to call createNewItem method on ItemFormController to show modal box. But I cannot do it directly, so I'm using method createItem which gets the create-form element and its controller and calls createNewItem method on it. It is kind of a delegate. But I don't like it, because I need to call createNewItem from many places of my code and I don't want to populate all my controllers with this kind of delegate methods.
Maybe I could make these delegates on some kind of root controller, but isn't there any better solution?
You can nest the edit controller scope in the list controller scope by simply nesting the divs (move the div with ng-controller="MultipleItemsEdit as multiEditCtrl" into the div with ng-controller="ItemsController as itemCtrl"). That way the you can call the method directly.
So, I can change a model value from a child controller, but when the child controller is in ng-switch then it doesn't work, why? I created an example to demonstrate it.
One way to avoid this is to use the . in the model name, like bunnies.kills. Is this a bug or this is a feature ?
Using Angular 1.0.6
Using your code structure, in your child controllers you would need to change:
$scope.$parent.kills++;
to
$scope.$parent.$parent.kills++;
Explanation: MainCtrl's scope is the parent scope of SimpleParentCtrl, but the grandparent of Step1Ctrl and Step2Ctrl. As some others pointed out, ng-switch creates its own scope, and then your Step1Ctrl and Step2Ctrl each created a child scope of the ng-switch.
Note: Each time the 1 or 2 button is clicked, both the ng-switch and it's currently matched child controller get a new scope.
Also: In case you happen to be looking in the Angular source and wondering how the ng-switch directive creates its own scope without a scope property, the answer is that it does so manually in its link method via scope.$new(). The directives ng-include, ng-switch, ng-repeat, and ng-view all create new scope this way, either in the link method or the compile method's returned link function.
Resources:
https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance
http://www.youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m
ng-switch creates its own child scope, which is why #sh0ber's answer is one way to get it to work. In general, models should be referenced in controller scopes (hence reference objects), and not be not primitives. So using a . is a "best practice".
This is not a bug, but it is not a feature either. This is the way JavaScript prototypal inheritance works with primitives.
I would take a slightly different approach to this problem.
Rather than use $scope.$parent, I would recommend you move all of your bunny killing logic into a shared service/model.
Also, I would try to avoid referencing parent views/controllers. Referencing the parent can make it difficult to reuse your code and can be painful to debug as the project grows. It is okay for a parent to know about it's children but a child should know little to nothing about it's parent.
Here is an updated Plunk: http://plnkr.co/edit/PLDbfU8Fu7m59A42qdR6?p=preview
HTML
<body ng-controller="MainCtrl">
<p>
Dead bunnies: <strong>{{Elmer.deadWabbits}}</strong>
</p>
<div ng-controller="SimpleParentCtrl">
<button ng-click="Elmer.killTheWabbit()">Kill from simple parent gun</button>
</div>
<hr>
<div ng-switch="" on="step">
<div ng-switch-when="first" ng-controller="Step1Ctrl">
<button ng-click="Elmer.killTheWabbit()">Kill from 1 tab gun</button>
</div>
<div ng-switch-when="second">
<div ng-controller="Step2Ctrl">
<button ng-click="Elmer.killTheWabbit()">Kill from 2 tab gun</button>
</div>
</div>
</div>
<hr>
<p>
<button ng-click="changeStep('first')">1</button> <button ng-click="changeStep('second')">2</button>
</p>
</body>
JS
angular.module('plunker', []).
service("Elmer", [function() {
this.deadWabbits = 0;
this.killTheWabbit = function() {
this.deadWabbits++;
};
}]).
controller('MainCtrl', function($scope, Elmer) {
$scope.Elmer = Elmer;
$scope.step = 'first';
$scope.changeStep = function(name){
$scope.step = name;
};
}).
controller('SimpleParentCtrl', function() {}).
controller('Step1Ctrl', function() {}).
controller('Step2Ctrl', function() {});
One way to avoid this is to use the . in model name, like bunnies.kills. Is this a bug or this is a feature ?
This has been explained numberous times : https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance
and in mhevery's video