Edit a filter with a simple checkbox - angularjs

I would like to make an option for showing inactive users by checking a button.
Here is the users array:
$scope.users = [
{firstname: 'Paul', inactive: true},
{firstname: 'Mark', inactive: false},
{firstname: 'Maggie', inactive: false},
{firstname: 'Lucy', inactive: true}
];
And the table to display it:
<table>
<thead>
<th>Firstname</th>
<th>Activity</th>
</thead>
<tbody>
<tr ng-repeat="user in users | filter: ??">
<td>{{user.firstname}}</td>
<td>{{user.inactive}}</td>
</tr>
</body>
</table>
<input type="checkbox" ng-click="showInact()">Show inactives
I'm learning AngularJS, I didn't found an interesting way to do that. Can you help me finding a solution?
Thanks :)

Just do this way:
1) At you controller:
$scope.showInactive = false;
$scope.filterInact = function(item)
{
return item.inactive === $scope.showInactive;
};
$scope.showInact = function() {
$scope.showInactive = !$scope.showInactive;
}
2) Setup the filter:
<tr ng-repeat="user in users | filter:filterInact">
<td>{{user.firstname}}</td>
<td>{{user.inactive}}</td>
</tr>

I personally like using a filter function to filter data. This approach is flexible and can easily interact with your other form variables. From the angular docs, the filter expression can be a:
function(value, index): A predicate function can be used to write
arbitrary filters. The function is called for each element of array.
The final result is an array of those elements that the predicate
returned true for.
So an example filter function might look like this:
$scope.filterFunc = function (user) {
//if the checkbox is checked show everyone, else only those that aren't inactive
return $scope.showInactives || !user.inactive;
};
And the HTML would be changed to accommodate the filter function. Specifically, I'm binding the checkbox to a $scope variable ($scope.showInactives) that the filterFunc function uses in its logic. And of course the ng-repeat is using the function called filterFunc.
<input type="checkbox" ng-model="showInactives"/>Show inactive users
<br/>
<table>
<thead>
<tr>
<th>Firstname</th>
<th>Activity</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in users | filter:filterFunc">
<td>{{user.firstname}}</td>
<td>{{user.inactive}}</td>
</tr>
</tbody>
</table>
And if you need any other convoluted logic, the filter function allows you a lot of freedom. The only thing it needs to return is a boolean (true or false).
Demo.
Unrelated to filtering, I also had to fix the table's HTML by putting the <th> inside a table row <tr>.

Easiest Way !! You can set value of filter on click event.
<tr ng-repeat="user in users | filter:filters">
<td>{{user.firstname}}</td>
<td>{{user.inactive}}</td>
</tr>
on click filter set to check box
<input type="checkbox" ng-click="filters.inactive = true">
First, You need to set controller name instade of "myCtrl.js" whatever your controller name.

Related

Angular ng-repeat stay paged after filter

