Using an OR conditional when using Angular filters - angularjs

I just started working with Angularjs and I'm facing the following problem:
I get this kind of object from an API request:
{
"recid": "1576",
"title": "19th Century UK Periodicals",
"description": "Bevat ruim 180 gescande tijdschriften die full text doorzoekbaar zijn",
"type": "tek",
"tags": "Engeland",
"primair": "b:bio",
"secundair": "h:eng,h:ges",
},
And I need to filter by selecting data that belongs to 'primair' OR 'secundair' field, how could I do that since chaining uses a sort of AND intead of Or.
regards in advance,
Eduardo

The easiest way would be to use a custom filter function. In your controller you would define it like this:
function YourController ($scope) {
$scope.filterFn = function (v) {
return (v.primair === 'somevalue') || (s.secundair === 'someothervalue');
};
}
In your template you can use it like this:
<li ng-repeat="item in yourlist | filter:filterFn">{{item.title}}</li>

Related

angularjs filter multidimensional object json

I wnt to use ng-repeat to display a list filtered by an object value. Here is a plukr of my attempt https://plnkr.co/edit/vD4UfzM4Qg7c0WGTeY18?p=preview
The following returns all of my JSON names as expected.
<li ng-repeat="item in collection_data">{{navitem.name}}</li>
now i want to filter and only show the names of the items that have "foreign_lang": "es", like in this json snippet
{
"id": "ddb06ba2-6348-4d45-9e63-a6fa3632e5c2",
"created_at": "2015-10-12T18:34:15.668Z",
"updated_at": "2016-04-14T15:55:37.433Z",
"custom_attributes": {
"Display Name": "Activos en Español",
"foreign_lang": "es",
"display_boxes": "false"
},
},
so i made this filter function
$scope.filterByDisplay = function() {
$filter('filter')($scope.collection_data, ['foreign_lang', 'es']);
}
and called it like this.
<li ng-repeat="item in collection_data" | filter: filterByDisplay>{{navitem.name}}</li>
I did not get any console errors but i got nothing returned.
How do I properly filter through this collection to only return items with 'foreign_lang', 'es' as a value in the json? See the plunkr to see a working example https://plnkr.co/edit/vD4UfzM4Qg7c0WGTeY18?p=preview
Third attempt (since the question was revised). Use the filter function to check each object individually, and returning only those that pass the truth test.
$scope.filterByDisplay = function(value) {
return (value.content)
&& (value.content.custom_attributes)
&& (value.content.custom_attributes.foreign_lang === "es");
}
Updated Plunk - Using Filter Function

Multiple array grouping - ngRepeat

I get my data like below;
[
{
// restaurant details here,
"restaurant_class": {},
"city": {},
"location": {},
"menu_categories": [],
"menu_items": [],
"menu_modifier_groups": [],
"menu_modifier_items": []
}
]
How can I use ng-repeat to group by menu_categories? menu-items is a child of menu_categories
Basically I want to display menu_items, menu_modifier_groups, menu_modifier_items grouping them by menu_categories
i think you can sort (generally manipulate) your array using a filter and then do the ng-repeat, and it won't necessarily change the original data. so inside the html you can do it like this: ng-repeat="item in myArray | myFilter: otherData".
and defining your filter in the js file like:
.filter('myFilter', function () {
return function(input, otherData) {
var output = [];
//some changes using "if"s and other things depending on your sorting algorithm
return output;
};
})
if you have problem with the sorting algorithm, it would help if you could give us the real data...
hope this helps

AngularJS custom directive within ng-repeat with dynamic attributes and two way binding

