how to have unique scope in ng-repeat in angularjs - angularjs

Is it possible for me to have multiple array for $scope?
i have a list of div with child scopes that is generated from a parent scope in ng-repeat. How can i have the scope variable individually unique?
I am generating a list of ng-repeat in another ng-repeat.
<div ng-repeat="" ng-init="hide=true" ng-click="hide=!hide">
<div ng-hide="hide" ng-init="childhide=true" ng-click="childhide=!childhide">
<div ng-repeat="" ng-init="childhide" ng-hide="childhide">
<div>{{ variable }}</div>
</div>
</div>
</div>
How can i have the variable unique? Coz each time when i click on either one div, all div with childhide variable will show. Anyway to make them behave individually?
Thanks.

To get a new $scope for each div, the first ting that comes to mind is to create another directive and specify which type of scope you want.
<div class="container">
<div ng-repeat="item in items"></div>
</div>
will become:
<div class="container">
<inner-directive ng-repeat="item in items"></inner-directive>
</div>
then in inner-directive:
app.directive('innerDirective', function () {
return {
restrict: 'E',
template: '<div></div>', // this replaces what you had before
scope: {}
};
});
This will create an isolate scope, which does not inherit properties from it's parent.
There are a couple of other scope options but i cant remember off the top of my head what each one does. Easy to read in the docs though.

Related

Angularjs compile a directive inside ng-repeat with isolated scope

I have a directive in the form of a dropdown, pretty simple. The user can click a button to add as many as they need to in a ul, make their selections, and save it off. This is all inside of several ng-repeats.
I'm having trouble mastering the scope. As I expected, this works:
<div ng-repeat="group in groups" question-group="group" class="question-group">
<div ng-repeat="question in questions">
<ul>
<li ng-repeat="case in question.cases"></li>
<li><new-case group='group'></new-case></li>
</ul>
</div>
</div>
When I say "works", I mean that group is properly scoped (the data of the entire group is necessary for the resulting input).
When I switch it to "click to add":
<div ng-repeat="group in groups" question-group="group" class="question-group">
<div ng-repeat="question in questions">
<ul>
<li ng-repeat="case in question.cases"></li>
<li>add case</li>
</ul>
</div>
</div>
group is undefined in the scope. Here is my createNewCase function:
function createNewCase($event) {
var thisLi = angular.element($event.target).closest('li');
var listItem = $compile('<li><new-case group=\'group\'></new-case></li>');
var html = listItem($scope);
thisLi.before(html);
}
$scope.createNewCase = createNewCase;
And the newCase directive:
angular.module('groups.directives.newCaseDirective', [])
.directive('newCase', ['$window', function() {
return {
restrict: 'EA',
scope: { group: '=' },
templateUrl: 'groups/views/newcase.tpl.html'
};
}]);
I've been reading for days and I've tried a few other derivatives but I'm ultimately just not getting it. Help is greatly appreciated.
Thanks!
The issue is that group is created by ng-repeat and is only available in child scopes of ng-repeat.
Each repeated element is in it's own child scope. So your directive version works but your other one doesn't because the controller doesn't see those child scopes.
You would have to pass group as argument of the function if you want to access it in controller
<a href="#" ng-click="createNewCase($event, group)">

Hide/show an element in Angularjs using custom directive, ng-show and $scope