I'm using AngularJS and I have a simple table using ng-repeat and filter. When i insert anything at the search input it filters OK but it keeps paginated as it was before I filter the table. Any ideas why this is happening ?
Here's my code:
//some var initialization
$scope.pagina = {};
$scope.pagina.currentPage = 1,
$scope.pagina.numPerPage = 10,
$scope.pagina.maxSize = 5;
//return from server with data
coresFactory.getCores().then(function(response) {
$scope.tabelaCores = response.data;
$scope.filteredTodos = $scope.tabelaCores.cores.slice(0, 10);
});
//do pagination
$scope.$watch('pagina.currentPage', function() {
var begin = (($scope.pagina.currentPage - 1) * $scope.pagina.numPerPage),
end = begin + $scope.pagina.numPerPage;
$scope.filteredTodos = $scope.tabelaCores.cores.slice(begin, end);
},true);
<input ng-model="pesquisaTabela" type="search" style="width:300px;" class="form-control input-inline" placeholder="" aria-controls="sample_1"></label>
<table class="table" style="margin-bottom:5px;border:1px solid #DDDDDD" id="sample_1">
<thead>
<tr style="background-color:#F9F9F9">
<th style="width:100px; text-align:center;"> Id </th>
<th> Nome </th>
<th> Plural </th>
<th style="width:100px; text-align:center;"> Ativo </th>
</tr>
</thead>
<tbody>
<tr ng-repeat="cor in filteredTodos | filter:pesquisaTabela" ng-click="setSelected(cor.id)" ng-class="{linhaSelected: cor.id === idCorSelecionada}">
<td style="text-align:center;"> {{cor.id}} </td>
<td style="text-transform:capitalize"> {{cor.nome}} </td>
<td style="text-transform:capitalize"> {{cor.plural}} </td>
<td style="text-align:center;">
<span ng-if="cor.status=='sim'" class="label label-sm label-success"> Sim </span>
<span ng-if="cor.status=='nao'" class="label label-sm label-danger"> Não </span>
</td>
</tr>
</tbody>
</table>
<pagination first-text="<<" last-text=">>" next-text=">" previous-text="<" ng-model="pagina.currentPage" total-items="tabelaCores.cores.length" max-size="pagina.maxSize" boundary-links="true"></pagination>
You have quite a disconnect between various parts
Pagination is working off of one array, display from another and filter from yet another because when filter kicks in it returns a new filtered array.
The way you have things structured, your filter won't work properly either.
When you slice the main data array the filter is only going to work on that part that is sliced....not the whole main array
In order for pagination to be synchronized with filtering your simplest start point would likely be do your own filtering and share the same filtered array between the pagination and the table.
There are some other built in filters that you could use also like limitTo that takes 2 arguments limit and begin. That would help you get rid of currrent slice
There are lots of available grid/table directives available.
Using one of those would be my best suggestion
There is one other way you could do this all in the view. There is a syntax for ng-repeat that creates a new filtered array on the scope and therefore gives you access to it's length
<tr ng-repeat="row in filteredData = ( data |filter:pesquisaTabela | limitTo:10:start)">
Using this you could pass filteredData array to the total-items of the pagination directive.
It also now lets you do things like:
<div> Filtered length is: {{filteredData.length}}</div>

Angular js: Access data filtered in ng-repeat (ngRepeat) from controller

I need to access data that was already filtered on a template (inside a ng-repeat) from my controller.
Here's what I mean:
I have this table in my template:
<table class="table">
<thead>
<th>Name</th>
<th>Gender</th>
</thead>
<tbody>
<tr ng-repeat="person in persons | filter:query">
<td>{{person.name}}</td>
<td>{{person.gender}}</td>
</tr>
</tbody>
</table>
Then I have a <select> that is used to filter data by gender
<h1>Cluster:</h1>
<select ng-model="query.gender" >
<option value ="">ALL</option>
<option value ="male">Male</option>
<option value ="female">Female</option>
</select>
This works ok.
If someone selects one of the options, I need to do something with the filtered results.
That's why I have a $watch in my controller that looks for changes to the filter query:
$scope.$watch('query.gender', function(newValue, oldValue) {
// Here is where I need to get filtered results
});
My question is:
How do I access the FILTERED content from the controller?
I'd like this preferably without having to do another "filtering" operation in the controller... (since the data was already filtered, the result is sitting somewhere in memory, right?)
You can simplify your code with the following:
<tbody>
<tr ng-repeat="person in (filteredPersons = (persons | filter:query))">
<td>{{person.name}}</td>
<td>{{person.gender}}</td>
</tr>
</tbody>
After that, you can get access to $scope.filteredPersons into controller or {{filteredPersons}} right into the view :)
I'm not entirely sure, but you can use the filter function in your controller. So try something like:
$scope.$watch('query.gender', function(newValue, oldValue) {
var x = $filter('filter')($scope.persons, $scope.query);
});
x should then contain the same filtered data as in your table I think. Have a look at the docs here: http://docs.angularjs.org/api/ng.filter:filter for some more info.

turn off re-sorting in angularJS while editing