I'm banging my head on the wall over this for days and finally decided to post this question since I can't find an answer that matches what I'm trying to do.
Context: I'm building a dynamic form building platform that describes form elements in a JSON structure like this -
{
"name": "email",
"type": "email",
"text": "Your Email",
"model": "user.profile.email"
}
And then in the View I have a recursive ng-repeat that includes the field template like this -
<script type="text/ng-template" id="field.html">
<div ng-if="field.type === 'email'" class="{{field.class}}">
<p translate="{{field.text}}"></p>
<input type="{{field.type}}" name="{{field.name}}" class="form-control" dyn-model="{{field.model}}">
</div>
</script>
As you see, I use a custom directive dynModel to create the ng-model attribute with interpolated value of the model from the string value. So far do good.
Now I have a more complex scenario in which I have a collection of fields that can be added or removed by clicking on Add button or removeMe button. See below -
{
"name": "urls",
"type": "collection",
"text": "Your Profile URLs",
"model": "user.profile.urls",
"items": [
{
"name": "url",
"type": "url",
"text": "Facebook URL",
"model": "url"
},
{
"name": "url",
"type": "url",
"text": "Facebook URL",
"model": "url"
}
],
"action_button": {
"name": "add",
"type": "action",
"action": "addURL"
}
}
<div ng-if="field.type === 'collection'">
<button class="btn btn-info" dyn-click click-action="{{field.action_button.action}}" click-model="{{field.model}}">{{field.action_button.text}}</button>
<div dyn-ng-repeat="item in {{field.model}}" >
<div ng-repeat="field in field.items" ng-include src="'field.html'"></div>
</div>
</div>
As you'll notice, I have another custom directive that takes care of interpolation of {{field.model}} from the previous ng-repeat (not shown).
Now to the crux of the issue. As you see in the template, I have nested ng-repeats, the first one iterates through user.profile.urls and the second one iterates through the field parameters in JSON and creates the HTML tags, etc. One of those fields is a button (action_button) that is used to add more URLS to the list. When I click the button, I want it to trigger a function in my controller and effectively add a new child to the parent model (user.profile.urls). I then also want each URL, existing and new to have a remove button next to them that will be dynamic and will remove that particular item from the model.
If you see the code above, I have a custom directive dyn-click that reads in the
click-action="{{field.action_button.action}}"
That contains the function name (addURL) to be called that resides in my controller and the model
click-model="{{field.model}}"
(user.profile.urls) to which the new item is to be added. This is not working. The reason for this complexity is that I have multiple levels of nesting and at each level there are dynamic elements that need to be interpolated and bound. The directive dyn-click looks like this right now -
exports = module.exports = function (ngModule) {
ngModule.directive("dynClick",function() {
return {
restrict: 'A',
link: function(scope,element,attrs) {
$(element).click(function(e, rowid){
scope.clickAction(scope.clickModel, scope.$index);
});
}
};
});
};
With this code, when I click on the rendered form's Add button, the code in the $(element).click method above gets executed giving the following error -
Uncaught TypeError: undefined is not a function
I have tried a few different things with scope:{} in the dyn-click directive, with different errors and none of them have worked completely with two way binding of the model and calling the function as expected.
Help!
EDIT-1 - please see the comments:
$(element).click(function(e, rowid){
scope.$eval(attrs["clickAction"])(scope.$eval(attrs["clickModel"]), scope.$index);
});
EDIT-2: The plunker is here - http://plnkr.co/edit/DoacjRnO61g4IYodPwWu?p=preview. Still tweaking it to get it right, but you guys should be able to see the necessary pieces. Thanks!
EDIT-3: Thanks Sebastian. The new plunker is here - http://plnkr.co/edit/Z6ViT7scubMxa17SFgtx?p=preview . The issue with the field.items ng-repeat still exists. For some reason the inner ng-repeat is not being executed. Any ideas? Josep, Sebastian?

Wrap items in backbone collection?

