How to show and hide items in dynamic rows in AngularJS? - angularjs

I'm fairly new to AngularJS and I can't seem to find a way to do this appropriately. I created a custom directive to Apply a function a pass in the row Index. However, I can't seem to think of the way to show items in a row. What would be the best way to do this? I want to show specific and hide a target row via controller.
HTML:
<div class="row" data-index="{{$index}}">
<div>other information</div>
<div class="item hidden" ng-class="{hidden: hidden[{{$index}}]}">
Item
</div>
</div>
My Directive:
scope.$apply(function () {
scope.$parent.showItem(index);
});
Controller:
$scope.teamDrop = function(index) {
$scope.hidden[index] = false;
};

You can use the ng-show and ng-hide directives to hide and show elements.
You can also use the ng-if directive to remove elements from the dom.
For your example I'd change your ng-class to an ng-hide
<div class="row" data-index="{{$index}}">
<div>other information</div>
<div class="item hidden" ng-hide="hidden[$index]">
Item
</div>
</div>
You also don't need to use the {{}} syntax in the ng-class becausue it's already expecting an angular expression, that's for data binding.

Related

How to access nested elements in AngularJS

I have a list of items (divs) with a button. When I click on one of this buttons I can access the element using
$event.currentTarget
that returns some thing like
<div ng-click="myFunc()">
<i class="someclass"></i>
<span>bla bla</span>
</div>
how can I access and elements to modify attributes like class?
Don't. Use existing directives like ng-class or ng-if, etc in your html templates.
<div ng-click="clicked = true">
<span ng-class="{'someclass': clicked}">bla bla</span>
</div>
See stackblitz
Although you can get the html dom element and edit it you should only do this as a last option and other angularjs supported methods have failed or are not supported.
template
<div ng-click="myFunc($event)">
<i class="someclass"></i>
<span>bla bla</span>
</div>
controller
$scope.myFunc(event) {
var elem = angluar.element(event.currentTarget);
elem.children(".someclass").removeClass("someclass")
}

Angularjs 1.5 replace directive element html

I want to built an ng-repeat with dynamic buttons that change according to $state.params so I wrapped them in a directive.
link:function(scope,element,attrs) {
$rootScope.$watch('params.source',function() {
var link = scope.link;
var name = scope.name;
var href = $state.href('home',{source:link});
if($state.params.source.indexOf(scope.link) == -1) {
var template = '<a href="'+href+'" class="btn
btn-default">'+name+'</a>';
}
else template = '<a href="'+href+'" class="btn
btn-default">'+name+' what!?</a>';
el = $compile(template)(scope);
element.replaceWith(el);
});
}
But after the the first change of params, element is forgotten and answers with Cannot read property 'replaceChild' of null
How can I replace element?
Edit: I need to swap the whole Element for sowmething else in final product, that's why I can't use ng-show or ng-if
Thanks!
You may be better off using ng-hide or ng-show in your html. Maybe something like this:
<div ng-repeat="yourArray as item">
<a href="{{item.href}}" class="btn
btn-default">{{item.name}} <section ng-show="conditionToShow">what!?</section></a>';
</div>
This is a bit cleaner of a solution and it keeps you from having to add a watch. Watches are expensive and you should avoid them if at all possible.
Edit:
<div ng-repeat="yourArray as item">
<div class="singleBtn" ng-show="conditionToShow" >
{{item.name}}
</div>
<div class="multiBtn" ng-hide="conditionToShow">
<a //rest of multi btn code
</div>
</div>
Because of the fact that Angular provides both ng-show and ng-hide directives this allows you to control the presentation of these elements based on just one conditional.(i.e. your state.params)
When it is true, it will show just one btn and when it's false it will show multiple buttons. If the logic needs to be reversed, just switch ng-show and ng-hide.
Hopefully I understood your need better this time.

Override ng-hide on ng-click

