Get $index after filter - angularjs

I have a small image gallery which has a search box and when the user clicks on a image it'll open a lightbox with that same image.
Basically I'm passing the $index to a function which opens the item in $scope.list[lb.index].
my code:
HTML
<input type="text" ng-model="query.name" />
<ul class="list" ng-show="list.length>0">
<li ng-repeat="item in list | filter:query">
<a class="img" ng-style="{'background-image':'url(/uploads/<%item.image%>)'}" ng-click="openLB($index)"></a>
</li>
</ul>
<div class="lightbox" ng-if="lb.show">
<a class="prev" ng-show="list.length>1" ng-click="changeItemLB(lb.index, 'prev')"></a>
<a class="next" ng-show="list.length>1" ng-click="changeItemLB(lb.index, 'next')"></a>
<div class="holder">
<img ng-if="list[lb.index].image.length" ng-src="/uploads/<%list[lb.index].image%>" />
</div>
</div>
Angular
$scope.openLB = function(index) {
$scope.lb.show = true;
$scope.lb.index = index;
};
$scope.changeItemLB = function(index, action) {
var tot = $scope.list.length-1,
goto = 0;
if(action=='prev') goto = index==0 ? tot : index-1;
if(action=='next') goto = index==tot ? 0 : index+1;
$scope.openLB(goto);
}
The problem is after the user filters the results (search input), the click still maintains the index from the list without the filter which makes the lightbox open the wrong image. Does anyone know how to solve this?
Thanks

Pass object instead of index
Suppose you have 5 item in your list
filter showing two result.
If you click then $index value will be your current view's index
but your list still then have 5 item.
Try like this
<a class="img" ng-style="{'background-image':'url(/uploads/<%item.image%>)'}" ng-click="openLB(item)"></a>
controller
$scope.openLB = function(item) {
var index = $scope.list.indexOf(item);
$scope.lb.show = true;
$scope.lb.index = index;
};
EDIT
How to get Filtered result
Try like this
view
<li ng-repeat="item in filtered= (list | filter:query)">
controller
$scope.filtered=[];
Now you can get yor filtered list from $scope.filtered

Related

How to get model array after angularjs drag and drop?

I have a list in which data is filled as such:
<ul ui-sortable="sortableOptions" ng-model="hiringstagelist" class="sortable list">
<li ng-repeat="y in hiringstagelist">
<div class="row">
<div class="col-md-1">
<label class="switch">
<input id="{{y.id}}" class="switch-input" type="checkbox" ng-model="y.isDeleted" ng-click="deletehiringstage(y.id)" />
<span id="data_{{y.id}}" class="switch-label" data-on="" data-off=""></span>
<span class="switch-handle"></span>
</label>
</div>
<div class="col-md-11">
{{y.stageName}}
<span style="float:right;margin-right:10px;"><img src="~/Images/sortarrow.png" /></span>
</div>
</div>
</li>
</ul>
As I'm using angularjs drag and drop hence as soon as I drag and drop any item in the list an event gets triggered which is :
$scope.sortableOptions = {
stop: function (e, ui) {
var model = ui.item.model;
console.log(model);
var index = ui.item.sortable.index;
var draggedModel = ui.item.sortable.model;
var newProdArray = ui.item.sortable.resort.$modelValue;
}
}
With index I only get the old value of the item in the list. "newProdArray" doesn't seems to work. Can i use anything so that i can get a model with new index values along with other parameters od the item like id etc ?
Please guide
I think you want ui.item.sortable.dropindex which gives you the index the item was dropped at. See here for documentation.

ng-show if filter and inner if statements are valid

