NgFor looping through array inside of object - javascript

I am pulling data from an API, and trying to render that data on the page.
JSON structure:
{page: 1, results: Array(20), total_pages: 832, total_results: 16629}
page: 1
results: Array(20)
0:
adult: false
backdrop_path: "/xDMIl84Qo5Tsu62c9DGWhmPI67A.jpg"
genre_ids: (3) [28, 12, 878]
id: 505642
original_language: "en"
original_title: "Black Panther: Wakanda Forever"
overview: "Queen Ramonda, Shuri, M’Baku, Okoye and the Dora Milaje fight to...
popularity: 7141.639
poster_path: "/sv1xJUazXeYqALzczSZ3O6nkH75.jpg"
release_date: "2022-11-09"
title: "Black Panther: Wakanda Forever"
video: false
vote_average: 7.5
vote_count: 2879
1
2
3
4
5
6
Component
import { Component } from '#angular/core';
import { tap } from 'rxjs';
import { MovieDataService } from '../services/movie-data.service';
#Component({
selector: 'home-card',
templateUrl: './home-card.component.html',
styleUrls: ['./home-card.component.css']
})
export class HomeCardComponent {
movieData: any = {};
constructor(private movieDataService: MovieDataService) {}
ngOnInit(): void {
this.movieDataService.getData().subscribe((data) => {
this.movieData = data;
// JSON to console
console.warn(data);
})
}
}
Template
<ul>
<li *ngFor="let item of movieData.results | keyvalue">
Key: <b>{{item}}</b>
</li>
</ul>
This successfully renders on the page, and renders the object like so:
But when trying to access the title field of an object i get the error: Property title does not exist on type KeyValue<unknown, unknown>.
This is how I'm trying to access the title field:
<ul>
<li *ngFor="let item of movieData.results | keyvalue">
Key: <b>{{item.title}}</b>
</li>
</ul>
I'm sure that I am missing something very obvious, but I have done a lot of reading/research and haven't been able to solve it.

keyvalue pipe works only for objects, you just need to do something like this
<ul>
<li *ngFor="let item of movieData.results">
Key: <b>{{item.title}}</b>
</li>
</ul>

Related

How to remove the item which has ```CLOSED``` in ngFor in angular/

TS
#Select(NotificationState.get('onDisplay')) onDisplay$: Observable<Array<Partial<Alert>>>;
HTML
<ng-container *ngFor="let notif of onDisplay$ | async; let i = index">
<span>{{notif.name}}</span>
</ng-container?>
the data is:
[{
name: 'John',
case: 'CLOSED'
},{
name: 'Joshua',
case: 'CLOSED'
},{
name: 'Carl',
case: 'NEW'
},{
name: 'Jen',
case: 'CLOSED'
}]
I'm using observable and what I'm trying to do is to remove the data which has case: 'CLOSED'.
How to remove the item have CLOSED from the array in ngFor?
Demo write custom pipe for this
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'closedPipe'
})
export class CustomPipe implements PipeTransform {
transform(row: any[]): any {
return row.filter(x=>x.case!="CLOSED");
}
}
You could use *ngIf directive to show only properties with certain condition.
<ng-container *ngFor="let notif of onDisplay$ | async; let i = index">
<span *ngIf="notif.case !== 'CLOSED'">{{notif.name}}</span>
</ng-container>

Dynamically nested angular material menu