I have an AngularJS app that lists a bunch of items in a table. Like this:
<table class='unstyled tmain'>
<tr>
<td ng-click='setSort($event)'>X</td>
<td ng-click='setSort($event)'>Desc</td>
<td ng-click='setSort($event)'>created</td>
<td> </td>
</tr>
<tr ng-repeat="item in items | orderBy:itemNormalizationFunction:sortReverse">
<td><input type='checkbox' ng-model='item.done'
ng-click='onCheckedChanged(item)'/></td>
<td><div edit-in-place="item.text"
on-save="updateItemText(value,previousValue,item)"></div></td>
<td><span class='foo'>{{item.created | dateFormatter}}</span></td>
</tr>
</table>
The table headers are clickable to set the sort order. The cell in the 2nd column in each data row is editable "in place" - if you click on the text it gets replaced with an input textbox, and the user can edit the text. I have a little directive enabling that. This all works.
The problem comes in while editing. Suppose I have it set to sort by "description" (the 2nd column). Then if I edit the description (via the edit-in-place directive), as I am typing in the input box, the sort order changes. If I change the first few characters, then angular re-sorts and the item is no longer under my cursor. Nor is it even focused. I have to go hunting through the list to find out where it got re-sorted to, then I can re-focus, and resume typing.
This is kinda lame.
What I'd like to do is tell angular to (a) stop re-sorting while I am keying in the input box, or (b) sort on a separate (not-displayed) index value that preserves the ordering before the edit began. But I don't know how to do either of those. Can anyone give me a hint?
I know this is sort of complicated so I'll try to put together a plunkr to show what's happening.
This is the plunkr that shows how I solved the problem.
http://embed.plnkr.co/eBbjOqNly2QFKkmz9EIh/preview
You can create custom filter and call that only when necessary. Example when you click on 'Grid header' for sorting or after dynamically adding/removing values to array, or simply click of a button(Refresh Grid)
You need to dependency Inject Angular filter and sort filter
angular
.module('MyModule')
.controller('MyController', ['filterFilter', '$filter', MyContFunc])
function ExpenseSubmitter(funcAngularFilter, funcAngularFilterOrderBy) {
oCont = this;
oCont.ArrayOfData = [{
name: 'RackBar',
age: 24
}, {
name: 'BamaO',
age: 48
}];
oCont.sortOnColumn = 'age';
oCont.orderBy = false;
var SearchObj = {
name: 'Bama'
};
oCont.RefreshGrid = function() {
oCont.ArrayOfData = funcAngularFilter(oCont.ArrayOfData, SearchObj);
oCont.ArrayOfData = funcAngularFilterOrderBy('orderBy')(oCont.ArrayOfData, oCont.sortOnColumn, oCont.orderBy);
}
}
and call in HTML something like:
<table>
<thead>
<tr>
<th ng-click="oCont.sortOnColumn = 'age'; oCont.RefreshGrid()">Age</th>
<th ng-click="oCont.sortOnColumn = 'name'; oCont.RefreshGrid()">Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="val in oCont.ArrayOfData">
<td>{{val.age}}</td>
<td>{{val.name}}</td>
</tr>
</tbody>
</table>

Hide/show particular element on click with AngularJS

I have an HTML table with many rows in it and want to hide a row when the user clicks the delete button for that particular row. I'm having trouble doing it with Angular and the ng-hide directive.
Here's my (simplified) HTML code for just two rows:
<tr ng-hide="isRowHidden">
<td>Example template title</td>
<td>
Delete template
</td>
</tr>
<tr ng-hide="isRowHidden">
<td>Another example template title</td>
<td>
Delete template
</td>
</tr>
And here is my Angular code (in CoffeeScript) thus far:
$scope.deleteTemplate = (templateId) ->
console.log "Deleting template id #{templateId}"
$scope.isRowHidden = true
I know that the last line is incorrect because it hides all rows instead of just one. What am I missing? Thanks!
You need to model the data as an array with multiple isRowHidden values, then list the rows via ng-repeat:
http://jsfiddle.net/XqchD/ (uses JS, not coffee)
myApp = angular.module("myApp", [])
FieldCtrl = ($scope) ->
$scope.data = fields: [
value: "1F"
isRowHidden: false
,
value: "2F"
isRowHidden: false
]
$scope.deleteTemplate = (field) ->
console.log field
field.isRowHidden = true
HTML:
<table ng-repeat="field in data.fields">
<tr ng-hide="field.isRowHidden">
<td>{{field.value}}</td>
<td>
Delete template
</td>
</tr>
</table>

