I got the following code:
<div class="map" ng-controller="DealerMarkerListCtrl">
<a ng-click="showdetails=!showdetails" href="#/dealer/{{marker.id}}" class="marker" style="left:{{marker.left}}px;top:{{marker.top}}px" ng-repeat="marker in dealer"></a>
</div>
and this view:
<div ng-show="showdetails" class="alldealermodal">
<div ng-view></div>
</div>
This same "ng-show" stuff is working properly with just one link outside of the ng-repeat but in this ng-repeat it isn't working.
The link shall open an overlay. The ng-view works too.
What am I missing?
Since ngRepeat creates a new scope the showdetails being referenced outside of your ng-repeat is not the same instance as the showdetails being updated in your repeated ng-click.
You can see this post for more details but one way around the new scope(s) is to bind to an object property instead of a primitive type.
This fiddle shows a small example binding to details.show instead of showdetails with:
$scope.details = { show: true };
"What am I missing?"
As #Gloopy already mentioned, you probably didn't realize that ng-repeat creates child scopes (one for each item). In addition, an understanding about how JavaScript prototypal inheritance works is also necessary because each child scope prototypically inherits from the same parent scope, and that affects how JavaScript finds (or creates) properties on scopes.
Note that a number of Angular built-in directives create child scopes: ng-repeat, ng-include, ng-switch, ng-controller, directives (can, but may not). For (much) more information about how prototypal inheritance works, why it is a problem when trying to bind to primitives, and how it relates to Angular scopes, see here.
To extend #Gloopy's answer, there are two other alternatives you could consider:
use $parent inside the ng-repeat to bind to the parent scope property (instead of creating child scope properties):
<a ng-click="$parent.showdetails=!$parent.showdetails" ...
define a method on the parent scope and call it, thereby also changing a parent scope property (rather than a child scope properties):
<a ng-click="toggleDetails()" ...
Related
Learning Angular JS, trying a simple function where clicking an element hides its parent element.
My code:
<div class="form-section" ng-init="visible = true" ng-show="visible">
<a class="next" ng-click="$parent.visible = false">NEXT</a>
</div>
However, nothing happens when I click my <a> tag.
I based my code on this fiddle: http://jsfiddle.net/oxda3aes/
You don't need the $parent
What you're doing is creating a variable on the scope called visible and setting it to true.
So all you need to do to change the variable is call visible
$parent isn't referencing a parent node, it's referencing a parent scope.
<div class="form-section" ng-init="visible = true" ng-show="visible">
<a class="next" ng-click="visible = false">NEXT</a>
</div>
http://jsfiddle.net/oxda3aes/25/
This might help you see a little better what's happening:
http://jsfiddle.net/oxda3aes/26/
Here I declare the visible variable in the controller. Both accomplish the same task.
And here I have a function on the scope that gets called on click that does the same thing:
http://jsfiddle.net/oxda3aes/27/
$parent in angular does not mean parent element in HTML structure, but nested scopes relation. In mentioned by you example, ng-repeat creates child scopes for each it's iteration, and $parent in this case from this child scope points to its parent scope. In your case both HTML elements belong to the same scope, so, as was already answered just do not use $parent in your case.
When dealing with parent child scopes interactions in angular, it is always to use objects for your ng-models, rather then primitives (ngModel="obj.property" instead of just ngModel="property"), see very good article about scopes in angular https://github.com/angular/angular.js/wiki/Understanding-Scopes
I'm confused from angular transclude scope. I'm trying to make let say collapsible directive. But binding inside the transclude scope will not change model of parent unless I use some object for the model eg. data.
<div>
data.prop: {{data.prop}} <br>
prop: {{prop}}
<collapsible>
data.prop: <input type="text" ng-model="data.prop" /> <br> // WILL CHANGE PARENT
prop: <input type="text" ng-model="prop" /> // WONT CHANGE PARENT
</collapsible>
</div>
I already read this topic and still I don't get it why I must use prefix to the model.
Confused about Angularjs transcluded and isolate scopes & bindings
Working example at http://plnkr.co/edit/z3IvR1a37jdNRCJWG0Yq?p=preview
In my app I'm using object for forms, so it works fine but I just want to know why is that.
When you use an object to bind to the model, your object is passed to the different scope as an reference, not a copy, in Javascript objects are passed to functions as a reference. In that case it will still reference to previous scope.
How does angular keep track of which $scope is related to which element? I have a few theories:
angular element keeps scope object as property
angular saves each scope in cache with the relationship with element
angular searches each ng-scope, and magically find element
For example, I have an element with $scope, and I want to change dom hierarchy of this element (moving to outside of the outer controller). How is scope affected by this action?
If you have an angular app, and your HTML looks something like this:
<div ng-controller="OuterController">
...
<div ng-controller="InnerController">
<div id="wrapper">
<span>An Element</span>
</div>
</div>
</div>
And you move the <span> out of the #wrapper div, it's still within the context of both OuterController and InnerController. However, if you move it up to where the ... is, only the OuterController's context applies.
Each controller has its own context, and that context extends down into all child-elements recursively, even if a new controller is present, at which point, both controllers are in-scope.
This is true whether the element is moved dynamically with Javascript or otherwise. Angular keeps track of most context internally.
With Angularjs, you can bind an event to a dom element like this:
<div ng-controller="SampleController">
<a ng-click="showTheHiddenDiv()">Show the hidden div</a>
<div ng-show="showHiddenDiv">
hidden content
</div>
</div>
My question: is it possible to attach an event handler function like this from outside the controller, like this?
<a ng-click="showTheHiddenDiv()">Show the hidden div</a>
<div ng-controller="SampleController">
<div ng-show="showHiddenDiv">
hidden content
</div>
</div>
This does not work, I'm wondering if there is a special way to access the showTheHiddenDiv() function. I know i could just wrap everything in another container and make that the controller scope, but I'm wondering if its possible to do it this way.
Thanks!
Think bindings. You want to introduce a variable on the scope that the view (ng-click and inner div can bind to). It would make sense to make this variable a Boolean so that when it changes, it will update the views.
Start by creating a showDiv variable on the scope, and updating it when the anchor link is clicked:
<div ng-controller="SampleController">
Show the hidden div
<div ng-show="showDiv">
hidden content
</div>
</div>
When the link is clicked, it will toggle 'showDiv' which is saved on the scope of SampleController. Since, the ng-show directive on the inner div is bound to 'showDiv' (the same showDiv that is on the SampleController's scope), it will automatically show and hide based on the value of 'showDiv'.
[EDIT]
I failed to answer the original question, so I'll try again.
It is certainly possible to define a scope variable that is outside SampleController and bind it to the view within an inner controller:
Show the hidden div
<div ng-controller="SampleController">
<div ng-show="showDiv">
hidden content
</div>
</div>
This works because of scope inheritance. When you have scope inheritance, the child scope inherits the scope variables from the parent scope. In the above example, you have the scope of SampleController (the child scope) which inherits from the scope of the outside controller (the parent scope). If there is no parent controller, then the parent scope is just the root scope. The important point is that child scope inherits from parent scope, and inherits all its scope variables.
In the example above, ng-show is bound to showDiv, but it hasn't actually been assigned to a scope variable in any scope (yet). So initially, 'showDiv' is undefined but it remains hidden because in angular, undefined is treated as false when evaluated. Once you click the link, then showDiv is assigned !showDiv. It is at this point that the scope variable 'showDiv' is created, and since the assignment happens outside of SampleController, the scope variable is created in the parent scope - in this case, the root scope.
Since showDiv is bound to the same scope variable in both the anchor link and the ng-show directive, any changes to the scope variable 'showDiv' (such as clicking the link), will propagate to all views that are bound to it (the div that contains the hidden content).
You can always use a service and a directive to share a value between different scopes.
app.service('SharedService', function(){
this.showHiddenDiv = false;
});
app.directive('showDiv', function(SharedService){
return {
restrict: 'AC',
link : function(scope, element, attr) {
scope.showHiddenDiv = SharedService.showHiddenDiv;
scope.toggleDiv = function(){
SharedService.showHiddenDiv = SharedService.showHiddenDiv ? false : true;
}
}
}
})
html:
<a ng-click="toggleDiv()" show-div>Show the hidden div</a>
<div ng-controller="SampleController">
<div ng-show="showHiddenDiv" show-div>
hidden content
</div>
</div>
As a side note, any time you're trying to manipulate the DOM, you should be doing so in a directive, that's the angular way!
I have created two directives, one of them is used within the other and it has been created just for the matter of seperation of concerns.
<div class="docinputContainer">
<textarea></textarea>
<easy-table></easy-table>
</div>
I have omitted the scope property on both of them, therefore i would have thought that i could bind a keydown eventon my parent directive, which then could call a $scope method from the child directive:
// parent
element.bind('keydown',function(e){
$scope.save();
});
// easyTable
$scope.save = function(){
// ...
}
However, this method is unknown in my parent directives $scope.
Now i have some questions:
Why is this? Shouldn't both directives share a scope?
What would be the best solution to this?
I know i could do one of the following:
Broadcast an event
Use a shared service to trigger an event
Concatenate both directives
I would prefer to share the scope, though. Any help is appreciated
Solution
I bypassed my issue by binding the element to a scope property on the parent directive, then binding the keydown event in easyTable itself, instead of the parent one.