Please tell me how I can solve the following problem:
I need to implement a dynamically created menu with different nesting levels depending on the data model object. At the moment, using recursion, we managed to create the menu as such, however, there is a problem of assigning the attribute [matMenuTriggerFor] for, directly, the submenu. The problem is that all subsequent submenus in fact refer to the very first, so when you hover over any of the submenus, it causes a "flip" to the original one (example on image: menu, which includes elements: Device, Extension, Queue, Queue member (with submenu elements)). Thus, for a fraction of seconds, I see the other submenu frame (example on image: submenu Grouped list), after which the very first becomes active. Of course, maybe I didn’t do everything right, so I’m turning here. Help me please. Thank you all.
imenu-item.ts
export interface IMenuItem {
name: string | string[];
link: string;
subItems: IMenuItem[];
}
dynamic-menu.service.ts
import {Inject, Injectable} from '#angular/core';
import {APP_CONFIG_ROUTES} from '../../../config/routes/app.config.routes';
import {IAppConfigRoutes} from '../../../config/routes/iapp.config.routes';
import {IMenuItem} from './imenu-item';
import {_} from '#biesbjerg/ngx-translate-extract/dist/utils/utils';
#Injectable({
providedIn: 'root'
})
export class DynamicMenuService {
private readonly appConfig: any;
constructor(#Inject(APP_CONFIG_ROUTES) appConfig: IAppConfigRoutes) {
this.appConfig = appConfig;
}
getMenuItems(): IMenuItem[] {
return [
{
name: _('labels.device'),
link: '/' + this.appConfig.routes.device,
subItems: null
},
{
name: _('labels.extension'),
link: '/' + this.appConfig.routes.extension,
subItems: null
},
{
name: _('labels.queue'),
link: '/' + this.appConfig.routes.queue,
subItems: null
},
{
name: _('labels.queueMember'),
link: null,
subItems: [{
name: _('labels.fullList'),
link: '/' + this.appConfig.routes.queueMember.all,
subItems: null
}, {
name: _('labels.groupedList'),
link: '/' + this.appConfig.routes.queueMember.grouped,
subItems: [{
name: 'subName',
link: 'subLink',
subItems: [{
name: 'subSubName1',
link: 'subSubLink1',
subItems: null
}, {
name: 'subSubName2',
link: 'subSubLink2',
subItems: null
}]
}]
}]
}
];
}
}
dynamic-menu.component.ts
import {Component, Input, OnInit} from '#angular/core';
import {IMenuItem} from './imenu-item';
#Component({
selector: 'app-dynamic-menu',
templateUrl: './dynamic-menu.component.html',
styleUrls: ['./dynamic-menu.component.scss']
})
export class DynamicMenuComponent implements OnInit {
dynamicMenuItemsData: IMenuItem[];
constructor(private dynamicMenuService: DynamicMenuService) {
}
ngOnInit() {
this.dynamicMenuItemsData = this.dynamicMenuService.getMenuItems();
}
}
dynamic-menu.component.html
<div>
<ng-container [ngTemplateOutlet]="recursiveListMenuItems"
[ngTemplateOutletContext]="{$implicit: dynamicMenuItemsData}">
</ng-container>
</div>
<ng-template #recursiveListMenuItems let-listMenuItems>
<div *ngFor="let menuItem of listMenuItems">
<ng-container [ngTemplateOutlet]="menuItem.subItems != null ? subMenuItem : simpleMenuItem"
[ngTemplateOutletContext]="{$implicit: menuItem}">
</ng-container>
</div>
</ng-template>
<ng-template #simpleMenuItem let-menuItemArg>
<a class="mat-button"
mat-menu-item
routerLink="{{menuItemArg.link}}">
<span>{{menuItemArg.name | translate}}</span>
</a>
</ng-template>
<ng-template #subMenuItem let-menuItemArg>
<a class="mat-button"
mat-menu-item
routerLink="{{menuItemArg.link}}"
[matMenuTriggerFor]="subItemsMenu">
<span>{{menuItemArg.name | translate}}</span>
<mat-menu #subItemsMenu="matMenu"
[overlapTrigger]="false">
<ng-container [ngTemplateOutlet]="recursiveListMenuItems"
[ngTemplateOutletContext]="{$implicit: menuItemArg.subItems}">
</ng-container>
</mat-menu>
</a>
</ng-template>
As a result, it turned out, relying on several similar problems with others. The examples from HERE (dynamic nested menu example) and from HERE (the problem with mat-menu hides immediately on opening) helped to figure it out (in the last example it was enough just to update zone.js by npm)
Sorry for the late answer, but maybe you can still find it helpful.
I wrote a little library called ng-action-outlet that is doing that quite neatly in my opinion.
It looks like this:
group: ActionGroup;
constructor(private actionOutlet: ActionOutletFactory) {
this.group = this.actionOutlet.createGroup();
this.group.createButton().setIcon('home').fire$.subscribe(this.callback);
this.group.createButton().setIcon('settings').fire$.subscribe(this.callback);
}
<ng-container *actionOutlet="group"></ng-container>
DEMO: https://stackblitz.com/edit/ng-action-outlet-demo?file=src/app/app.component.ts

How to get array values from attribute?

