Multiple array grouping - ngRepeat - angularjs

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

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

How to iterate by time (in seconds) through an array with AngularJS

I have this JSON object that contains an array of devices types and each type has an array of brands:
{
"types": [
{
"type": "phone",
"brands": [“samsung”,”apple”,”LG”, … //a list of brands]
},
{"type": "PC",
"brands": [“DELL”,”apple”,”HP”, … //a list of brands]
},
…// a list of types
]
}
Using AngularJS ng-repeat I can iterate through every array , but I want to show the list of brands one by one in the same button , every value is shown for 2 seconds , in infinite loop, but I can’t figure out a way to do that.
you dont need ng-repeat for this. given that you need to display only one element at a time.
vm.display = {
count : 0,
current : null,
repeat : function() { $interval( vm.display.changeElement(), 2*1000) }
changeElement : function() { vm.display.current = list[vm.display.count] ; vm.display.count++; }
}
then just call vm.repeat() after getting data from server
As mentioned earlier, you can use $interval instead of ng-repeat to do this.
$interval(function(){
$scope.buttonLabel =$scope.data.types[0].brands[$scope.count%3];
$scope.count++;
},2000);
I hope this is what you need. :)
http://plnkr.co/edit/8VIJAN?p=preview

Angular merge nested arrays

I have a dataset that looks like this:
[
{
'title' : 'cats',
'names' : [
'felix',
'tom',
... more names
]
},
{
'title' : 'dogs',
'names' : [
'fido',
'rover',
... more names
]
},
{
... More animal types
]
And I would like to have the following:
<p ng-repeat='name in names'>{{ name }}</p>
But, to do that I really need to at some stage set
$scope.names = ['felix', 'tom', 'fido', rover'];
My question is: is there an 'Angular' way to merge arrays or take content from multiple places from one object? Or do I need to use a for loop with a concat function to create the array I use?
Sure, just defined names based on your data, demo.
$scope.names = function() {
return Array.prototype.concat.apply([], animals.map(function(animal) {
return animal.names;
}));
};
Then use that method in your view
<p ng-repeat='name in names()'>{{ name }}</p>
Or we could assume the list of animals won't change, and use a library like lodash for readability, demo.
$scope.names = _.chain(animals)
.pluck('names')
.flatten()
.value()
It is not likely to be a core functions for such feature in javascript nor in angular as angular is just mvc framework on javascript. function you need is not common enough for javascript, or specific enough for mvc

$q.all(promises)and structure of promises object to collect the returned data

I am using Angularjs $q.all(promises) to make multiple REST call and then collecting the data once promise is successful. I have following following.
If "promises is simple array then it works Plunker
var promises = [
Users.query().$promise,
Repositories.query().$promise
];
If "promises" is simple object then also it works Plunker
var promises = {
users: Users.query().$promise,
repos: Repositories.query().$promise
};
If "promises" is nested object then it is not working. For my requirement I need nested object to remember the input parameters. Plunker
var promises = {
users: {"phx":Users.query().$promise},
repos: {"phx":Repositories.query().$promise}
};
These plunkr are just to simulate my problem. However I want this approach in real project for following requirement.
I have list of 12 product
Each product has "details", "benefits" and "offers" data
I have separate REST API services for "details", "benefits" and "offers" having :productID as parameter
I am making call in following order
a. Loop for each cards
b. For each card, make a REST API call for "details", "benefits" and "offers"
c. Add #b steps into "promises" object
d. call
$q.all(promises).then(function(results) {
// Here need logic to compile the result back to product
// and corresponding "details", "benefits" and "offers" mapping
}
and get the data back
Following is json structure I needed to collect my response.
{
"prod1": {
"benefits": {},
"offers": {},
"pages": {
"productPage": {}
}
}
},
"common": {
"benefits": {},
"pages": {
"commonBenefit": {}
},
"others": {}
}
How can I achieve this?
If you really need it, you can wrap the nest with $q.all like this:
var promises = {
users: $q.all({"phx": Users.query().$promise}),
repos: $q.all({"phx": Repositories.query().$promise})
};
plnkr.co

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)

Resources