using ng-model within nested ng-repeat directives

I'm trying to use ng-model "within" a ng-repeat directive that is itself nested in a ng-repeat directive.
The following jsfiddle demonstrates my problem and my two attempts to solve it.
http://jsfiddle.net/rskLy/4/
My Controller is defined as follows:
var mod = angular.module('TestApp', []);
mod.controller('TestCtrl', function ($scope) {
var machine = {};
machine.noteMatrix = [
[false, false, false],
[false, true, false],
[false, false, false]
];
$scope.machine = machine;
// ...
});
1.
<table>
<thead>
<tr>
<th>--</th>
<th ng-repeat="no in machine.noteMatrix[0]">{{$index+1}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="track in machine.noteMatrix">
<td>--</td>
<td ng-repeat="step in track">
<input type="checkbox" ng-model="track[$index]"> {{step}}
</td>
</tr>
</tbody>
</table>
The first example/attempt updates the machine.noteMatrix inside the controller, but everytime a checkbox is pressed, angularjs displays the following error twice in the javascript console:
Duplicates in a repeater are not allowed. Repeater: step in track
and sometimes it will also display this error:
Duplicates in a repeater are not allowed. Repeater: no in machine.noteMatrix[0]
2.
<table>
<thead>
<tr>
<th>--</th>
<th ng-repeat="no in machine.noteMatrix[0]">{{$index+1}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="track in machine.noteMatrix">
<td>--</td>
<td ng-repeat="step in track">
<input type="checkbox" ng-model="step"> {{step}}
</td>
</tr>
</tbody>
</table>
The second example/attempt reads the data correctly from the noteMatrix and no errors are displayed in the javascript console when checking/unchecking the checkboxes. However changing their states is not updating the machine.noteMatrix in the controller (press the "Show Matrix" button to see the matrix in the jsfiddle).
Can anyone shed a light on this? :)
This is a common issue for people in Angular. What's happening is ng-repeat creates it's own scope, and if you pass an array of value types into it (such as an array of booleans), updating them will not update the parent scope. You need to pass an array of reference types to the ng-repeat and update those in order for it to persist:
Here is a solution showing this based off of your fiddle
machine.noteMatrix = [
[
{ value: false },
{ value: false },
{ value: false }
],
[
{ value: false },
{ value: true },
{ value: false }
],
[
{ value: false },
{ value: false },
{ value: false }
]
];
It's ugly, I know, but the alternative is uglier. You'd need to do something to manage your own loop and reference the values via the $parent or $parent.$parent object. I don't recommend this.
Your first solution seems to be correct.
This appears to be a bug, introduced in the unstable branch of angularJS (you are using 1.1.4, which is unstable - the stable version, 1.0.6, works as expected)
EDIT:
Turns out this isn't a bug, but a new feature - the ngRepeat directive now allows for a tracking function to be defined (associating the model's id with the DOM element), and no longer allows for these tracking variables to be repeated. See the corresponding commit, the docs for 1.1.4 on ngRepeat and the changelog
You don't need to alter your model or access the $parent. What's missing is "track by $index":
<tr ng-repeat="track in machine.noteMatrix">
<td>--</td>
<td ng-repeat="step in track track by $index">
<input type="checkbox" ng-model="track[$index]"> {{step}}
</td>
</tr>
Here it is in yr fiddle.
More information: Angular ng-repeat dupes
I'm not sure if track by existed yet when the question was asked, so the other answers may have been correct at the time, but in current Angular this is the way to go.

Resources