I have an ng-repeat being created like so:
<div class="items">
<!-- WANT TO HIDE THIS ENTIRE AREA IF THERE IS NO VALID ITEMS -->
<b>Items</b>
<span ng-repeat="item in items | orderBy:'priority'" itemid="{{item.$id}}" ng-if="!item.groupID || item.groupID == 'misc'">
{{item.title}}
</span>
</div>
As you can see I have an ng-if which checks if the item has a null item.groupid or is in the misc category.
Many times there is no items that match all of these criteria and in that case I want to hide the outer div <div class="items">
I can't figure out how to hide it because I can't do an ng-show on the inner elements part of the loop.
You can use a filter, and assign the result of the filter to a variable. Use that variable to decide if the enclosing div should be shown or not.
Here's a plunkr showing how it works:
<div ng-show="filteredItems.length > 0">
Test
<div ng-repeat="item in items | filter:hasGroupFilter(group) as filteredItems">
{{ item.name }} - {{ item.group }}
</div>
</div>
And in the controller:
$scope.hasGroupFilter = function(group) {
return function(item) {
return !item.group || item.group === group;
};
};
I think the cleanest way to do that is to pre filter the list, and add a condition on your parent div.items instead of using ng-if in each one of the span in the ng-repeat.
Here is a working plunker
You should filter the list in your controller and just add the condition on the parent div
<div class="items" ng-if="filteredItems.length">
<b>Items</b>
<span ng-repeat="item in filteredItems | orderBy:'priority'" itemid="{{item.$id}}">
{{item.title}}
</span>
</div>
You could potentially create a function that has access to a public variable on the outer scope. It would look something along the lines of this:
Inside your JS file
$scope.NoValidItems = true; //assume no valid items code also might look different based on how you format your angular
$scope.CheckItems = function(item){
var check = false;
if(item.groupID && item.groupID !== 'misc'){
check = true;
$scope.NoValidItems = false;
}else
check = false;
return check;
};
HTML
<div class="items" ng-if="!NoValidItems ">
<!-- WANT TO HIDE THIS ENTIRE AREA IF THERE IS NO VALID ITEMS -->
<b>Items</b>
<span ng-repeat="item in items | orderBy:'priority'" itemid="{{item.$id}}" ng-if="!CheckItems(item)">
{{item.title}}
</span>
</div>
This should work, and the if statement might not be exact, but you get the drift. Pretty much if there is one that is valid it will show, but if there is not a valid item do not show. Valid being meeting your conditional criteria. Hopefully this helps, and I explained it alright

Display more items when more link selected using Angularjs

I am trying to display list of items, when I click more link it should display 10 more items in the list.
Here is my current code:
<div ng-repeat="items in itemList()">
{{items.name}}
</div>
and my controller is:
$scope.itemsList = function(){
return service.getItemList();
}
How do I add more link under the items, display 10 more items when I click 'more'
The easiest way would be to use the limitTo filter and use a button with ngModel to change the value that is limited. Documentation: https://docs.angularjs.org/api/ng/filter/limitTo
<div ng-repeat="items in itemList() | limitTo: displayNum">
{{items.name}}
</div>
<button ng-click="increaseBy(10)">Show More</button>
Controller Function:
$scope.displayNum = 10;
$scope.increaseBy = function(num) {
$scope.displayNum +=num;
}

Remove parent li after click child image

