如何从Angular中的NgFor循环中删除重复的记录 [英] How to remove duplicate records from NgFor Loop in Angular

查看:119
本文介绍了如何从Angular中的NgFor循环中删除重复的记录的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从* ngfor循环中删除重复的记录,并仅保留点击次数最多的记录.

目标是显示用户的点击URL,但是当前,当创建相同URL的新记录时,它将显示在列表中.参见下图:

点击效果正常,但过一会儿,该列表将变得难以辨认.我试图显示例如产品:客场衬衫,点击URL https://blablabla 广告点击次数:6,因为这是我需要显示的最新点击数.显示相同产品且具有旧广告点击数据的记录需要隐藏或从数组中删除.当前有具有相同产品名称,URL和点击数据的记录,并且每次新点击都在增加.我可以在创建记录时安排一个日期,但这似乎有点烦人并且没有完善.我宁愿只显示最新记录.

我试图创建一个过滤器,该过滤器看起来要从get请求中删除重复项,该请求从响应中创建一个变量this.commissions,但是每种过滤器方法均无效,并返回一系列空数组. /p>

使用Moxxi的解决方案并向组件添加一些返回值,该视图现在绑定了某些东西-为"false",但它绑定了某些东西:

analytics.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from 'src/app/environments/environments';
import { throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
import { Article } from './article';

@Injectable({
  providedIn: 'root'
})
export class AnalyticsService {

  article_url = environment.api_url + 'text_image_templates/rows';
  commissions_url = environment.api_url + 'commissions/rows';

  constructor(private http: HttpClient) { }

  getAllArticles(){
    return this.http.get<{data: Article[]}>(this.article_url)
    .pipe(
      retry(1),
      catchError(this.handleError),
    );
  }

  getAllCommissionData(): Observable<Card[]>{
    return this.http.get<Card[]>(this.commissions_url)
    .pipe(
      retry(1),
      catchError(this.handleError),
    )
  }

  handleError(error) {
    let errorMessage = '';
    if (error.error) {
      errorMessage = error.error.message;
    } else {
      errorMessage = error;
    }
    return throwError(errorMessage);
  }
}

卡类

export class Card {
    url: string;
    page_url: string;
    page_type: string;
    clicks: number;
}

click-cards.component.ts

import { Component, OnInit } from '@angular/core';
import { Commission } from '../commission';
import { AnalyticsService } from '../analytics.service';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import * as _ from 'lodash';
import { Card } from '../card';

@Component({
  selector: 'app-click-cards',
  templateUrl: './click-cards.component.html',
  styleUrls: ['./click-cards.component.scss']
})
export class ClickCardsComponent implements OnInit {

  commissions$: Observable<any>;

  constructor(private analyticsService: AnalyticsService) {}

  ngOnInit() {
    this.getCommissions();
  }

  getCommissions(){
    this.commissions$ = this.analyticsService.getAllCommissionData().pipe(
      map((commisions: Card[]) => _.uniqBy(commisions.sort((a, b) => b.clicks - a.clicks), commission => commission.page_url)),
      map((commissions: Card[]) => _.groupBy(commissions, commission => commission.page_type)),
    )
  }
}

click-cards.component.html

<ng-container *ngIf="commissions$ | async as commissions">
  <ng-container *ngFor="let type of ['home', 'article', 'products']">
    <h4>{{ type | titlecase }}</h4>
    <p *ngIf="!commissions[type]">No {{ type }} Commissions Logged Yet</p>
    <ul *ngFor="let card of commissions[type]">
      <app-click-card [card]="card"></app-click-card>
    </ul>
  </ng-container>
</ng-container>

click-card.component.html

<ng-container *ngIf="card">
  <li>
    <ul>
      <li><strong>Click Origin:</strong> {{ card.page_url }}</li>
      <li><strong>Click Through Url:</strong> {{ card.url }}</li>
      <li *ngIf="card.clicks"><strong>Ad Clicks:</strong> {{ card.clicks }}</li>
      <li *ngIf="!card.clicks">No Ad Clicks Yet</li>
    </ul>
  </li>
</ng-container>

这与我在循环中使用子组件这一事实有关吗?我需要在child-component.ts内部做些什么吗?我对下一步是什么有些困惑.

以前有没有人遇到过这个问题?

解决方案

在此处查看Stackblitz: https ://stackblitz.com/edit/angular-3kchji

不要做过滤器管道,也不要在组件内订阅. 更好:存储Observable,使用rxjs运算符删除重复项并使用异步管道.

模板中的异步管道

<ng-container *ngIf="commissions$ | async as commissions">
  <h4>Home</h4>
  <p *ngIf="!commissions['home']">No home Commissions Logged Yet</p>
  <ul *ngFor="let card of commissions['home']">
    <app-click-card [card]="card"></app-click-card>
  </ul>
  <h4>Articles</h4>
  <p *ngIf="!commissions['article']">No article Commissions Logged Yet</p>
  <ul *ngFor="let card of commissions['article']">
    <app-click-card [card]="card"></app-click-card>
  </ul>
  <h4>Products</h4>
  <p *ngIf="!commissions['products']">No product Commissions Logged Yet</p>
  <ul *ngFor="let card of commissions['products']">
    <app-click-card [card]="card"></app-click-card>
  </ul>
</ng-container>

还有您的组件

export class ClickCardsComponent implements OnInit {

  commissions$: Observable<any>;

  constructor(private analyticsService: AnalyticsService) { }

  ngOnInit() {
    this.getCommissions();
  }

  getCommissions(){
    this.commissions$ = this.analyticsService.getAllCommissionData().pipe(
      map((commissions: Commission[]) => {
        /* your logic to remove duplicates of the array */
      }),
      // below is extended answer
      map((commissions: Commission[]) => {
        _.groupBy(commissions, commission => commission.page_type)
      })
    )
  }
}

除此之外,您还可以将要显示的类型存储在数组中并对其进行循环

<ng-container *ngIf="commissions$ | async as commissions">
  <ng-container *ngFor="let type of ['home', 'article', 'products']">
    <h4>{{ type | titlecase }}</h4>
    <p *ngIf="!commissions[type]">No {{ type }} Commissions Logged Yet</p>
    <ul *ngFor="let card of commissions[type]">
      <app-click-card [card]="card"></app-click-card>
    </ul>
  </ng-container>
</ng-container>

完成此操作后,您可能还想保留app-click-card组件并将其直接添加到ul标签中.

I am trying to remove duplicate records from a *ngfor loop and leave only the record with the most clicks for that record.

The objective is to show click-through URLs from the user, but currently, when a new record for the same URL has been created, it displays in the list. See the image below:

The clicks are working as expected, but the list will become illegible after a while. I'm trying to show e.g Product: Away Shirt, Click through URL https://blablabla Ad Clicks: 6, as this is the most recent click number I need to display. Records showing the same Product which have old ad click data needs to be hidden or removed from the array. There are currently records with the same product name, URL and click data which is increasing with each new click. I could place a date when the record was created, but this seems a little crass and unrefined. I would rather just show the most up to date record.

I have tried to create a filter, where the filter looks to remove duplicates from the get request which creates a variable this.commissions from the response, but each filter approach doesn't work and returns a series of empty arrays.

Edited: Using Moxxi's solution and adding some returns to the component, the view is now binding something - which is 'false', but it is binding something:

analytics.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from 'src/app/environments/environments';
import { throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
import { Article } from './article';

@Injectable({
  providedIn: 'root'
})
export class AnalyticsService {

  article_url = environment.api_url + 'text_image_templates/rows';
  commissions_url = environment.api_url + 'commissions/rows';

  constructor(private http: HttpClient) { }

  getAllArticles(){
    return this.http.get<{data: Article[]}>(this.article_url)
    .pipe(
      retry(1),
      catchError(this.handleError),
    );
  }

  getAllCommissionData(): Observable<Card[]>{
    return this.http.get<Card[]>(this.commissions_url)
    .pipe(
      retry(1),
      catchError(this.handleError),
    )
  }

  handleError(error) {
    let errorMessage = '';
    if (error.error) {
      errorMessage = error.error.message;
    } else {
      errorMessage = error;
    }
    return throwError(errorMessage);
  }
}

card class

export class Card {
    url: string;
    page_url: string;
    page_type: string;
    clicks: number;
}

click-cards.component.ts

import { Component, OnInit } from '@angular/core';
import { Commission } from '../commission';
import { AnalyticsService } from '../analytics.service';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import * as _ from 'lodash';
import { Card } from '../card';

@Component({
  selector: 'app-click-cards',
  templateUrl: './click-cards.component.html',
  styleUrls: ['./click-cards.component.scss']
})
export class ClickCardsComponent implements OnInit {

  commissions$: Observable<any>;

  constructor(private analyticsService: AnalyticsService) {}

  ngOnInit() {
    this.getCommissions();
  }

  getCommissions(){
    this.commissions$ = this.analyticsService.getAllCommissionData().pipe(
      map((commisions: Card[]) => _.uniqBy(commisions.sort((a, b) => b.clicks - a.clicks), commission => commission.page_url)),
      map((commissions: Card[]) => _.groupBy(commissions, commission => commission.page_type)),
    )
  }
}

click-cards.component.html

<ng-container *ngIf="commissions$ | async as commissions">
  <ng-container *ngFor="let type of ['home', 'article', 'products']">
    <h4>{{ type | titlecase }}</h4>
    <p *ngIf="!commissions[type]">No {{ type }} Commissions Logged Yet</p>
    <ul *ngFor="let card of commissions[type]">
      <app-click-card [card]="card"></app-click-card>
    </ul>
  </ng-container>
</ng-container>

click-card.component.html

<ng-container *ngIf="card">
  <li>
    <ul>
      <li><strong>Click Origin:</strong> {{ card.page_url }}</li>
      <li><strong>Click Through Url:</strong> {{ card.url }}</li>
      <li *ngIf="card.clicks"><strong>Ad Clicks:</strong> {{ card.clicks }}</li>
      <li *ngIf="!card.clicks">No Ad Clicks Yet</li>
    </ul>
  </li>
</ng-container>

Is this relating to the fact I am using child-components in the loop? Do I need to do something inside the child-component.ts? I am a little stumped as to what my next step is?

Has anyone come across this issue before?

解决方案

Edit: see Stackblitz here: https://stackblitz.com/edit/angular-3kchji

Don't do the filter pipe and don't subscribe within the component. Better: store Observable, use rxjs operators to remove the duplicates and use async pipe.

Async pipe in your template

<ng-container *ngIf="commissions$ | async as commissions">
  <h4>Home</h4>
  <p *ngIf="!commissions['home']">No home Commissions Logged Yet</p>
  <ul *ngFor="let card of commissions['home']">
    <app-click-card [card]="card"></app-click-card>
  </ul>
  <h4>Articles</h4>
  <p *ngIf="!commissions['article']">No article Commissions Logged Yet</p>
  <ul *ngFor="let card of commissions['article']">
    <app-click-card [card]="card"></app-click-card>
  </ul>
  <h4>Products</h4>
  <p *ngIf="!commissions['products']">No product Commissions Logged Yet</p>
  <ul *ngFor="let card of commissions['products']">
    <app-click-card [card]="card"></app-click-card>
  </ul>
</ng-container>

And your component

export class ClickCardsComponent implements OnInit {

  commissions$: Observable<any>;

  constructor(private analyticsService: AnalyticsService) { }

  ngOnInit() {
    this.getCommissions();
  }

  getCommissions(){
    this.commissions$ = this.analyticsService.getAllCommissionData().pipe(
      map((commissions: Commission[]) => {
        /* your logic to remove duplicates of the array */
      }),
      // below is extended answer
      map((commissions: Commission[]) => {
        _.groupBy(commissions, commission => commission.page_type)
      })
    )
  }
}

Beyond that you could also store the types you want to display within an array and loop it

<ng-container *ngIf="commissions$ | async as commissions">
  <ng-container *ngFor="let type of ['home', 'article', 'products']">
    <h4>{{ type | titlecase }}</h4>
    <p *ngIf="!commissions[type]">No {{ type }} Commissions Logged Yet</p>
    <ul *ngFor="let card of commissions[type]">
      <app-click-card [card]="card"></app-click-card>
    </ul>
  </ng-container>
</ng-container>

And with this being done you maybe also want to spare your app-click-card component and add it directly in the ul tag.

这篇关于如何从Angular中的NgFor循环中删除重复的记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