When a link is clicked in the app navigation a dropdown with ui-view content shows below each respective link.
The HTML:
<div class="sc-dash-header">
<ul>
<li>
<a class="navbar-brand" show-nav-popup href="">download</a>
<div id="nav-download-progress" class="dash-hdr-popup" ng-show="showPopup">
<div ui-view="hdr-download-progress"></div>
</div>
</li>
<li>
<a class="navbar-brand" show-nav-popup href="">add</a>
<div id="nav-add" class="dash-hdr-popup" ng-show="showPopup">
<div ui-view="hdr-add-content"></div>
</div>
</li>
<li>
<a class="navbar-brand" show-nav-popup href="">enter pin</a>
<div id="nav-unlock" class="dash-hdr-popup" ng-show="showPopup">
<div ui-view="hdr-unlock"></div>
</div>
</li>
</ul>
</div>
I've included an ng-show attribute to open the dropdown when $scope.showPopup is set to true.
To achieve this I've created a custom directive with an on click called show-nav-popup.
The JS:
.directive('showNavPopup', function () {
return {
restrict: 'A',
// scope: {},
link: function(scope, el, attrs) {
el.on('click', function(){
scope.$apply(function () {
scope.showPopup = true;
});
console.log(scope);
});
}
};
});
The above works, but the dropdown opens on each element.
Question: I need to isolate the scope, so on each click, only the respective dropdown appears. I uncomment the line // scope: {} - but this doesn't work..
Angularjs n00b here - any help would be much appreciated
Having an isolate scope in this situation wouldn't fix the problem. There are a ton of ways to achieve what you want though. One of which is to assign each show-popup-nav an id, turn $scope.showPopup into an array, and keep an individual true/false for each id. Then for each ng-show, you look at the index corresponding to each id for the true/false value.
I coded it up on that guy's Plunker, working as you expect: http://plnkr.co/edit/CSikLIiuPNT9dfsfZfLk
EDIT: I should say, you COULD use an isolate scope to fix this, but that would require a lot of changes to your DOM, as the ng-show directive is a sibling to your show-popup-nav, and not a child.
When you create the isolate scope, the scope applies to the element that your directive is applied to, and it's child elements. In this case that's just the anchor tag:
<a class="navbar-brand" show-nav-popup href="">download</a>
You are using an ng-show on a tag that is a sibling to the anchor tag:
<div id="nav-download-progress" class="dash-hdr-popup" ng-show="showPopup">
The sibling is not part of the isolate scope, and so it never notices that the value of showPopup has changed.
The ng-show would work if it were applied to a DOM element that was a child of the anchor tag.
EDIT
One way to make this work would be to wrap your two siblings in a parent tag, and use the directive on the parent:
<div show-nav-popup>
Download
<div ng-show="showPopup"></div>
</div>
Then you'd need to modify your directive's code to find the anchor tag and apply the click handler.
You might instead try a completely different approach as suggest in the other answer by #Bill Bergquist

AngularJS : isolated scope + two-way binding + ng-repeat not working

the ng-repeat outputs nothing. in the link function, i can see $scope.clients is an array. if i remove the isolate scope, and use the parent scope, the ng-repeat works.
html with directive "clients".
<div container
ng-cloak
ng-app="summaryReportApp"
ng-controller="summaryReportController as summaryReport">
<fieldset clients="summaryReport.clients">
<legend>Clients</legend>
<div align="left">
<div ng-repeat="client in clients track by $index">
{{client}}
</div>
</div>
</fieldset>
</div>
directive
var clients = function(){
var definition = {
restrict: "A",
scope: {
clients:"=clients"
},
link: function($scope,$element,attributes){
}
}
return definition;
}
This is a common question I seem to answer frequently. Directives can have other HTML Elements nested in them, in the same way that an <input> can be nested inside a <div>. However, the Elements nested inside the Directive are not part of the directive, and are not scoped to the directive, they are scoped to the HTML they are in. The only items that have access to the Isolated Scope are the compile, link, controller, and template items in the directive definition. If you moved your inner html from inside the fieldset into a template, it would function as expected.
You can also reference http://angular-tips.com/blog/2014/03/transclusion-and-scopes/ for more examples and ways to test this.

using ng elements outside of ng-controller

