Filter collection by child - angularjs

I have the following fictional object which I'm trying to filter:
{
"0":{
"boy":{
"age":"32",
"name":"Daniel Grey"
}
},
"1":{
"boy":{
"age":"23",
"name":"John Doe"
}
}
}
And then, the ng-repeat directive looks like this:
<ul>
<li ng-repeat="person in people">{{person.boy.name}}<li>
</ul>
My question is, how do I filter people by "name"? I've tried:
<input type="text" ng-model="name">
<ul>
<li ng-repeat="person in people | filter:name">{{person.boy.name}}<li>
</ul>
... but nothing happens [ ng-model seems disconnected from the view! ].
Any response is much appreciated!
Thank you!

Updated answer as per OP's updates
Looking at your fiddle, your $scope.people is essentially an array with one big JSON object, with multiple nested boy objects. This is hard to work with. If you have control over the construction of the JSON object, I will suggest converting into an array of multiple JSON objects, which may look something like:
$scope.people = [
{
"name":"Daniel Grey",
"age":"32",
"gender": "male"
},
{
"name":"John Doe",
"age":"23",
"gender": "male"
}
];
Notice how I converted the boy key into the gender attribute.
If you really have absolutely no control over the data structure, you may have to come up with a custom filter to parse through the nested structure.
Take a look at this fiddle. A few things to pay attention to:
I have to specify people[0] in ng-repeat to retrieve the one big JSON object in your array.
The custom nameFilter searches the .boy.name attribute only.
Original Answer
If you want your filter by just name, you will have to specify the specific attributes in your ng-model directive. So in your case, it will be
<input type="text" ng-model="search.boy.name">
<ul>
<li ng-repeat="person in people | filter:search">{{person.boy.name}}<li>
</ul>
But first you will need to fix your JSON object.
UPDATE:
Live demo on fiddle. I did notice that the search-by-name-only filter doesn't work with angularjs 1.2.1, but works with angularjs 1.2.2.

Related

Edit ng-repeated item using ngclick

Im learning Angular and am trying to build bits and bobs to learn. Ive tried to explain myself as clear as possible below - any help will be much appreciated.
Example:
http://jsbin.com/micasafetise/2/
http://jsbin.com/micasafetise/2/edit?html,js,console,output
I've created myself this bit of data
$scope.people =
[
{
"personID": 1,
"first_name": "Sam",
"last_name": "Stimpson",
"attending": false
},
{
"personID": 2,
"first_name": "Alison",
"last_name": "van Schoor",
"attending": true
},
{
"personID": 3,
"first_name": "Lindsay",
"last_name": "van Schoor",
"attending": false
}
];
I've created an output in my view like this:
<div ng-repeat="person in people">
<a href="" ng-click="isAttending()">
{{person.first_name}}{{person.last_name}} - {{person.attending}}
</a>
</div>
Now the bit I'm stuck on. I want to be able to click on a person and update their attending field from false to true. I understand I can use ng-click like this
$scope.isAttending = function() {
alert("is attending");
};
but do not have a clue how to update the person Ive clicked to change false to true in the $scope.people.
When I have achieved this I plan to have another ng-repeat with a filter to show those attending but Ill be able to do that part I believe.
can anyone help me or give me some advise, anything will be much appreciated at the moment.
Thanks in advanced.
I wrote out an example of what I'm trying to do here:
http://jsbin.com/micasafetise/2/
http://jsbin.com/micasafetise/2/edit?html,js,console,output
You could just pass the repeated person <a href="" ng-click="isAttending(person)"> and have your handler method take that object, so you can update its property (well you could do that inline as well in the view, but it is better to separate out and place the logic in the controller as you originally had).
$scope.isAttending = function(person) {
person.attending = true;
};
Just add a filter as well on both the sections to show who are all attending and who are not.
<div ng-repeat="person in people | filter:{attending:false} track by person.personID">
<a href="" ng-click="toggleAttending(person)">
{{person.first_name}}{{person.last_name}} - {{person.attending}}
</a>
</div>
and
$scope.toggleAttending = function(person) {
person.attending = !person.attending;
};
Demo
In your case you could probably optimize better (you have 2 filters on same list of people) by filtering out from the controller and populate to 2 list of people people.invited and people.attendance.
function updatePeopleAttendance(){
$scope.people.attending = [];
$scope.people.invited = [];
angular.forEach(people, function(person){
$scope.people[person.attending ? 'attending' : 'invited'].push(person);
});
}
Demo
or even better you could pass the id, index and status in the method and pull the item out of one array and push it the other one.

Angularjs filter through array of object literals

I am new to angular js so please forgive me for this basic question.
I have an array of object literals and I want to add filter in angular js according to particular field type.
Array of object literals:
var todos= [{text: 'To-DO1', done: false,group:'Group1' }, {text: 'To-do2', done: false, group:'Group2' } ];
I am using this code:
<select data-ng-model="selectOption" data-ng-options="item as item.gtype for item in items"> </select>
<li data-ng-repeat="todo in todos track by $index | filter: selectOption.gtype ">
Above filter is not working
For populating the select I am using :
$scope.items = [
{gtype:'Group1'},
{gtype:'Group2'},`enter code here`
{gtype:'Group3'},
{gtype:'Group4'},
{gtype:'Group5'}
];
Could someone please help me through ?
The problem is with the track by $index. The correct place for it is at the end of the expression, after the filter (see ngRepeat documentation)...
<li data-ng-repeat="todo in todos | filter: selectOption.gtype track by $index">
Live Demo
Edit: As requested, here's an updated fiddle to show an alternate message if filter produces nothing...
Live Demo

