Reputation: 65978
I would like to write generic pipe
. This is the pipe
where I have written for data type Category
order-by-category.ts
import { Pipe, PipeTransform } from '@angular/core';
import { Category } from '../../models/Category';
import { sortBy } from 'lodash';
@Pipe({
name: 'orderByCategory',
})
export class OrderByCategoryPipe implements PipeTransform {
transform(value: Category[]): Category[] {
return sortBy(value, (o: Category) => { return o.name; });
}
}
.html
<ion-item *ngFor="let c of categorySortedList | orderByCategory">
<ion-label>{{c.name }}</ion-label>
<ion-radio [value]="c.id" (ionSelect)="selectedCategory(c)"></ion-radio>
</ion-item>
Now I need to write the same kind of pipe
. But the data type
is different.
Now it is like this:
transform(value: BudgetGroup[]): BudgetGroup[] {
return sortBy(value, (o: BudgetGroup) => { return o.name; });
}
So I would like to use generic solution here rather than the same kind of 2 pipes
and also I need to maintain Typed pipe
too.
Upvotes: 8
Views: 10387
Reputation: 7640
I made a similar question here : A way to cast variables in template to multiple types and since I went trough this post like 10 times when searching online, I would like to post a hacky solution here too.
I made the following simple pipe :
@Pipe({ name: 'toType', pure: true, }) export class ToTypePipe implements PipeTransform { transform<T>(value: unknown, _: T): T { return value as T; } }
You can call it like
value | toType: Animal.DOG
value can be anything of
Animal
but we just cast is as one of the value, so the compiler consider it ok to use it and do not complain.IT IS NOT SAFE AND KIND OF REMOVE THE PURPOSE OF STRICT MODE.
But, it allow to do some workaround and make some very simple cast easy. like in this case :
[cdkColumnDef]="columns.name.def"> <td class="align-items-center p-4 w-80" cdk-cell *cdkCellDef="let element"> ``` `*cdkCellDef="let element"` this IS an `Animal` but angular template do not allow us to have the correct typing, so in this case `let element | typeOf: Animal.DOG` should be completely safe
Upvotes: 3
Reputation: 9242
I'm just writing the same and found your question. The answer is quite obvious - generic function will work:
@Pipe({
name: 'orderByName',
})
export class OrderByNamePipe implements PipeTransform {
transform<T extends {name: string}>(value: T[]): T[] {
return value.sort((a, b) => {
if (a.name > b.name) return -1;
if (a.name < b.name) return 1;
return 0;
});
}
}
The original type is kept and seen in templates 🤓
Upvotes: 24
Reputation: 3170
Trying to clarify what @Rob has already pointed out, with code. You can generalize your pipe's transform()
method to conform to a certain type that has a mandatory 'name' like this.
import { Pipe, PipeTransform } from '@angular/core';
import { sortBy } from 'lodash';
export interface NameModelForPipe {
name: string; // forces the 'name' property
[key: string]: any; // allows for basically another property of 'any' type
}
@Pipe({
name: 'orderByNameModel',
})
export class OrderByNameModelPipe implements PipeTransform {
transform(value: NameModelForPipe[]): NameModelForPipe[] {
return sortBy(value, (o: NameModelForPipe) => { return o.name; });
}
}
If you don't want to declare an interface explicitly, you can specify it in-line,
import { Pipe, PipeTransform } from '@angular/core';
import { sortBy } from 'lodash';
@Pipe({
name: 'orderByNameModel',
})
export class OrderByNameModelPipe implements PipeTransform {
transform(value: { name: string; [key: string]: any; }[]): { name: string; [key: string]: any; }[] {
return sortBy(value, (o: { name: string; [key: string]: any; }) => { return o.name; });
}
}
Upvotes: 3