In an angular application I am working on I am trying to use compound filtering in conjunction with pagination. There will be a table with x entries, each row with three columns. I have an input above each column where the user can filter the columns based on input text. The filters compound to narrow down the results to the best - but in my use case there still might be too many to display on one page, therefore I am trying to use pagination.
The table body looks like this:
<tbody>
<tr>
<td class="col-md-4">
<input type="text" ng-model="nameFilter" class="form-control" placeholder="Name">
</td>
<td class="col-md-4">
<input type="text" ng-model="ageFilter" class="form-control" placeholder="Age">
</td>
<td class="col-md-4">
<input type="text" ng-model="stateFilter" class="form-control" placeholder="State">
</td>
</tr>
<tr data-ng-repeat="person in people | filter:{'name': nameFilter} | filter:{'age':ageFilter} | filter:{'state':stateFilter} | limitTo: itemsPerPage">
<td class="col-md-4">{{person.name}}</td>
<td class="col-md-4">{{person.age}}</td>
<td class="col-md-4">{{person.state}}</td>
</tr>
</tbody>
The tricky part I am having is getting the pagination to work with the filters applied. I have seen an example like this one where the author uses a $watch on a singular search field and can still manage pagination. I also have seen through research there is a $watchCollection that can be used, but am not sure how to implement it.
$scope.$watchCollection('[nameFilter, ageFilter, stateFilter]', function(newValues) {
//not sure what to do here.
console.debug(newValues);
$scope.filtered = '';; //really it should equal the now filtered by 3 filters group of people
$scope.noOfPages = Math.ceil($scope.filtered.length/$scope.itemsPerPage);
});
Are watches even the best choice to use in this case? I have this working plunker as an example of what I am working on, but still need some guidance with the pagination. One of the questions I do have is I am not sure how to make the pagination apply specifically with the list of data. I have seen other examples and have mimicked but I think the filtering might be messing it up (not sure)? Any help appreciated.
You don't have to watch for the filters. You only need to bind the filters to your pagination. This way angular will handle everything and you don't need to write additional code in the controller. I updated your plunker and you can see how is everything is implemented with angular bindings.
Also you have limitTo filter that handles pagination
<tr data-ng-repeat="person in people | filter:{'name': nameFilter} | filter:{'age':ageFilter} | filter:{'state':stateFilter} | limitTo: itemsPerPage: (currentPage - 1) * itemsPerPage">
<td class="col-md-4">{{person.name}}</td>
<td class="col-md-4">{{person.age}}</td>
<td class="col-md-4">{{person.state}}</td>
</tr>
<ul uib-pagination
total-items="(people | filter:{'name': nameFilter} | filter:{'age':ageFilter} | filter:{'state':stateFilter}).length"
items-per-page="itemsPerPage"
boundary-links="true" ng-model="currentPage"></ul>
https://plnkr.co/edit/5bW3XV3eEmomOtHJWW7x?p=preview
Related
I've couple of select fields on basis of which I want to filter data. Here is working plukr: http://plnkr.co/edit/VUnAoL2Sl0IqGcZqHbbi?p=preview
If I select any one value from select, it works fine, but when I select the other one it don't show any data.
These filters are going to be dynamic (depending on number of columns) So I want that in HTML I could use it with single filter declaration like: <tr ng-repeat="row in data | filter:{columns: filt}">
Not like: <tr ng-repeat="row in data | filter:{columns: filt.something} | filter: {some: thing}">
<tr ng-repeat="row in _data = (data | filter:{columns: {dealType:filt.dealType}}) | filter:{columns:{primaryUse:filt.primaryUse}}">
<td ng-repeat="column in row.columns">
{{column[column.header]}}
</td>
</tr>
I have got the same issue and it worked with a change in your code as above. Thanks
Sorry if you find this question's solution is simple or silly.
Need suggestions or solution on this angular part.
I have an object containing array("value"), as shown below.
scope.resp.DefaultData.graphRowData = [
{YName:"Mary", value:[1,4], points:1},
{YName:"Tom", value:[2,5], points:1}
];
My Code viewer uses this style to render the array.
<table>
<tbody>
<tr ng-repeat="rowLabels in resp.DefaultData.graphRowData track by $index">
<th>
<input type="text" value="{{rowLabels.YName}}" ng-model="rowLabels.YName"/>
</th>
<td ng-repeat="value in rowLabels.value track by $index">
<input type="text" ng-model="value"/>
</td>
</tr>
</tbody>
</table>
The html viewer would render like below way:
<table>
<tbody>
<th>Mary</th><td>1</td><td>4</td>
<th>Tom</th><td>2</td><td>5</td>
</tbody>
</table>
Now to my question:
The table displays the data as per the model but if i try to update the table with custom or edit the values, doesn't update the model and the value remains same
For example: Mary has two tags of 1 and 4 values, if I try to change the 1 to 2 and 4 to 5, the data inside the model remains same without update.
Is there any way to fix in my code or should I change the array into array of objects like below
value:[{val:1},{val:4}]
and so ... for other objects under resp.DefaultData.graphRowData? Then it would work fine. Just confused why for array not working in angular js in my code! :(
You need to pass a reference of array instead of value in ng-model
<tr ng-repeat="rowLabels in resp.DefaultData.graphRowData track by $index">
<th>
<input type="text" value="{{rowLabels.YName}}" ng-model="rowLabels.YName"/>
</th>
<td ng-repeat="value in rowLabels.value track by $index">
<input type="text" ng-model="rowLabels.value[$index]"/>
</td>
</tr>
check this working plunker
I am making a custom directive which will paginate and make my table sortable. I am trying to make it so that multiple data types will work with the sorting with no effort from the user. Here is my problematic html in my templateUrl:
<tr ng-repeat="row in cndPaginatedObject track by $index">
<td ng-repeat="data in row track by $index" ng-if="!isValidDate(row[cndTableHeaders[$index]])">
{{row[cndTableHeaders[$index]]}}
</td>
<td ng-repeat="data in row track by $index" ng-if="isValidDate(row[cndTableHeaders[$index]])">
{{row[cndTableHeaders[$index]] | date: cndDateFormat}}
</td>
</tr>
I would prefer to use ng-if as opposed to ng-show/hide because it would unnecessarily duplicate DOM elements with ng-show/hide and just not show them. I have tried both ways, but I get the same result either way. The content of columns with dates in them don't show up, but the td itself is still there taking up space. So, I end up having three headers for columns and more than three columns. As far as I was aware, ng-if is supposed to remove the entire element from the DOM.
here is my other code of significance:
scope.isValidDate = (data) => {
var timestamp = Date.parse(data);
return !(isNaN(timestamp));
}
As for cndPaginatedObject, it is just an array of objects with multiple strings contained within. Thank you for any help in advance!
UPDATE:
Just thought I would also add that the cndTableHeaders is literally the table headers. cndPaginatedObject uses the table headers as the keys to each of the values. This way they show up in the proper order in the table.
Two ways to do what you're doing better without the need to produce extra doms and looping a ng-repeat twice
A better way to do what you want to do is
<tr ng-repeat="row in cndPaginatedObject track by $index">
<td ng-repeat="data in row track by $index">
{{(!isValidDate(row[cndTableHeaders[$index]])) ? row[cndTableHeaders[$index]] : row[cndTableHeaders[$index]] | date: cndDateFormat}}
</td>
</tr>
Or add a span inside the with the ng-if logic
<tr ng-repeat="row in cndPaginatedObject track by $index">
<td ng-repeat="data in row track by $index"">
<span ng-if="!isValidDate(row[cndTableHeaders[$index]])>
{{row[cndTableHeaders[$index]]}}
</span>
<span ng-if="isValidDate(row[cndTableHeaders[$index]])">
{{row[cndTableHeaders[$index]] | date: cndDateFormat}}
</span>
</td>
</tr>
Alternatively you could create your own filter which checks if date is valid and then using the date filter to filter it.
I am trying to make an entirely dynamic angularJS page in such a way that the data-model and header-column-model can be passed in as JSON format and the angular/view can decode everything no matter how many columns, etc.
I have everything (Column Headers, single-Column sorting, multi-Column textbox filtering) working except the data portion where it looks like I want to nest {{bindings}}.
The idea is something like this:
<tr ng-repeat="item in model | filter:filters | orderBy:predicate:reverse">
<td ng-repeat="header in headers">{{{{$parent.item}}.{{header.colName}}}}</td>
</tr>
So that it would resolve to item.column (which is how this is done normally/statically).
However, there doesn't seem to be any embedding/nesting allowed for the double-squiggle references; aka. the first '{{' matches with the first '}}' automatically.
Is there a [good] way to do this?
Try using $eval:
<tr ng-repeat="item in model | filter:filters | orderBy:predicate:reverse">
<td ng-repeat="header in headers">{{$eval('item.' + header.colName}}</td>
</tr>
Here is a plunker showing that concept:
http://plnkr.co/edit/msGuMCCj477jyUAqjChz?p=preview
I am new to angularJS i have an object of which contains nearly 4000 records which i am showing them using ng-repeat
<div infinite-scroll='loadMore()' infinite-scroll-distance='2'>
<li ng-repeat="outlet in outlets | filter:searchText" >
<table>
<tr>
<td>
<div clss="title">{{outlet.name}}</div>
<div class="discription">{{outlet.address}},{{outlet.cityName}},{{outlet.countyName}}</div>
</td>
</tr>
</table>
</li>
</div>
now i wanted to use endless list by using ng-infinitescroll but the problem is the filter is happening only which are visible not in total 4000 records so what do i have to implement to get the search functionality to be done in total 4000 records and endless list
searchText filter would only work on in-memory data. In order to search things that havent been loaded yet you would need to implement a server-side search api.