Angular 8中合并分类项统计方法的最优方案及实现
Great question! Let’s break down your options and land on the best approach for your Angular 8 app.
First, let’s talk about why an Angular Pipe is the optimal choice here:
- It aligns with Angular’s best practices by separating view formatting logic from your component’s business logic
- It’s reusable across multiple components if you ever need this count display elsewhere
- It makes your template code cleaner and more readable
- It’s easier to test independently since the formatting logic is isolated
Step 1: Create the ItemCount Pipe
First, generate a new pipe (you can use Angular CLI or create the files manually):
ng generate pipe item-count
Then replace the generated pipe code with this implementation:
import { Pipe, PipeTransform } from '@angular/core'; import { DossierItemTypeDto } from './path-to-your-dto'; // Update this to your actual DTO path @Pipe({ name: 'itemCount' }) export class ItemCountPipe implements PipeTransform { transform( itemType: DossierItemTypeDto, typeSearchMatches: Record<string, { total: number; matches: number }>, hasSearchQuery: boolean ): string { // Get the count data, default to 0 if no entry exists const count = typeSearchMatches[itemType.toString()] || { total: 0, matches: 0 }; // Format based on search query presence return hasSearchQuery ? `(${count.matches}/${count.total})` : `(${count.total})`; } }
Don’t forget to add this pipe to your module’s declarations array so it’s available to your components:
@NgModule({ declarations: [ // ... other components/directives ItemCountPipe ] }) export class YourModule { }
Step 2: Update Your Template
Now you can simplify your template by replacing the method call with the pipe:
<mat-tab> <ng-template mat-tab-label> <mat-icon class="goals">grade</mat-icon> <span i18n>Goals</span> {{ itemTypes.Goal | itemCount: typeSearchMatches: hasSearchQuery }} <a [routerLink]="['../', dossier.id, 'item', 'new', itemTypes.Goal]"> <mat-icon class="add_box">add</mat-icon> </a> </ng-template> <ng-container *ngTemplateOutlet="itemList; context: { itemType: itemTypes.Goal }"></ng-container> </mat-tab> <mat-tab> <ng-template mat-tab-label> <mat-icon class="action-steps">list</mat-icon> <span i18n>Action steps</span> {{ itemTypes.ActionStep | itemCount: typeSearchMatches: hasSearchQuery }} <a [routerLink]="['../', dossier.id, 'item', 'new', itemTypes.ActionStep]"> <mat-icon class="add_box">add</mat-icon> </a> </ng-template> <ng-container *ngTemplateOutlet="itemList; context: { itemType: itemTypes.ActionStep }"></ng-container> </mat-tab>
Step 3: Clean Up Your Component
You can now remove the two original methods (dossierItemsCountBy and dossierItemsCountString) from your component—this keeps your component focused on business logic rather than view formatting.
Alternative: Merge the Methods (If Reusability Isn’t a Priority)
If you only need this logic in the current component and don’t plan to reuse it, merging the two methods into one is a quick fix:
dossierItemsCountString(itemType: DossierItemTypeDto): string { const count = this.typeSearchMatches[itemType.toString()] || { total: 0, matches: 0 }; return this.hasSearchQuery ? `(${count.matches}/${count.total})` : `(${count.total})`; }
This works, but it ties the formatting logic directly to your component, which violates the single-responsibility principle and makes it harder to reuse later.
Final Recommendation
Go with the Pipe approach—it’s the most scalable, maintainable, and Angular-idiomatic solution for this scenario.
内容的提问来源于stack exchange,提问作者user13275794