I am inexperienced with angular.
I am using angular to create a series of nested divs (a form) on a webpage. The top div has ng-controller="controllername" as an attribute. Within the nested divs is a div with ng-show="showvar" as an attribute.
It looks like this.
<div class="page">
<div ng-controller="controllername">
<div ng-show="showvar">Hidden Stuff</div>
</div>
</div>
When I perform functions on showvar to make it true, the div appears (and disappears when false) as intended.
I also have a completely separate div 'outside' the the original nest of divs with the ng-controller attribute. As such, there is no ng-controller attribute in this seperate hierarchy BUT I have nested another div inside with the ng-show="showvar" attribute.
Updated HTML structure is as such
<div class="page">
<div ng-controller="controllername">
<div ng-show="showvar">Hidden Stuff</div>
</div>
<div class="seperate">
<div ng-show="showvar">More Hidden Stuff</div>
</div>
</div>
When the page loads, both divs with ng-show="showvar" in the separate nests are hidden as ng-hide has been appended by angular. When I perform functions on showvar after the page load to make it true, only the div within the ng-controller div gets shown.
I (think I) understand this is because the ng elements are evaluated at page load (and appended with ng-hide, even outside the controller?) but only the ng elements within the div with the ng-controller attribute are evaluated when functions are performed after page load. Is this correct?
How can I get the other ng-show to be evaluate 'outside' of the ng-controller div?
I was thinking one option is to append ng-controller to the overall 'page' div instead of the nested div. But what other options do I have?
EDIT: I also tried simply adding ng-controller="controllername" to the separate div. I guess angular 'ignores' the duplicate ng-controller div?
The problem your facing is that the showvar resides in your controller's scope, your second usage of the showvar is not within that scope.
What you need to do is make sure the variable is available where needed.
Say you add the variable to the parentController (you don't have one in your example so I'll add one)
<div class="page" ng-controller="parentController">
<div ng-controller="controllername">
<div ng-show="showvar">Hidden Stuff</div>
</div>
<div class="seperate">
<div ng-show="showvar">More Hidden Stuff</div>
</div>
</div>
app.controller('ParentController', function($scope){
$scope.showvar = false;
});
problem with this is when you set showvar to true within your controllername controller it will set it in the innerscope and not the outer. When making sure you have the right scope by accessing it through another object you should be safe.
So try it like this:
<div class="page" ng-controller="parentController">
<div ng-controller="controllername">
<div ng-show="obj.showvar">Hidden Stuff</div>
</div>
<div class="seperate">
<div ng-show="obj.showvar">More Hidden Stuff</div>
</div>
</div>
app.controller('ParentController', function($scope){
$scope.obj = {
showvar: false
}
});
Quick demo
Your issue here is that you ended with 2 "showvar" variables: one within the "controllername" scope and another one on the app scope (as you have a ng-app declaration somewhere in your html parent of the "page" div).
When you load your page, you get the value of "showvar" in the controller scope for the first div, and for the "separate" one, you get the "showvar" variable in the app scope, which doesn't exist, therefore it is resolved to "false" (even though angular declares it for you in your app scope and you can even modify its value later).
When you change the value of "showvar" in the controller scope, it doesn't change the one in the app scope, making the "separate" div stay hidden forever =)

How to do an ng-repeat that doesn't duplicate the ng-repeat element

I need to use ng-repeat to produce several elements but those elements cannot be each wrapped inside a div (this is for box layout purposes where the box layout only works on the immediate children). For example, I need this result:
<div class='box-layout'>
<div class='item-header>Head 1</div>
<div class='item-body>Body 1</div>
<div class='item-header>Head 2</div>
<div class='item-body>Body 2</div>
</div>
There is no wrapping element on the repeated sections. This structure is required to make use of flex-box style layouts. How can I do this with AngularJS?
As #Anders Bornholm said, it can't be done (fyi, in knockout.js this is easy).
Albiet its ugly, you can accomplish this via a directive, e.g.:
AngularJS ng-repeat with no html element
ng-repeat without HTML element (this time really without any)
JS:
directive('htmlAppend', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.append(attrs.htmlRepeat);
}
};
})
HTML:
<div class='box-layout'>
<div class='item-header' ng-repeat="s in sections" html-append="<div class='item-body'>Body</div>">Head</div>
</div>
Short answer: can't be done. One of Angular's biggest drawbacks for me personally.
If directive templates would have allowed more than one root element you could have done it with a directive, but that doesn't work either.

Resources