How to create new bootstrap row per two items on Angular? - javascript

I'd like to add row on each 2 elements of ngFor loop.But I couldnt handle it.
I have studentNames array like below
studentNames=[
{
name:"Jonas",
age:22,
number:"1234"
},
{
name:"Mathilda",
age:25,
number:"5432"
},
{
name:"Jacob",
age:20,
number:"4321"
},
{
name:"Ivan",
age:23,
number:"6984"
},
{
name:"Kate",
age:21,
number:"3432"
},
{
name:"James",
age:20,
number:"4312"
},
{
name:"Sean",
age:23,
number:"4321"
},
{
name:"Julia",
age:23,
number:"4321"
},
]
Here what I tried
<ng-container *ngFor="let item of studentNames;let i=index">
<div class="row" *ngIf="i%2==0">
<div class="col md-12">
{{item.name}}
</div>
</div>
</ng-container>
This only skipped when index is not even.
Here how I want to see them below(order doesnt matter only should be 2 by 2 per row).
Stackblitz example: https://stackblitz.com/edit/bootstrap-wpfukz?file=app%2Fapp.component.html

This is a bit more "hacky" approach but it's HTML-only:
<ng-container *ngFor="let item of studentNames;let i=index">
<div *ngIf="i%2==0">
<div class="row">
<div class="col md-12">
{{studentNames[i].name}}
</div>
<div class="col md-12">
{{studentNames[i + 1].name}}
</div>
</div>
</div>
</ng-container>

You can try like below. Use *ngFor exported value index and use half length of the array to continue the loop
<ng-container *ngFor="let item of studentNames; let j = index;">
<ng-container *ngIf="j < (studentNames.length / 2)">
<div class="row">
<div class="col md-6">
{{item.name}}
</div>
<div class="col md-6">
{{ studentNames[ j + (studentNames.length / 2) ].name }}
</div>
</div>
</ng-container>
</ng-container>
Working stackblitz
Note : Array length should be an even number ;)

Related

Using slice in a multiple loop ngFor angular

Need to slice the inner ngfor loop into 3 parts
<div class="row" *ngFor="let row of matrix; index as i">
<div class="col" *ngFor="let col of row; index as j">
<div *ngFor="let placeholder of placeholders | slice:i:j">
<ng-container [ngComponentOutlet]="placeholder.component" >
</ng-container>
</div>
</div>
</div>
There are a couple of ways around this:
$any
<p *ngFor="let item of data | slice:2:4">
{{ $any(item).parentName }}
</p>
Bracket notation
<p *ngFor="let item of data | slice:2:4">
{{ item['parentName'] }}
</p>
A function
slicedData(data : any[]) : any[] {
return data.slice(2,4)
}
<p *ngFor="let item of slicedData(data)">
{{ item['parentName'] }}
</p>

Display categories that only contain images

