Ng-repeat inside ng-repeat - angularjs

<ul ng-repeat="cate in restaurant.categories"><li>{{cate}}</li>
<li ng-repeat="menuItem in restaurant.menuItems" ng-show="menuItem.category == cate">{{menuItem.name}}</li></ul>
I want one ng-repeat loop inside another and to show the menu only if the menuItem is in the category. I only have items in the first category loop, and empty for all the other categories.
Categories and menuItem are 2 different arrays. If the menuItem's category is under the current category it should be added to the page.
menuItems = {{name: dish1, category:soup},
{name: dish2, category:beef}}
categories = {beef, soup}

#show-me-the-code : Bill Bi has two different array. So the best option to achieve this is by filter in inside loop as stated in my comment.
Here is the final code with filter for inside loop. I am including fiddler for quick reference.
<div ng-app ng-controller="testCtrl">
<ul ng-repeat="cate in categories">
<li>{{cate}}</li>
<li ng-repeat="menuItem in menuItems | filter:{category: cate}">{{menuItem.name}}</li>
</ul>
</div>
function testCtrl($scope) {
$scope.menuItems = [{name: 'dish1', category:'soup'},
{name: 'dish2', category:'beef'}];
$scope.categories = ['beef', 'soup']
}
Fiddle : JSFiddle

I would change my data representation to match what you are actually trying to display like so:
$scope.restaurant = {
categories: [{
name: "beef",
menuItems: [{
name: "dish1",
"price": "$10"
}, {
name: "dish2",
"price": "$15"
}]
}, {
name: "soup",
menuItems: [{
name: "dish1",
"price": "$20"
}, {
name: "dish2",
"price": "$25"
}]
}]
};
This way you could easily match your two nested loops like this:
<div ng-app ng-controller="testCtrl">
<ul ng-repeat="cate in restaurant.categories">
<li>{{cate.name}}</li>
<li ng-repeat="menuItem in cate.menuItems">{{menuItem.name}} - {{menuItem.price}}</li>
</ul>
</div>
Check out this fiddle if you would like to see it in action.
If you need to stick to your JSON data, you will have to do filtering to pull the contents you want to display.

Related

How to apply CSS active class to an item of three independent lists?

How to apply a CSS active class to one item of three lists?
I get a list of objects from server. These objects has a property of type list. Here is a similar object.
[
{
name: 'europe',
countries: ["Englad", "France", "Germany", "Italy"]
}, {
name: 'asia',
countries: ["Japan", "China", "Iran", "Korea"]
}, {
name: 'Africa',
countries: ["Somalia", "Egypt", "Ghana"]
},
];
By default, the first item of the lists is selected. When the user clicks an another item, it should be selected without affecting the items of other lists.
Some information that might help.
The object is above is just an example. In future, I may get a list of more than three objects.
There is only one controller.
Each one of the countries has an 'id', but I have not included.
Here is an exmpale of this object on jsfiddle JSFiddle
Thank you so much.
Basically you would create an object to hold the selection, as you mentionned your data is variable the selection object generation should be dynamic. To do so I've taken advantage of the ECMAScript 2015 objects computed property names, there is more info on MDN for Object Initializer.
Having a default selection requires initialization as well.
Based on your fiddle, here's the html
<div ng-app ng-controller="ContinentController">
<ul>
<li ng-repeat="continent in continents">
<h2>{{continent.name}}</h2>
<ul>
<li ng-repeat="country in continent.countries">
<span ng-class="{active: selected[continent.name] === $index}"
ng-click="select(continent.name, $index)">{{country}}</span>
</li>
</ul>
</li>
</ul>
</div>
And the controller
function ContinentController($scope) {
$scope.continents = [{
name: 'europe',
countries: ["Englad", "France", "Germany", "Italy"]
}, {
name: 'asia',
countries: ["Japan", "China", "Iran", "Korea"]
}, {
name: 'Africa',
countries: ["Somalia", "Egypt", "Ghana"]
}, ];
$scope.selected = {};
$scope.select = select;
initializeSelection();
function initializeSelection() {
for (var i = 0; i < $scope.continents.length; i++) {
$scope.selected[$scope.continents[i].name] = 0;
}
}
function select(name, index) {
$scope.selected[name] = index;
}
}
Here's the working fiddle.
Now this assumes all you names are unique. Since you mentionned you have ids, if they are unique then using them as key instead of the name property would definitely be better.

How to display an array of objects inside the html template in Angular 2?