I want to remove the parent li when I click on the child image using angular. Here's what I have so far:
View
<ul class="cg-tag-list">
<li ng-repeat="tag in list">
<span>{{tag}}</span>
<img src="" ng-click="fnRemoveTag()"/>
</li>
</ul>
JS
//Remove Tag
$scope.fnRemoveTag = function () {
// Put Code here
}
pass the tag to the fnRemoveTag function;
<ul class="cg-tag-list" >
<li ng-repeat="tag in list">
<span>{{tag}}</span>
<img src="" ng-click="fnRemoveTag(tag)"/>
</li>
</ul>
$scope.fnRemoveTag = function (tag) {
// get the index of the tag which we are going to remove
var index = $scope.list.indexOf(tag);
// remove that tag from the `list` array
$scope.list.splice(index, 1);
//this will automatically update the dom for you
}
here is a DEMO
and note that you can pass the $index of the instead of tag like
<img src="" ng-click="fnRemoveTag($index)"/>
and remove the element in controller as,
$scope.fnRemoveTag = function(index) {
// remove that tag from the `list` array
$scope.list.splice(index, 1);
//this will automatically update the dom for you
}
example DEMO
if you don't use the orderBy in the ng-repeat then use the $index other vise use the tag, because if you use orderBy it will sort the array according to sort description and when you pass the $index it may not the correct index of $scope.list.
for EX:
say you have array like $scope.list = [3, 2, 1]; and you need to ng-repeat this with order by
<li ng-repeat="tag in list | orderBy:tag">
after the orderBy, ng-repeat will repeat the sorted array, but actually $scope.list remain as before & it will not gonna sort only the repeat order gets change.
so $index is the ng-repeat's index and its not represent the index of $scope.list, if you do not use the orderBy then both will be same.
then when you try to remove from the $scope.list with the passed $index it will may not remove the correct element.
see this DEMO
try to remove first one and note that it actually remove the last one. because we pass the $index as 0 to the function and remove the 0th index element of the $scope.list which has value of 3.
hope that makes sense.
View
<ul class="cg-tag-list" >
<li ng-repeat="tag in list">
<span>{{tag}}</span>
<img src="" ng-click="fnRemoveTag(tag)"/>
</li>
</ul>
JS
//Remove Tag
$scope.fnRemoveTag = function (listItem) {
var index = $scope.list.indexOf(listItem);
if (index > -1) {
$scope.list.splice(index, 1);
}
}
if you have jQuery you can use this:
var li = $(this).closest("li");
li.parent().remove(li);
Simply put $index as function parameter then use array splice method.
View
<ul class="cg-tag-list" >
<li ng-repeat="tag in list">
<span>{{tag}}</span>
<img src="" ng-click="fnRemoveTag($index)"/>
</li>
</ul>
Javascript (don't use orderBy filter in this purpose)
$scope.fnRemoveTag = function (index) {
$scope.list.splice(index, 1);
}

How to show / hide a list of items with a button based on the number of items (Angular)

I have a list of items like:
<ul>
<li ng-repeat="show in data_shows">
<span class="program_shows_show_day">{{show[2]*1000 | amDateFormat:'ddd D MMM'}}</span>
<span class="program_shows_show_time">{{show[2]*1000 | amDateFormat:'H:mm'}} - {{show[3]*1000 | amDateFormat:'H:mm'}}</span>
</li>
</ul>
When there are more than 4 items I would like to show only the first 4 items and hide the rest with a button ('Show all').
With jQuery I would just count the items and if they're over 4 items apply a class to the <ul>. In Angular it's a bit more difficult- I know ng-repeat produces a $index variable, but I can only use that within the <li> items right? So what's the best way to apply a class on the <ul> element?
ngClass is your best bet:
<ul ng-class="{'my-hiding-class': data_shows.length > 4}">
...
This just says add the class 'my-hiding-class' to the ul when your collection size is greater than 4. Documentation here
EDIT:
For the button you'd need to do something like this:
<button ng-click="toggleList()"></button>
Then in your directive:
scope.toggleList = function() {
scope.showList = !scope.showList;
};
Then change your ngClass declaration slightly:
<ul ng-class="{'my-hiding-class': data_shows.length > 4 && showList}">
Edit 2:
Using the limitTo filter seems nice, but you might want to wrap it with custom functionality if you decide to go the filter route:
angular.module('myApp').filter('limitlessFilter', function($filter){
return function(objects, limit, showAll) {
if(showAll){
return objects;
} else {
return $filter('limitTo')(objects, limit);
}
}
});
In your HTML:
<li ng-repeat="show in data_shows | limitlessFilter:4:showAll">
...
<button ng-bind="showAll && 'Show Less' || 'Show More'" ng-click="toggleShowAll()"></button>
In your controller/directive
scope.toggleShowAll = function() {
scope.showAll = !scope.showAll;
};
On a side note, if you want to limit the number of renders your li go through, be sure to use track by on your ng-repeat. Documentation here
You can use Angular's limitTo filter to originally limit the number of displayed items to 4.
Additonally, you can have 2 buttons (one for showing all items, one for limiting the displayed items to 4) and show/hide them (or disable/enable them) according to the currently displayed items and the total number of items.
E.g.:
// VIEW
<ul>
<li ng-repeat="item in items | limitTo:limit">{{item}}</li>
</ul>
<button ng-click="setLimit(4)" ng-disabled="(limit===4)||(items.length<=4)">
Show few
</button>
<button ng-click="setLimit(0)" ng-disabled="items.length<=limit">
Show all
</button>
// CONTROLLER
$scope.items = [...];
$scope.limit = 4;
$scope.setLimit = function (lim) {
$scope.limit = (lim <= 0) ? $scope.items.length : lim;
};
See, also, this short demo.
Only add 4 items to your data_shows collection in your controller. That way you can test the logic, testing the view would be more tricky.
This should do the trick
<ul>
<li ng-repeat="show in data_shows | limitTo:4">
<span class="program_shows_show_day">{{show[2]*1000 | amDateFormat:'ddd D MMM'}}</span>
<span class="program_shows_show_time">{{show[2]*1000 | amDateFormat:'H:mm'}} - {{show[3]*1000 | amDateFormat:'H:mm'}}</span>
</li>
</ul>
<button ng-hide="data_shows.length < 4">Show more</button>
You could replace the first "4" with a variable in your scope and modify this value when clicking the button.
But yeah, BenCr's answer sounds better in some ways.

Resources