I keep running into some confusing solutions and unclear ways to wrap items that match into a div using backbone.
I am just building a simple example for myself, and would like to nest all models in a collection that have the same attribute team, using a comparator works well in organizing the list, but for the life of me I can't find a clear solution to wrapping each so that I have more control over the list of players inside the team.
There has to be a clear easy solution for a beginner like me. I really just want to keep things as clean and simple as possible. My desired html result looks like below.
<div class="pacers">
<li>Paul</li>
<li>Roy</li>
</div>
<div class="bulls">
<li>Kirk</li>
<li>Taj</li>
</div>
Based on a backbone friendly json array like below.
[
{
"name": "Paul",
"team": "pacers"
},
{
"name": "Kirk",
"team": "bulls"
},
{
"firstname": "George",
"team": "pacers"
},
{
"name": "Taj",
"team": "bulls"
}
]
So using a comparator is awesome I just write this comparator : 'team' and it handles the list order for me, cool, but I dont have much control I would like to wrap the list in a more hierarchical system.
Another approach:
If you are using underscore's templates this could be one way of doing it. You can use underscore's groupBy function to group the list based on teams.
var teams = [
{
"name": "Paul",
"team": "pacers"
},
{
"name": "Kirk",
"team": "bulls"
},
{
"firstname": "George",
"team": "pacers"
},
{
"name": "Taj",
"team": "bulls"
}
];
var groupedList = _.groupBy(list, function(l){
return l.team;
});
console.log(JSON.stringify(groupedList));
This is how it would be grouped.
{
"pacers": [
{
"name": "Paul",
"team": "pacers"
},
{
"firstname": "George",
"team": "pacers"
}
],
"bulls": [
{
"name": "Kirk",
"team": "bulls"
},
{
"name": "Taj",
"team": "bulls"
}
]
}
You can then use for each loop and in template and generate HTML in following way. The groupedList is passed as teams to below template.
<%
_.each(teams, function(team, teamName){
%>
<div class="<%=teamName%>">
<%
_.each(team, function(player){
%>
<li><%=player.name%></li>
<%
});
%>
</div>
<%
});
%>
This would generate the HTML the way you expected.
NOTE:
The code snippets are given considering underscore templating, you might have to make changes based on what you use. Hope it helps.
Correct me if I am wrong the problem being described relates more to controlling the contents of each item in relation to it's model as well as how to simply render them in groups.
1) Niranjan has covered grouping out the data into separate lists but remember that this list returned is not a Backbone construct.
2) As per the manual the '_.groupBy' method should be available to you via the collection i.e.:
myCollection.groupBy(etc);
3) I would personally consider mapping the results of the groupBy back into models and pass each and every model into a separate view and render them from within the main list view.
var CollectionView = Backbone.View.extend({
initialize : function () {
// Note: I am pretending that you have a real collection.
this.collection.fetch().then(
this.addAll(true);
);
}
addOne : function (model) {
// call .render individual template items here for each model.
var view = new ItemView(model);
this.$el.append(view.render();
},
addAll : function (groupOpts) {
var col = this.collection;
if(groupOpts === true) {
// Do grouping (or do it in the model). Maybe put back into new collection?
}
_.each(col, function(model) {
this.addOne(model);
}, this);
},
render : function () {
// Render your template here.
}
});
var ItemView = Backbone.View.extend({
render : function () {
}
});
Not a complete example but that's the general pattern I would follow when attempting the same thing. Having an individual view/model for each item, in my opinion, gives you more control.
This could be handled in a pretty crazy view template (depends on your template language)... or you could use a simpler template/view and just make some more crazy collection queries (first using a pluck to get the team, de-dupping that array, then running some where's for each of the teams... but you can see how this gets crazy)
I'd vote for the view and view template should handle this... what are you using? Jade? Mustache?
Something like this - logical psuedo code here since I don't know your template language:
var team;
forEach player in players
if(!team) {
set team = player.team
print open holder and then the first row
} (team !== player.team {
set team = player.team
print close of previous holder, then open holder and then the first row of new team
} else {
print just the player row
}
Even so, you can see how this is a bit dirty in and of itself... but what you are describing is a view/presentation concern, and you can do it here with no new additional loops and maps and wheres (like you'd have to do if you did it in the data layer before calling the views)

Angular ng-repeat filter with predicate function not works as expected

This is my first time playing with AngularJS, and actually, I'm following the getting-started tutorial. It came to my mind that I would tweak the tutorial scripts to my understandings, by just adding a little that was not in the tutorial.
Basically, the phone object used in the tutorial was:
{
"age": 1,
"id": "motorola-xoom",
"imageUrl": "img/phones/motorola-xoom.0.jpg",
"name": "MOTOROLA XOOM™",
"snippet": "The Next, Next Generation..."
}
What I was trying to do was to add an auto populated select box for order the list:
<select ng-model="orderProp">
<option ng-repeat="(key, value) in phones[0]" value="{{key}}">
{{labels[key]}}
</option>
</select>
and added a model labels to the controller:
$scope.labels = {
"name": "Phone name",
"snippet": "Description",
"age": "Newest",
};
It was working as expected, except that I only wanted to filter the 3 properties above, so I think it would be easy to add a custom predicated function for filtering like this:
$scope.isPhonePropFilterable = function (propName) {
console.log('it DOES NOT get here!!!');
return propName == 'name' || propName != 'snippet' || propName != 'age';
};
and added this to the ng-repeat
<option ng-repeat="(key, value) in phones[0] | filter:isPhonePropFilterable" value="{{key}}">
To my suprise, it was not as easy as I thought, my filter function was not called.
See it here: plunker
Did I do anything wrong?
edited: ng-repeat filter supports filtering array only, not object. The filter function returnes if array param is not an array...
Well, it was my fault. Ng-repeat filter supports array only, it did only mention array in the docs. And checking the filter function, it returned if array param is not an array....

Resources