I developed a gallery of images separated by several categories.
I just want to display categories that contain images.
I have three categories: "new", "old", "try".
Of these three categories, only new and old have images. My problem is that all categories are appearing, even those that have no image (as is the case with try).
Is there a way to present only the categories that contain images?
How can I do this?
DEMO
code
<div *ngFor="let cat of Cats">
<div class="row ">
<span class="">{{cat}}</span>
</div>
<ul class="mdc-image-list my-image-list" style="padding-left: 10px;padding-right: 10px;">
<ng-container *ngFor="let product of data; let j = index;">
<li class="mdc-image-list__item" *ngIf="product.Cat == cat">
<div class="mdc-image-list__image-aspect-container">
<ng-container *ngIf="product.image == null; else productImage">
<img src="./assets/image-not-found.svg" class="mdc-image-list__image imagenotfound">
</ng-container>
<ng-template #productImage>
<img [src]="product.image" class="mdc-image-list__image">
</ng-template>
</div>
</li>
</ng-container>
</ul>
</div>
The try should not be presented :(
You can wrap your category div in an <ng-container>, and then use *ngIf to check if your data array contains a product in a given category. The best way to achieve that is to have an array with product counts per given category.
Add this to your AppComponent class:
get counts() {
return this.data.reduce((obj, value) => {
if (value.Cat in obj) {
obj[value.Cat]++;
} else {
obj[value.Cat] = 1;
}
return obj;
}, {});
}
And then use this as your template:
<ng-container *ngFor="let cat of Cats">
<div *ngIf="counts[cat]">
<div class="row ">
<span class="">{{cat}}</span>
</div>
<ul class="mdc-image-list my-image-list" style="padding-left: 10px;padding-right: 10px;">
<ng-container *ngFor="let product of data; let j = index;">
<li class="mdc-image-list__item" *ngIf="product.Cat == cat">
<div class="mdc-image-list__image-aspect-container">
<ng-container *ngIf="product.image == null; else productImage">
<img src="./assets/image-not-found.svg" class="mdc-image-list__image imagenotfound">
</ng-container>
<ng-template #productImage>
<img [src]="product.image" class="mdc-image-list__image">
</ng-template>
</div>
</li>
</ng-container>
</ul>
</div>
</ng-container>
You can write format function, write data as an object where the key is category, value is array of images and use keyvalue pipe.
For example:
component property: formattedData: {[key: string]: string[]} = {}
format function:
formatData(data: {image: string; cat: string}[]): void {
data.forEach((item: {image: string; cat: string}) => {
if (this.formattedData[item.cat]) {
this.formattedData[item.cat].push(item.image)
} else {
this.formattedData[item.cat] = [item.image]
}
});
}
call the format function in constructor:
constructor() {
this.formatData(this.data);
}
template:
<div *ngFor="let item of formattedData | keyvalue">
<div class="row">
<span class="">{{item.key}}</span>
</div>
<ul class="mdc-image-list my-image-list" style="padding-left: 10px;padding-right: 10px;">
<ng-container *ngFor="let image of item.value;">
<li class="mdc-image-list__item">
<div class="mdc-image-list__image-aspect-container">
<img [src]="image ? image : './assets/image-not-found.svg'" class="mdc-image-list__image" [ngClass]="{'imagenotfound': !image}">
</div>
</li>
</ng-container>
</ul>
</div>
I think it looks better because we have a little less logic in the template. You have only listed categories that have images.

How to populate json object in UI Angular 2

I'm using Angular 2 in my project,and I have this json object:
[
{
"city": "toto"
},
{
"city": "titi"
},
{
"city": "tata"
},
...
]
What I want is, to populate this object in the UI like this:
toto -> titi (with a button here)
titi -> tata (with a button here)
I tried this but it doesn't work:
<div formArrayName="prices">
<div *ngFor="let myGroup of myForm.controls.prices.controls; let i=index">
<div [formGroupName]="i">
<span *ngIf="myForm.controls.prices.controls.length > 1" >
</span>
<div class="input-group spinner">
<input type="text" formControlName="price" class="form-control" >
</div>
</div>
Any suggestions please?
Something like this?
<div *ngFor="let item of items; let i=index">
<span>{{item.city}}</span> --> <span *ngIf="items[i+1]">{{items[i+1].city}}</span>
</div>
outputs:
toto --> titi
titi --> tata
tata -->
If you want to not show the last item use let last = last and filter out the last item.
<div *ngFor="let item of items; let i=index; let last = last">
<div *ngIf="!last">
<span>{{item.city}}</span> --> <span *ngIf="items[i+1]">{{items[i+1].city}}</span>
</div>
</div>

Auto Increment in ng-repeat object

I am taking game info from mlb.com and displaying them using angularjs and ng-repeat directive. an example of the JSON feed is below.
{
"data": {
"games": {
"next_day_date": "2017-08-19",
"modified_date": "2017-08-18T16:57:16Z",
"month": "08",
"year": "2017",
"game": {
"0": [{
"away_team_name": "CUBS"
}, {
"home_team_name": "INDIANS"
}],
"1": [{
"away_team_name": "CUBS"
}, {
"home_team_name": "INDIANS"
}]
},
"day": "18"
}
}
I am only able to display the data properly however, only 1 game. html below
<div class="card mb-3" ng-repeat="x in scoreboard" ng-if="$index > 1">
<div class="card-header" align="center">
{{x.games.game[0].away_team_name}} ({{x.games.game[0].away_win}}-{{x.games.game[0].away_loss}}) At {{x.games.game[0].home_team_name}} ({{x.games.game[0].home_win}}-{{x.games.game[0].home_loss}})<br>
<small>{{x.games.game[0].time}}</small>
</div>
<div class="card-block"></div>
</div>
I do understand [0] refers to the game ID '0' in the json feed, however is there a way to auto increment that number in {{x.games.game[0].time}} to loop through all games instead of doing each game individually like below?
<div class="card mb-3" ng-repeat="x in scoreboard" ng-if="$index > 1">
<div class="card-header" align="center">
{{x.games.game[0].away_team_name}} ({{x.games.game[0].away_win}}-{{x.games.game[0].away_loss}}) At {{x.games.game[0].home_team_name}} ({{x.games.game[0].home_win}}-{{x.games.game[0].home_loss}})<br>
<small>{{x.games.game[0].time}}</small>
</div>
<div class="card-block"></div>
</div>
<div class="card mb-3" ng-repeat="x in scoreboard" ng-if="$index > 1">
<div class="card-header" align="center">
{{x.games.game[1].away_team_name}} ({{x.games.game[1].away_win}}-{{x.games.game[1].away_loss}}) At {{x.games.game[1].home_team_name}} ({{x.games.game[1].home_win}}-{{x.games.game[1].home_loss}})<br>
<small>{{x.games.game[1].time}}</small>
</div>
<div class="card-block"></div>
</div>
I have tried
{{x.games.game[$index + 1].time}} but still return only 1 game
Any insight would be appreciated!
I adjusted my $scope in the controller from
.success(function (data) { $scope.scoreboard = data;} to
.success(function (data) {$scope.scoreboard = response.data.games.game;}
html now looks like
<div class="card mb-3" ng-repeat="x in scoreboard track by $index" ng-if="$index > 1">
<div class="card-header" align="center">{{x.away_team_name}} </div>
</div>
works perfectly
There are a couple of problems:
An iteration over the wrong object, the iteration should be made over scoreboard.games.game instead of scoreboard.
The syntax x in list should only be used in the ng-repeat to iterate over arrays, both scoreboard and scoreboard.games.game are NOT arrays, they're Object literals
The syntax to iterate over Object literals in ng-repeat is (key, value) in object as mentioned in the documentation.
To properly solve this problem we must iterate over the right object with the right syntax, here is how:
<div class="card mb-3" ng-if="key > 1"
ng-repeat="(key, game) in scoreboard.games.game">
<div class="card-header" align="center">
{{ game.away_team_name }}
({{ game.away_win }}-{{ game.away_loss }}) At
{{ game.home_team_name }}
({{ game.home_win }}-{{ game.home_loss }})<br />
<small>{{ game.time }}</small>
</div>
<div class="card-block"></div>
</div>

multidimensional array in angular

I have a multidimensional array from an API. Is it possible to programatically loop through the array?
{
success: true,
categories: [{
cat_id: "2",
name: "This is category One",
description: null,
photo_url: "/img/test.png",
order: "1",
items: [{
item_id: "1",
title: "Desk",
item_url: "/690928460",
photo_url: "/desk.png",
}, {
item_id: "2",
title: "Chair",
item_url: "/18882823",
photo_url: "/chair.png",
},
}]
}]
}
My controller looks like this:
myApp.controller('items', function($scope, $http, $location, Data) {
var apiUrl = '/api/items';
$http.get(apiUrl).
success(function(data) {
$scope.data = Data;
$scope.allData = data;
});
$scope.changeView = function(view) {
$location.path(view);
}
});
Angular index file just has: <div ng-view=""></div>
View file
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="row" ng-repeat="categories in allData">
<div class="col-xs-6" ng-repeat="category in categories">
<div class="items">
<div class="title">
{{ category.name }}
</div>
</div>
</div>
</div>
</div>
</div>
I can loop through the category names fine, but when trying to return items for EACH category I don't understand the logic behind it...
I would suggest some simple nested for loops, as for each gives rise to more complexity.
As I'm not sure what you want to do with the data let's just create an array of all item names and one of all category names:
Within your success function:
var items = [], categories = []
for(var i = 0; i < data.categories.length;i++){
categories.push(data.categories[i].name);
for(var j = 0; j < data.categories[i].items.length;j++){
items.push(data.categories[i].items[j].name);
}
}
console.log(categories);
console.log(items);
EDIT:
Completely missed your html code somehow, here is my solution:
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="col-xs-6" ng-repeat="category in allData.categories">
<div class="items">
<div class="title">
{{ category.name }}
</div>
</div>
</div>
</div>
</div>
EDIT 2:
As to your comment:
If you want to select the secondary view's contents(ie the items) based on the selection of a category I would suggest a ng-click attribute. A directive could be used but isn't necessary:
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="col-xs-6" ng-repeat="category in allData.categories">
<div class="title" ng_click="selected_category = category">
{{ category.name }}
</div>
</div>
<div class="col-xs-6" ng-repeat="item in selected_category.items">
<div class="title">
{{ item.name }}
</div>
</div>
</div>
</div>
So when your categories data is loaded the first ng-repeat is populated with the categories. Each div with class title will have a function called on click which will make the selected_category object equal the selected category.
This will then cause the second view to be populated with all the items in the selected category by Angular's two way bind.

Categories

Resources