I am building web app that will have a list of 3d models. I’m doing it for a practice purposes, in order to gain some initial knowledge of angular 2.
Each listed model, will have name, main picture, category, and a slider (array of images).
Model data array is:
export var MODELS: Model[] = [{
id: 1,
name: 'Model 1',
image: 'app/img/models/model-1.jpg',
category: 'Cat 1',
slides: [
{ alt: 'Model 1 front view' , url: 'app/img/models/model-2.jpg' },
{ alt: 'Model 1 rear view' , url: 'app/img/models/model-2.jpg' },
{ alt: 'Model 1 side view' , url: 'app/img/models/model-2.jpg' }
]
},
{
id: 2,
name: 'Model 2',
image: 'app/img/models/model-2.jpg',
category: 'Cat 2',
slides: [
{ alt: 'Model 2 front view' , url: 'app/img/models/model-2.jpg' },
{ alt: 'Model 2 rear view' , url: 'app/img/models/model-2.jpg' },
{ alt: 'Model 2 side view' , url: 'app/img/models/model-2.jpg' }
]
}
];
Service that is used for proper displaying of certain model is:
#Injectable()
export class ModelService {
getModels(): Promise<Model[]> {
return Promise.resolve(MODELS);
}
getModel(id: number): Promise<Model> {
return this.getModels()
.then(models => models.find(model => model.id === id));
}
}
Model detail’s page template is:
<div *ngIf="model">
<h2>{{model.name}} details!</h2>
<img [src]="model.image" alt="{{model.name}}"/>
<div>{{model.slides}}</div>
</div>
These are Mostly stuff that I used from: https://angular.io/docs/ts/latest/tutorial/
However, when I try to use simplest way to display slides array into model-detail page, it shows [object Object]. I read that this is might be a common issue in angular 2, that can be resolved by using custom pipes. Unfortunately, I have no clue how to write one for this certain case.
General question will be: How to properly display array of objects (slides) on html template, and wrap them separately into img tag.
Here is plunker example link: http://plnkr.co/edit/HVo3dtGprMHsPYeyRWBR?p=preview
Thanks in advance,
That's because your slides property is in fact an Object, it's an array so displaying the property alone will simply display 'object'. What you need to do is iterate over the slides property and separately create img divs:
<img *ngFor="let slide of model.slides" [alt]="slide.alt" [src]="slide.url" />
this is what your updated template would look like from your plunkr:
<ul class="models">
<li *ngFor="let model of models" (click)="gotoDetail(model)">
<img *ngFor="let slide of model.slides" [src]="slide.url"/>
{{model.name}},{{model.category}}
</li>
</ul>
You could use *ngFor in an img tag I have included some of my practice code to show you what I mean.
<div class="col-md-6" *ngFor="let picture of imageUrl; let i = index; trackBy: trackByFn">
<div class="portfolio-item"
style="padding-bottom: 10px;">
<a (click)="galleryOpen = false;componentIndex = i;" data-toggle="dropdown" href="#" name="gal2">
<img class="img-portfolio img-responsive" src={{picture}} height="350" width="455"
style="height: 350px; max-width: 455px;">
</a>
</div>
</div>
The *ngFor in this case could just a easily be placed in the img tag. I hope this helps you out.

Two-tier sorting for ng-repeat

For example, I have the following collection used for ng-repeat:
$scope.pokedex = [{
type: "Fire",
pokemon: ["Charizard", "Moltres"]
},{
type: "Rock",
pokemon: []
},{
type: "Fighting",
pokemon: ["Machamp", "Hitmonchan"]
},{
type: "Dragon",
pokemon: []
}];
This collection will be churned out in a ng-repeat directive. In the actual application, the collection will be retrieved from a database, so it may be unsorted. I want to sort it in the following manner: priority sort types with Pokemon to the top, then sort each group by name.
Edit: I need to clarify what the backend data consists of. In the above example, $scope.pokedex consists of a constant number of types - these are categories. The application retrieves Pokemon from the database and fill up each category's list accordingly. The full range of types is intentionally hardcoded into the array and will remain unchanged regardless of whether the list of Pokemon in it is empty or not.
When the web page is generated using ng-repeat, the desired end state is as follows:
An accordion is displayed with each type as a header, and the list of Pokemon in the body as a list/table.
All empty categories shall be disabled but still visible, their headers given a particular CSS format, and all of them PUSHED TO THE BOTTOM beneath the non-empty categories.
The empty group and non-empty group shall individually be sorted by category/type name.
Everything except the pushing of empty groups to the bottom and the sorting by name have been implemented. These are my final requirements to implement.
How can I do that? Is there a way to do it in advance, or via orderBy during ng-repeat, or any other workable solution?
You Can Try like this This
<ul>
<li ng-repeat="people in peoples | orderObjectBy:'pokemon':true">{{ people.name }}</li>
</ul>
Please try the code below
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="pokemonController">
<ul>
<li ng-repeat="people in peoples | orderBy:['-pokemon.length','name']">
name: {{people.name}} - number of Pokemons: {{people.pokemon.length}}
<hr />
</li>
</ul>
</div>
</div>
<script>
var app = angular.module('myApp',[]);
app.controller('pokemonController', ['$scope', function($scope){
$scope.peoples = [{
name: "Obama",
pokemon: ["Charizard", "Dragonite", "Moltres"]
},{
name: "Merkel",
pokemon: []
},{
name: "Putin",
pokemon: ["Machamp", "Hitmonchan"]
},{
name: "Kim",
pokemon: []
}];
}]);
</script>

