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>
I want to remove the matChip on click of cancel icon
here is my code
<mat-chip-list>
<mat-chip
*ngFor="let user of data.users"
[removable]="true"
[selectable]="true"
(removed)="remove(user.id)">
{{user.name}}
<mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
</mat-chip-list>
Even though [removable]="true" is set it is not removing the clicked chip
I have been following this docs example
Working example. Please, find the update files from your above link (which is available in your question) as below, while other files remains as same. If you want to check the working code, then try to replace the whole code in respective files of your link with below code to same files.
NOTE: for better readability, I have removed input element with add functionality.
chips-input-example.html
<mat-form-field class="example-chip-list">
<mat-chip-list #chipList aria-label="User selection">
<mat-chip *ngFor="let user of data.users" [selectable]="selectable" [removable]="removable"
(removed)="remove(user.id)">
{{user.name}}
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
</mat-chip>
</mat-chip-list>
</mat-form-field>
chips-input-example.ts
import {Component} from '#angular/core';
interface User {
name: string;
id: number;
}
interface Data {
users: User[];
}
#Component({
selector: 'chips-input-example',
templateUrl: 'chips-input-example.html',
styleUrls: ['chips-input-example.css'],
})
export class ChipsInputExample {
selectable = true;
removable = true;
data: Data = {
users: [
{name: 'User1', id: 0},
{name: 'User2', id: 1},
{name: 'User3', id: 2},
]};
remove(getId: number): void {
this.data.users = [...this.data.users.filter(({id}) => getId !== id)];
}
}
chips-input-example.css
.example-chip-list {
width: 100%;
}
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>
Apparently, Angular 2 will use pipes instead of filters as in Angular1 in conjunction with ng-for to filter results, although the implementation still seems to be vague, with no clear documentation.
Namely what I'm trying to achieve could be viewed from the following perspective
<div *ng-for="#item of itemsList" *ng-if="conditon(item)"></div>
How to implement so using pipes?
Basically, you write a pipe which you can then use in the *ngFor directive.
In your component:
filterargs = {title: 'hello'};
items = [{title: 'hello world'}, {title: 'hello kitty'}, {title: 'foo bar'}];
In your template, you can pass string, number or object to your pipe to use to filter on:
<li *ngFor="let item of items | myfilter:filterargs">
In your pipe:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'myfilter',
pure: false
})
export class MyFilterPipe implements PipeTransform {
transform(items: any[], filter: Object): any {
if (!items || !filter) {
return items;
}
// filter items array, items which match and return true will be
// kept, false will be filtered out
return items.filter(item => item.title.indexOf(filter.title) !== -1);
}
}
Remember to register your pipe in app.module.ts; you no longer need to register the pipes in your #Component
import { MyFilterPipe } from './shared/pipes/my-filter.pipe';
#NgModule({
imports: [
..
],
declarations: [
MyFilterPipe,
],
providers: [
..
],
bootstrap: [AppComponent]
})
export class AppModule { }
Here's a Plunker which demos the use of a custom filter pipe and the built-in slice pipe to limit results.
Please note (as several commentators have pointed out) that there is a reason why there are no built-in filter pipes in Angular.
A lot of you have great approaches, but the goal here is to be generic and defined a array pipe that is extremely reusable across all cases in relationship to *ngFor.
callback.pipe.ts (don't forget to add this to your module's declaration array)
import { PipeTransform, Pipe } from '#angular/core';
#Pipe({
name: 'callback',
pure: false
})
export class CallbackPipe implements PipeTransform {
transform(items: any[], callback: (item: any) => boolean): any {
if (!items || !callback) {
return items;
}
return items.filter(item => callback(item));
}
}
Then in your component, you need to implement a method with the following signuature (item: any) => boolean, in my case for example, I called it filterUser, that filters users' age that are greater than 18 years.
Your Component
#Component({
....
})
export class UsersComponent {
filterUser(user: IUser) {
return !user.age >= 18
}
}
And last but not least, your html code will look like this:
Your HTML
<li *ngFor="let user of users | callback: filterUser">{{user.name}}</li>
As you can see, this Pipe is fairly generic across all array like items that need to be filter via a callback. In my case, I found it to be very useful for *ngFor like scenarios.
Hope this helps!!!
codematrix
Simplified way (Used only on small arrays because of performance issues. In large arrays you have to make the filter manually via code):
See: https://angular.io/guide/pipes#appendix-no-filterpipe-or-orderbypipe
#Pipe({
name: 'filter'
})
#Injectable()
export class FilterPipe implements PipeTransform {
transform(items: any[], field : string, value : string): any[] {
if (!items) return [];
if (!value || value.length == 0) return items;
return items.filter(it =>
it[field].toLowerCase().indexOf(value.toLowerCase()) !=-1);
}
}
Usage:
<li *ngFor="let it of its | filter : 'name' : 'value or variable'">{{it}}</li>
If you use a variable as a second argument, don't use quotes.
This is what I implemented without using pipe.
component.html
<div *ngFor="let item of filter(itemsList)">
component.ts
#Component({
....
})
export class YourComponent {
filter(itemList: yourItemType[]): yourItemType[] {
let result: yourItemType[] = [];
//your filter logic here
...
...
return result;
}
}
I'm not sure when it came in but they already made slice pipe that will do that. It's well documented too.
https://angular.io/docs/ts/latest/api/common/index/SlicePipe-pipe.html
<p *ngFor="let feature of content?.keyFeatures | slice:1:5">
{{ feature.description }}
</p>
A simple solution that works with Angular 6 for filtering a ngFor, it's the following:
<span *ngFor="item of itemsList" >
<div *ngIf="yourCondition(item)">
your code
</div>
</span>
Spans are useful because does not inherently represent anything.
You could also use the following:
<template ngFor let-item [ngForOf]="itemsList">
<div *ng-if="conditon(item)"></div>
</template>
This will only show the div if your items matches the condition
See the angular documentation for more information
If you would also need the index, use the following:
<template ngFor let-item [ngForOf]="itemsList" let-i="index">
<div *ng-if="conditon(item, i)"></div>
</template>
pipes in Angular2 are similar to pipes on the command line. The output of each preceding value is fed into the filter after the pipe which makes it easy to chain filters as well like this:
<template *ngFor="#item of itemsList">
<div *ngIf="conditon(item)">{item | filter1 | filter2}</div>
</template>
I know its an old question, however, I thought it might be helpful to offer another solution.
equivalent of AngularJS of this
<div *ng-for="#item of itemsList" *ng-if="conditon(item)"></div>
in Angular 2+ you cant use *ngFor and *ngIf on a same element, so it will be following:
<div *ngFor="let item of itemsList">
<div *ngIf="conditon(item)">
</div>
</div>
and if you can not use as internal container use ng-container instead.
ng-container is useful when you want to conditionally append a group of elements (ie using *ngIf="foo") in your application but don't want to wrap them with another element.
There is a dynamic filter pipe that I use
Source data:
items = [{foo: 'hello world'}, {foo: 'lorem ipsum'}, {foo: 'foo bar'}];
In the template you can dinamically set the filter in any object attr:
<li *ngFor="let item of items | filter:{foo:'bar'}">
The pipe:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'filter',
})
export class FilterPipe implements PipeTransform {
transform(items: any[], filter: Record<string, any>): any {
if (!items || !filter) {
return items;
}
const key = Object.keys(filter)[0];
const value = filter[key];
return items.filter((e) => e[key].indexOf(value) !== -1);
}
}
Don't forget to register the pipe in your app.module.ts declarations
Pipe would be best approach. but below one would also work.
<div *ng-for="#item of itemsList">
<ng-container *ng-if="conditon(item)">
// my code
</ng-container>
</div>
For this requirement, I implement and publish a generic component. See
https://www.npmjs.com/package/w-ng5
For use this components, before, install this package with npm:
npm install w-ng5 --save
After, import module in app.module
...
import { PipesModule } from 'w-ng5';
In the next step, add in declare section of app.module:
imports: [
PipesModule,
...
]
Sample use
Filtering simple string
<input type="text" [(ngModel)]="filtroString">
<ul>
<li *ngFor="let s of getStrings() | filter:filtroString">
{{s}}
</li>
</ul>
Filtering complex string - field 'Value' in level 2
<input type="text" [(ngModel)]="search">
<ul>
<li *ngFor="let s of getComplexTypesExtends() | filter:[{field:'n1.n2.valor2', value: search}]">
{{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}}
</li>
</ul>
Filtering complex string - middle field - 'Value' in level 1
<input type="text" [(ngModel)]="search3">
<ul>
<li *ngFor="let s of getComplexTypesExtends() | filter:[{field:'n1.valor1', value: search3}]">
{{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}}
</li>
</ul>
Filtering complex array simple - field 'Nome' level 0
<input type="text" [(ngModel)]="search2">
<ul>
<li *ngFor="let s of getComplexTypesExtends() | filter:[{field:'nome', value: search2}]">
{{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}}
</li>
</ul>
Filtering in tree fields - field 'Valor' in level 2 or 'Valor' in level 1 or 'Nome' in level 0
<input type="text" [(ngModel)]="search5">
<ul>
<li *ngFor="let s of getComplexTypesExtends() | filter:[{field:'n1.n2.valor2', value: search5}, {field:'n1.valor1', value: search5}, {field:'nome', value: search5}]">
{{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}}
</li>
</ul>
Filtering nonexistent field - 'Valor' in nonexistent level 3
<input type="text" [(ngModel)]="search4">
<ul>
<li *ngFor="let s of getComplexTypesExtends() | filter:[{field:'n1.n2.n3.valor3', value: search4}]">
{{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}}
</li>
</ul>
This component work with infinite attribute level...
I've created a plunker based off of the answers here and elsewhere.
Additionally I had to add an #Input, #ViewChild, and ElementRef of the <input> and create and subscribe() to an observable of it.
Angular2 Search Filter: PLUNKR (UPDATE: plunker no longer works)
Based on the very elegant callback pipe solution proposed above, it is possible to generalize it a bit further by allowing additional filter parameters to be passed along. We then have :
callback.pipe.ts
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'callback',
pure: false
})
export class CallbackPipe implements PipeTransform {
transform(items: any[], callback: (item: any, callbackArgs?: any[]) => boolean, callbackArgs?: any[]): any {
if (!items || !callback) {
return items;
}
return items.filter(item => callback(item, callbackArgs));
}
}
component
filterSomething(something: Something, filterArgs: any[]) {
const firstArg = filterArgs[0];
const secondArg = filterArgs[1];
...
return <some condition based on something, firstArg, secondArg, etc.>;
}
html
<li *ngFor="let s of somethings | callback : filterSomething : [<whatWillBecomeFirstArg>, <whatWillBecomeSecondArg>, ...]">
{{s.aProperty}}
</li>
This is my code:
import {Pipe, PipeTransform, Injectable} from '#angular/core';
#Pipe({
name: 'filter'
})
#Injectable()
export class FilterPipe implements PipeTransform {
transform(items: any[], field : string, value): any[] {
if (!items) return [];
if (!value || value.length === 0) return items;
return items.filter(it =>
it[field] === value);
}
}
Sample:
LIST = [{id:1,name:'abc'},{id:2,name:'cba'}];
FilterValue = 1;
<span *ngFor="let listItem of LIST | filter : 'id' : FilterValue">
{{listItem .name}}
</span>
Another approach I like to use for application specific filters, is to use a custom read-only property on your component which allows you to encapsulate the filtering logic more cleanly than using a custom pipe (IMHO).
For example, if I want to bind to albumList and filter on searchText:
searchText: "";
albumList: Album[] = [];
get filteredAlbumList() {
if (this.config.searchText && this.config.searchText.length > 1) {
var lsearchText = this.config.searchText.toLowerCase();
return this.albumList.filter((a) =>
a.Title.toLowerCase().includes(lsearchText) ||
a.Artist.ArtistName.toLowerCase().includes(lsearchText)
);
}
return this.albumList;
}
To bind in the HTML you can then bind to the read-only property:
<a class="list-group-item"
*ngFor="let album of filteredAlbumList">
</a>
I find for specialized filters that are application specific this works better than a pipe as it keeps the logic related to the filter with the component.
Pipes work better for globally reusable filters.
I created the following pipe for getting desired items from a list.
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'filter'
})
export class FilterPipe implements PipeTransform {
transform(items: any[], filter: string): any {
if(!items || !filter) {
return items;
}
// To search values only of "name" variable of your object(item)
//return items.filter(item => item.name.toLowerCase().indexOf(filter.toLowerCase()) !== -1);
// To search in values of every variable of your object(item)
return items.filter(item => JSON.stringify(item).toLowerCase().indexOf(filter.toLowerCase()) !== -1);
}
}
Lowercase conversion is just to match in case insensitive way.
You can use it in your view like this:-
<div>
<input type="text" placeholder="Search reward" [(ngModel)]="searchTerm">
</div>
<div>
<ul>
<li *ngFor="let reward of rewardList | filter:searchTerm">
<div>
<img [src]="reward.imageUrl"/>
<p>{{reward.name}}</p>
</div>
</li>
</ul>
</div>
Ideally you should create angualr 2 pipe for that. But you can do this trick.
<ng-container *ngFor="item in itemsList">
<div*ngIf="conditon(item)">{{item}}</div>
</ng-container>
This is your array
products: any = [
{
"name": "John-Cena",
},
{
"name": "Brock-Lensar",
}
];
This is your ngFor loop
Filter By :
<input type="text" [(ngModel)]='filterText' />
<ul *ngFor='let product of filterProduct'>
<li>{{product.name }}</li>
</ul>
There I'm using filterProduct instant of products, because i want to preserve my original data.
Here model _filterText is used as a input box.When ever there is any change setter function will call.
In setFilterText performProduct is called it will return the result only those who match with the input. I'm using lower case for case insensitive.
filterProduct = this.products;
_filterText : string;
get filterText() : string {
return this._filterText;
}
set filterText(value : string) {
this._filterText = value;
this.filterProduct = this._filterText ? this.performProduct(this._filterText) : this.products;
}
performProduct(value : string ) : any {
value = value.toLocaleLowerCase();
return this.products.filter(( products : any ) =>
products.name.toLocaleLowerCase().indexOf(value) !== -1);
}
You can do this trick:
<ng-container *ngFor="item in items">
<div *ngIf="conditon(item)">{{ item.value }}</div>
</ng-container>
or
<div *ngFor="item in items">
<ng-container *ngIf="conditon(item)">{{ item.value }}</ng-container>
</div>
Here's an example that I created a while back, and blogged about, that includes a working plunk. It provides a filter pipe that can filter any list of objects. You basically just specify the property and value {key:value} within your ngFor specification.
It's not a lot different from #NateMay's response, except that I explain it in relatively verbose detail.
In my case, I filtered an unordered list on some text (filterText) the user entered against the "label" property of the objects in my array with this sort of mark-up:
<ul>
<li *ngFor="let item of _items | filter:{label: filterText}">{{ item.label }}</li>
</ul>
https://long2know.com/2016/11/angular2-filter-pipes/
The first step you create Filter using #Pipe in your component.ts file:
your.component.ts
import { Component, Pipe, PipeTransform, Injectable } from '#angular/core';
import { Person} from "yourPath";
#Pipe({
name: 'searchfilter'
})
#Injectable()
export class SearchFilterPipe implements PipeTransform {
transform(items: Person[], value: string): any[] {
if (!items || !value) {
return items;
}
console.log("your search token = "+value);
return items.filter(e => e.firstName.toLowerCase().includes(value.toLocaleLowerCase()));
}
}
#Component({
....
persons;
ngOnInit() {
//inicial persons arrays
}
})
And data structure of Person object:
person.ts
export class Person{
constructor(
public firstName: string,
public lastName: string
) { }
}
In your view in html file:
your.component.html
<input class="form-control" placeholder="Search" id="search" type="text" [(ngModel)]="searchText"/>
<table class="table table-striped table-hover">
<colgroup>
<col span="1" style="width: 50%;">
<col span="1" style="width: 50%;">
</colgroup>
<thead>
<tr>
<th>First name</th>
<th>Last name</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let person of persons | searchfilter:searchText">
<td>{{person.firstName}}</td>
<td>{{person.lastName}}</td>
</tr>
</tbody>
</table>
After some googling, I came across ng2-search-filter. In will take your object and apply the search term against all object properties looking for a match.
I was finding somethig for make a filter passing an Object, then i can use it like multi-filter:
i did this Beauty Solution:
filter.pipe.ts
import { PipeTransform, Pipe } from '#angular/core';
#Pipe({
name: 'filterx',
pure: false
})
export class FilterPipe implements PipeTransform {
transform(items: any, filter: any, isAnd: boolean): any {
let filterx=JSON.parse(JSON.stringify(filter));
for (var prop in filterx) {
if (Object.prototype.hasOwnProperty.call(filterx, prop)) {
if(filterx[prop]=='')
{
delete filterx[prop];
}
}
}
if (!items || !filterx) {
return items;
}
return items.filter(function(obj) {
return Object.keys(filterx).every(function(c) {
return obj[c].toLowerCase().indexOf(filterx[c].toLowerCase()) !== -1
});
});
}
}
component.ts
slotFilter:any={start:'',practitionerCodeDisplay:'',practitionerName:''};
componet.html
<tr>
<th class="text-center"> <input type="text" [(ngModel)]="slotFilter.start"></th>
<th class="text-center"><input type="text" [(ngModel)]="slotFilter.practitionerCodeDisplay"></th>
<th class="text-left"><input type="text" [(ngModel)]="slotFilter.practitionerName"></th>
<th></th>
</tr>
<tbody *ngFor="let item of practionerRoleList | filterx: slotFilter">...
Most simple and easy way to limit your ngFor is given below
<li *ngFor="let item of list | slice:0:10; let i=index" class="dropdown-item" >{{item.text}}</li>