I am making application in angular 6 and As i am sending and receiving data in dynamic mode, i am in the need to pass and retrieve the array values through attribute for which i have used the following,
HTML:
<ul class="list-unstyled" id="list" [attr.parent_id]="123">
<li #li class="media p-2 column" *ngFor="let item of items;let i = index;" [attr.items]="item"> {{item.name}} </li>
</ul>
TS:
import { Component, ViewChildren, QueryList, ElementRef } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
#ViewChildren("li") listElements: QueryList<ElementRef<HTMLLIElement>>;
name = 'Angular';
items = [
{ "id": 123, "name": "hello" },
{ "id": 234, "name": "world" }
]
ngAfterViewInit() {
this.printElements();
this.listElements.changes.subscribe(_ => this.printElements());
}
private printElements() {
const elements = this.listElements.toArray();
elements.forEach(element => {
console.log(element.nativeElement.getAttribute('items'));
})
}
}
Working Stackblitz:
https://stackblitz.com/edit/angular-jndtv1
Here instead of
console.log(element.nativeElement.getAttribute('items'));
(which gives [object object])
If i include JSON.stringify,
console.log(JSON.stringify(element.nativeElement.getAttribute('items')));
(gives "[object object]")
Kindly help me to get the array values through the attribute using only Pure javascript/typescript without jquery.
you can do this, not the good way but it works:
in your html:
<ul class="list-unstyled" id="list" [attr.parent_id]="123">
<li #li class="media p-2 column" *ngFor="let item of items;let i = index;"
[attr.items]="item | json"> {{item.name}} </li>
</ul>
in your ts:
private printElements() {
const elements = this.listElements.toArray();
elements.forEach(element => {
console.log(JSON.parse(element.nativeElement.getAttribute('items')).id);
console.log(JSON.parse(element.nativeElement.getAttribute('items')).name);
})
}
DEMO

Group array of objects in Angular Material with *ngFor

Similar to this related question, I want to group an array of objects, e.g., by team name
[
{name: 'Gene', team: 'team alpha'},
{name: 'George', team: 'team beta'},
{name: 'Steve', team: 'team gamma'},
{name: 'Paula', team: 'team beta'},
{name: 'Scruath of the 5th sector', team: 'team gamma'}
];
Unfortunately, the accepted answer using ng-repeat with a groupBy filter doesn't seem to work within an Angular Material expansion panel, which is what I'm trying to do:
I want multiple expansion panels, one per team, which, when expanded, show the involved players.
I tried
<mat-expansion-panel ng-repeat="(key, value) in players | groupBy: 'team'">
<mat-expansion-panel-header>
<mat-panel-title>{{ key }}</mat-panel-title>
</mat-expansion-panel-header>
<li ng-repeat="player in value">
{{player.name}}
</li>
</mat-expansion-panel>
However, ng-repeatis not allowed inside the mat-expansion-panel. *ngFor is allowed but I don't know how to use it with the groupBy filter. *ngFor="let player in players | groupBy: 'team'" throws an error and I can't find any documentation.
You should make your own custom pipe to support GroupBy, also ng-repeat is an angularjs syntax, you should use ngFor.
Your custom pipe should look as,
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({name: 'groupBy'})
export class GroupByPipe implements PipeTransform {
transform(collection: Array<any>, property: string): Array<any> {
if(!collection) {
return null;
}
const groupedCollection = collection.reduce((previous, current)=> {
if(!previous[current[property]]) {
previous[current[property]] = [current];
} else {
previous[current[property]].push(current);
}
return previous;
}, {});
return Object.keys(groupedCollection).map(key => ({ key, value: groupedCollection[key] }));
}
}
STACKBLITZ DEMO

get a particular item inside an array in angular 4

In Angular view I defined a variable data which is like this:
[{"name":"science", count: 3},
{"name":"action", count: 1},
{"name":"thriller", count: 1},
{"name":"article", count: 1},
]
"
So in html file I want to get the count count value for name "science" or "article"
I tried like this:
<span ng-repeat="item in data| filter: {name: "science"}">
{{ item.count }}
</span>
but this gives nothing, I guess because of the filter. How can I do this? Can anyone help me?
You can use pipe like this -
import {Injectable, Pipe, PipeTransform} from 'angular2/core';
#Pipe({
name: 'myfilter'
})
#Injectable()
export class MyFilterPipe implements PipeTransform {
transform(items: any[], args: any[]): any {
return items.filter(item => item.id.indexOf(args[0]) !== -1);
}
}
And the template would look like this -
*ngFor="let element of (elements | myfilter:'123')"
First of all you are mixing up with AngularJS (1.x) syntax, with Angular (2+) you should use ngFor and a custom pipe for filtering.
<span> <tr *ngFor="item in data | sciencefilter:'science'"> {{ item.count }} </span>
and your filter should be
#Pipe({
name: 'sciencefilter'
})
#Injectable()
export class ScienceFilterPipe implements PipeTransform {
transform(items: any[], type: string): any {
return items.filter(item => item.id.indexOf(type) !== -1);
}
}
WITHOUT USING FILTER
count :number;
and then,
this.count = this.items.filter(t=>t.name ==='science');
and in component
<span>
{{ count.length }}
</span>

Categories