ngOptions ordering and selecting objects in array

I have a set of options in my controller that looks like this:
$scope.options = [
{one: 'ONE'},
{two: 'TWO'},
{three: 'THREE'}
];
My view looks like the following currently looks like this:
<div ng-repeat="goal in objectives">
...
<select ng-model="goal.choices" ng-options="value for (key, value) in options"> </select>
...
</div>
PROBLEM: the resulting dropdown is not sorted by obj occurence in array rather by alpha of each objects key AND there is no default option selected, i want the dropdown to default to 'one' not ''
What is the ng-options expression need to make this work?????
Thanks
Your $scope.options array isn't usable in ngOptions because you have an array of three entirely different objects (one has a one property, another a two property, and the last a three property). If you want the select to default to $scope.choices[0], then goal.choices needs to be set to $scope.options[0].
I had to make some guesses here, because you didn't include what $scope.objectives was, but I can imagine you were going for something like this:
http://jsfiddle.net/A5KkM/
HTML
<div ng-app ng-controller="x">
<div ng-repeat="goal in objectives">{{goal.choice}}
<select ng-model="goal.choice" ng-options="o.name for o in options"></select>
</div>
</div>
JavaScript
function x($scope) {
$scope.options = [{
name: 'ONE'
}, {
name: 'TWO'
}, {
name: 'THREE'
}];
$scope.objectives = [{ choice: $scope.options[0] }, { choice: $scope.options[1] }];
}

AngularJS filter on empty string

I have a simple Angular filter setup between a select and a list. When a user selects an item from the dropdown, the list is updated to show the matching result. The problem is that I have a "Select..." option first in the dropdown with no value, and when that is the selected value, all items are shown. I suppose that makes sense, but I want the opposite. If the selected option has no value, I don't want to show the list. Here are some relevant bits, followed by a link to a full fiddle:
The dropdown:
<select class="world-list" ng-model="selectedWorld" ng-options="world.worldId as world.worldName for world in allWorlds">
<option value="">Select...</option>
</select>
The list:
<ul class="unstyled" id="charList">
<li ng-repeat="char in characters | filter:selectedWorld">
{{char.charName}} - {{char.charRace}} {{char.charClass}}
</li>
</ul>
And here is a link to the full fiddle, which contains my JSON structures that drives all this: http://embed.plnkr.co/6XUmC5efO0Y1BRNLRUig
What I'm trying to do is rather simple, and I'm pretty new to Angular so I'm sure I'm missing something obvious. Checked the Jabbr room, nobody on. :(
Anyway, thanks for any and all help!
One way to accomplish this is to filter by specific properties of the iteration object:
<ul class="unstyled" id="charList">
<li ng-repeat="char in characters | filter:{worldId: selectedWorld}">
{{char.charName}} - {{char.charRace}} {{char.charClass}}
</li>
</ul>
Plunker
I noticed in your code "script.js" you don't seem to be accounting for the blank option.
I did not test this (I probably should prior to posing this answer), but it should work by placing the "blank" option within your script.js.
app.controller('WorldCtrl', function ($scope, $http) {
$scope.allWorlds = [{
worldId: "",
worldName: ""
},
{
worldId: "b8ee0530-744b-463e-9428-23178f6c7bff",
worldName: "World 1"
},
{
worldId: "81211982-5613-4f9c-b704-7b6fa35faf84",
worldName: "World 2"
},
{
worldId: "df41208e-e8d2-46c9-8299-8f37632a51f8",
worldName: "World 3"
}];
$scope.characters = [{
charClass: "Rogue",
charName: "Vaes",
charRace: "Human",
worldId: "b8ee0530-744b-463e-9428-23178f6c7bff"
},
{
charClass: "Warrior",
charName: "Azash",
charRace: "Orc",
worldId: "b8ee0530-744b-463e-9428-23178f6c7bff"
},
{
charClass: "Mage",
charName: "Anele",
charRace: "Ogre",
worldId: "81211982-5613-4f9c-b704-7b6fa35faf84"
}];
});

Resources