List items by type in AngularJs using ng-repeat

I have some items, lets say they are Cars. I am using ng repeat to list them.
Cars also have tires. Let's say I want to list all of the tire options, but there are a several different types of tires, so we want to separate them by their type {summer, road, racing, etc....):
<div class="col-md-4" ng-repeat="individualCar in allCars" ng-show="CarTypeFilter[individualCar.type]">
<div class="">
{{individualCar.name}}
</div>
<ul>
<!-- This is the list of all one type of tires -->
<li class="TireType1" ng-repeat="tire in individualCar.tires" ng-show="CarTireFilter[tire.type]">
{{tire.name}}
</li>
<!-- This is another list of all another type of tires -->
<li class="TireType2" ng-repeat="tire in individualCar.tires" ng-show="CarTireFilter[tire.type]">
{{tire.name}}
</li>
</ul>
</div>
This isn't quite working, since there isn't a separate list of tire types per Car on the Controller $scope, and Im not sure if I need to build one to reference. How do I filter or list the types of tires separately using ng-repeat?
I plan on styling them differently, hence the distinction needed.
Example JSON:
{
"jokes":[
{
"id:":1,
"name":"Tesla",
"type":"CAR",
"Tires":[
{
"id":1,
"type": "summer",
"name":"Goodyear"
},
{
"id":2,
"type": "summer",
"name":"Kuhmo"
},
{
"id":3,
"type": "winter",
"name":"Perelli"
},
{
"id":4,
"type": "racing",
"name":"Cooper"
}
]
},
{...}
]
}
In the controller, I have an associative array that stores the different types {summer, racing, etc) as the key, and a boolean value, so that when they want to see each option via a checkbox, the items show in the ng-repeat:
$scope.CarTireFilter = {"summer":true, "racing":false, ... };
If you are wanting to only show items in the list by some specific atrribute then a filter is what you need. See the docs for Filter & OrderBy.
Do not us ng-show rather use this:
div class="col-md-4" ng-repeat="individualCar in allCars|
filter: SET CONDITIONS HERE |
orderBy: SET CONDITIONS HERE">
The other thing I would like suggest is that if you are going to be adding styling based on the various types. You might want to look into doing this in a more angular way using ng-style. Look at the docs ng-style. This approach would allow you add the needed styles based on type and not necessarily need to have separate li tags with different classes. you should try and let angular handle this.

Filter through array of objects

I have an array of objects (~650) in the format
[1: {firstName: "Hello",
lastName: "World",
shortName: "h.world",
...some other items
},
2: {firstName: "John",
lastName: "Doe",
shortName: "j.doe",
...some other items
}]
And I need to filter through the list based on a text input, which should search firstName+ " "+lastName. So searching He or Hello W will return Hello World, and searching Hellow wouldn't return it (case doesn't matter). What's the best way to filter the array? I've seen Angulars filter method but I've not yet worked out how to do it.
EDIT What I have
<div ng-controller="NotesCtrl">
<p>Search for a user to see there sessions and notes</p>
<input type="text" id="user" ng-model="search.firstName" />
{literal}
<ul>
<li ng-repeat="u in users | filter:search">{{u.activeDirectoryName}}</li>
</ul>
{/literal}
</div>
From what I've read that should work, but the list doesn't filter. Do I need to do anything in the controller to get this to work?
Answer: Turns out even though in PHP my data was an array, in Javascript it was an object, changed it around a bit so it came through as an array and it seems to be working now.
In your case, search should be a function in your controller that takes each item in the array and returns true or false depending if it passes the filter or not.

In AngularJS, is it possible to filter inside of a specific element of an array using only a view?

I've got some data like this:
people = [
{
names: [
{first: "Joe", last: "Smith"},
{first: "Joseph", last: "Smith"},
...
]
},
...
]
In other words, an array of objects with an array of names. For example, a person could be called "Joe Smith" or "Joseph Smith". How can I use a filter to only search the first element of names? IE: If I typed in "Jo" or "Smith" it would find the first person. But, if I typed in "seph" it wouldn't.
I've been looking at the examples on this page, but there isn't really an example of filtering inside arrays. Here's what I've tried but it gives me an error:
<input ng-model="search.names[0].$">
TypeError: Cannot set property '$' of undefined
Working Code
Input HTML
<input ng-model="searchTerm">
Results
<tr ng-repeat="p in people | filter:searchFunc">...</tr>
Controller
$scope.searchFunc = function(person) {
var firstPersonsName = [person.names[0]]; // Wrapping in array since the 'filter' $filter expects an array
var matches = $filter('filter')(firstPersonsName, $scope.searchTerm); // Running firstPersonsName through filter searching for $scope.searchTerm
return matches.length > 0;
}
Plunker Demo
Answer to the question in your title
I played around with filter and it doesn't seem like you can go beyond one level deep when specifying a pattern object for it e.g. ng-model="search.names" works but ng-model="search.names.otherVal" doesn't.
Also, even if filter supported going beyond one level deep, you still wouldn't be able to do ng-model="search.names[0]". This is because filter expects the input to be an array, but the elements of your names array are all objects e.g. people[0].names[0] == {first: "Joe", last: "Smith"} so filtering will never work.
The only way to do what you are asking purely through the view and no extra code in your controller is to just create your own custom filter that handles your case.
Would this do the trick?
Say that your input is
<input ng-model="search.name" type="text">
Then, you can display results like this:
<div ng-repeat="person in people[0].names | filter: alias">...</div>
Controller:
$scope.alias = function (object) {
if (object.first.match(new RegExp($scope.search.name))) {
return true;
}
return false;
};

Resources