On the process of trying to learn angularjs, I am creating a set of divs using ng-repeat. I have five divs in all. I am only displaying the first div using $firstdirective component.
<div class="container" ng-app="demoApp" ng-controller="demoController">
<div class="row" ng-repeat="job in jobs" ng-hide="!$first">
<div class="col-md-4">
{{job}}
</div>
<div class="col-md-4">
<button class="btn-primary btn-xs add" ng-click="showNext($event)" ng-hide="$last">Add</button>
<button class="btn-primary btn-xs delete" style="display: none;">Delete</button>
</div>
</div>
</div>
That works.
Now, each div has two buttons, Add and Delete. When I click add I want to hide the add button and delete button of the current row and open the next row. So I wrote,
$scope.showNext = function(evt) {
var elm = evt.target;
$(elm).hide();
$(elm).next().hide();
$(elm).closest("div.row").next().slideDown();
};
This is not working as ng-hide is overriding my slideDown. What is the angularjs way of doing it?
Also I would like the delete button only on the $last visible row && !first. How to find last visible row in ng-repeat?
Fiddle: https://jsfiddle.net/codeandcloud/u4penpxw/
You shouldn't manipulate the DOM with things like $(elm).hide();, you should use ng-class= to let Angular add classes that hide or show the elements. If you switch your ng-repeat to ng-repeat="job in jobs track by $index", you could write something like that in your showNext()-function:
$scope.showNext = function(index) {
$scope.currentJob = $scope.jobs[index]
}
and call it like ng-click="showNext($index) - of course you'd need to do some refactoring as well. But then you could write something like ng-class="{hidden: (job == currentJob}" to hide all buttons except in the current row.
There's a lot of good thoughts in this q&a: "Thinking in AngularJS" if I have a jQuery background?

AngularJS - One controller on two sibling DOM elements

I'm trying to do a very simple thing. I'm displaying a list of values with Edit links beside them. Clicking the edit link reveals a form that lets you update the value.
(I've simplified the question so the items just have one field "name". My actual use case has more fields, but is canonically equivalent.)
I've run into something that looks like a bug in Angular, but given my limited experience with it, I'm not so sure. Here's how I'm trying to do it:
<div ng-repeat-start="item in items" ng-controller="ItemCtrl as ctrl" ng-hide="ctrl.isEditing">
<span>Name: {{item.name}}.</span>
<a href='#' ng-click='ctrl.startEditing()'>Edit</a>
</div>
<div ng-repeat-end ng-show="ctrl.isEditing">
<input type='text' ng-model='item.name'/>
<a href='#' ng-click='ctrl.save()'>Save</a>
</div>
My controller looks like this:
app.controller('ItemController', function() {
this.isEditing = false;
this.startEditing = function() { this.isEditing = true; }
this.save = function() { this.isEditing = false; }
});
Clicking on Edit link calls the right controller function, and the first div hides. But the second div does not hide.
When I rearrange the code to look like this (essentially wrapping the two divs with a wrapper element), all is well.
<div ng-repeat="item in items" ng-controller="ItemCtrl as ctrl">
<div ng-hide="ctrl.isEditing">
<span>Name: {{item.name}}.</span>
<a href='#' ng-click='ctrl.startEditing()'>Edit</a>
</div>
<div ng-show="ctrl.isEditing">
<input type='text' ng-model='item.name'/>
<a href='#' ng-click='ctrl.save()'>Save</a>
</div>
</div>
Any idea what is technically wrong with the first version? Note that the <input> boxes do get populated with the right values from item.name.
PS: There's a reason why I'm trying to keep the two divs siblings: in my use case, they are actually implemented as two trs which are supposed to appear right below each other in a table.
It's not a bug from angular but it is quite logical.
<div ng-repeat-start="item in items" ng-controller="ItemCtrl as ctrl" ng-hide="ctrl.isEditing">
<span>Name: {{item.name}}.</span>
<a href='#' ng-click='ctrl.startEditing()'>Edit</a>
</div>
<div ng-repeat-end ng-show="ctrl.isEditing">
<input type='text' ng-model='item.name'/>
<a href='#' ng-click='ctrl.save()'>Save</a>
</div>
If you see the above code you have injected controller only to the first div so obviously sibling div doesn't know what is ctrl or ItemCtrl until and unless you do as in you second way.
So if you want to achieve it as sibling, if you are using routing then add the controller attribute in your route path.
So that the controller will be active for that entire template and you can achieve what you want.
Hope it helps.
Thats because controller has its own scope. When you placed controller ONLY on your first div controllers' scope is limited to only this one div. When you wrapped both your divs inside another and place controller on wrapper, controllers' scope now is all that inside this wrapper. So in this case this works fine and this is not the angular bug

change angular ui-view inside ng-repeat

I am using multiple named ui-views in a single controller. Everything is working as spected when name the ui-view in the html file with this code:
<div class="box">
<div ui-view="selector"></div>
<div ui-view="widget1"></div>
<div ui-view="widget2"></div>
<div ui-view="widget3"></div>
<div ui-view="widget4"></div>
</div>
But when I want to load dynamically some of those views with a ng-repeat it does not update the information.
<div class="widgets">
<div class="widget-container" ng-repeat="widget in widgets">
<div ui-view="{{widget}}"></div>
</div>
</div>
How could I load ui-views from my ng-repeat?
This hadn't been answered so i decided to go ahead and show how to do it. Unfortunately for some reason the attempt in the question didn't work but what you can do is link to a function between curly brackets and return the string of the view you want. For instance the controller would look like this:
$scope.groups = ['main','cases', 'models'];
$scope.pickView = function(index){
return $scope.groups[index];
};

Resources