如何使用 *ngFor 循环我的数据对象?

Posted

技术标签:

【中文标题】如何使用 *ngFor 循环我的数据对象?【英文标题】:How can I use *ngFor to loop my data object? 【发布时间】:2020-01-21 02:24:11 【问题描述】:

这是我的“monthlyCalendar”对象:


    "monthName": "September 2019",
    "dateObjList": 
        "1": 
            "dayOfWeek": "0",
            "isPublicHoliday": false,
            .............
        ,
        "2": 
            "dayOfWeek": "0",
            "isPublicHoliday": true,
            .............
        ,
        .....................
    
   

这是我的组件.ts:

...........
ngOnInit() 
    this.monthlyCalendarService.getMonthlyCalendar(null, null).subscribe(
      (res: MonthlyCalendar) => 
        this.monthlyCalendar = res;
        this.monthName = this.monthlyCalendar.monthName;
      ,
      (error: Error) => 
        alert('Something wrong when getting Monthly Calendar data.\n' + error.message);
      ,
    );
  
.........

这是我每月的CalendarService.ts:

import  Injectable  from '@angular/core';
import  HttpClient, HttpParams  from '@angular/common/http';
import  MonthlyCalendar  from './monthly-calendar';
import  Observable  from 'rxjs';
import  map, catchError  from 'rxjs/operators';
@Injectable(
  providedIn: 'root'
)
export class MonthlyCalendarService 

  constructor(private http: HttpClient)  

  getMonthlyCalendar(year: string, month: string): Observable<MonthlyCalendar> 
    const params = new HttpParams();

    params.set('year', year);
    params.set('month', month);

    return this.http.get('backend/getMonthlyCalendar.php', params).pipe(map((res: MonthlyCalendar) => res));
    /*
    return this.http.get('backend/getMonthlyCalendar.php', params)
    .pipe(map((res: MonthlyCalendar) => console.log('service:' + JSON.stringify(res)); return res; )
    //, catchError(this.handleError)
    );
    */
  

这是我的component.html

...........
<td *ngFor="let dateObj of monthlyCalendar?.dateObjList|async" class="phCell">
    <span *ngIf="dateObj.isPublicHoliday">PH</span>
</td>
...........

我想在 dateObj.isPublicHoliday 属性为 true 时显示 span 标签。

但是,浏览器提示我以下错误消息:

ERROR Error: InvalidPipeArgument: '[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]' for pipe 'AsyncPipe'

如果我也将“keyvalue”管道应用于 *ngFor,即

 <td *ngFor="let dateObj of monthlyCalendar?.dateObjList|async|keyvalue" class="phCell">

我收到以下错误消息:

Error: InvalidPipeArgument: '[object Object]' for pipe 'AsyncPipe'

我怎样才能让它工作?

【问题讨论】:

access key and value of object using *ngFor的可能重复 【参考方案1】:

最后,我整合你所有的想法来解决这个问题,

我修改了 component.ts 以将monthlyCalendar 对象更改为可观察。

monthlyCalendar: Observable<MonthlyCalendar>;
................

ngOnInit() 
    this.monthlyCalendar = this.monthlyCalendarService.getMonthlyCalendar(null, null);

然后我按照 Adrian 的建议创建了一个名为 toArray 的管道。 我将component.html修改如下

<td *ngFor="let dateObj of (monthlyCalendar|async)?.dateObjList|toArray " class="alignCenter phCell borderCell dateCell">
    <span *ngIf=dateObj.isPublicHoliday>PH</span>
</td>

虽然正确显示“PH”,但浏览器提示“TypeError: Cannot read property 'dateObjList' of null”。 最后,下面的网页告诉我为什么安全导航运算符不能与异步管道一起使用。

Safe navigation operator on an async observable and pipe in Angular 2

所以,toArray 管道的最终版本如下:

import  Pipe, PipeTransform  from '@angular/core';

@Pipe(
  name: 'toArray'
)
export class ToArrayPipe implements PipeTransform 
  transform(value, args: string[]): any 
    if (value != null) 
      return Object.values(value);
    
  

感谢您的所有帮助。

【讨论】:

【参考方案2】:

丢失异步管道,您的每月日历不是异步可观察对象。您在订阅后手动分配它。

管道keyvalue 使用value 属性返回并带有2 个道具keyvalue 的对象导航到您的对象。

检查管道keyvalue中的value属性

dateObj.value.isPublicHoliday

所以你的代码终于变成了

<td *ngFor="let dateObj of (monthlyCalendar)?.dateObjList|keyvalue" class="phCell">
    <span *ngIf="dateObj.value.isPublicHoliday">PH</span>
</td>

编辑 2:

Angular 推荐的正确方式

如果你想在你的组件中像 observable 那样做

monthlyCalendar$: Observable<any>; //declare your observable to use in template (Use a custom Interface to have data type as your response)

ngOnInit()
  this.monthlyCalendar$ = this.monthlyCalendarService.getMonthlyCalendar(null, null);

你的模板变成了

<td *ngFor="let dateObj of (monthlyCalendar$|async)?.dateObjList|keyvalue" class="phCell">
    <span *ngIf="dateObj.value.isPublicHoliday">PH</span>
</td>

但要实现这一点,可能需要进行大量架构更改。但是您应该阅读更多关于显示来自 Angular 官方文档的服务数据的信息

查看类似模拟https://stackblitz.com/edit/angular-pfhzrd的示例


编辑 3:

尽管选择了这样的数据结构,但很有可能您必须再次实现排序算法。

您的服务可以这样修改 [注意]:建议服务器应该发送一个 dataObjList 数组而不是一个对象。

return this.http.get('backend/getMonthlyCalendar.php', params).pipe(map((res: MonthlyCalendar) =>
  res.dateObjList = Object.values(res.dateObjList).reduce((list, date) => [...list, date], []);
  //res.dateObjList.sort((a,b) => a.id - b.id); //Note you might lose your sort coming from backend so you need to sort it based on some key or anything
  return res;
));

所以你的对象变成了如下所示的 dateObjList 数组


    "monthName": "September 2019",
    "dateObjList": [
        
            "dayOfWeek": "0",
            "isPublicHoliday": false,
            .............
        ,
        
            "dayOfWeek": "0",
            "isPublicHoliday": true,
            .............
        ,
        .....................
    ]
 

那么你的模板就不需要键值了:

<td *ngFor="let dateObj of (monthlyCalendar$|async)?.dateObjList" class="phCell">
    <span *ngIf="dateObj.isPublicHoliday">PH</span>
</td>

【讨论】:

浏览器提示如下错误: InvalidPipeArgument: '[object Object]' for pipe 'AsyncPipe' 我已经把服务和组件的源代码都包含了供大家参考。 似乎有人是对的,每月日历不是可观察的 @TheKNVB 检查更新的答案。虽然你应该直接将 observables 分配给这样的模板 stackblitz.com/edit/angular-pfhzrd 我不确定monthlyCalendar 是否是可观察的。我已将服务和组件添加到我的问题中以供参考。 它在错误的单元格中显示 PH。 “PH”应显示在单元格 14 中,但显示在单元格 6 中。【参考方案3】:

问题是您的数据不是可观察的,并且您使用的是异步管道。如果 monthlyCalendar?.dateObjList 可以观察到,则异步管道可以工作。

<td *ngFor="let dateObj of monthlyCalendar?.dateObjList" class="phCell">
    <span *ngIf="dateObj.isPublicHoliday">PH
    </span> 
</td>

否则你可以这样做。

<td *ngFor="let dateObj of (monthlyCalendar| async)?.dateObjList" class="phCell">
    <span *ngIf="dateObj.isPublicHoliday">PH
    </span> 
</td>

您可以从here 了解更多信息

【讨论】:

【参考方案4】:

试试这个方法

@Pipe(name: 'keys')
export class KeysPipe implements PipeTransform 
  transform(value, args:string[]) : any 
    let keys = [];
    for (let key in value) 
      keys.push(key: key, value: value[key]);
    
    return keys;
  



<td *ngFor="let dateObj of monthlyCalendar?.dateObjList|keys|async" class="phCell">
<span *ngIf="dateObj.isPublicHoliday">PH</span>

更多详情,请访问:How to display json object using *ngFor

【讨论】:

【参考方案5】:

ngFor 迭代数组而不是对象,你可以将数据分配给数组

dates = Object.values(data.dateObjList);

然后你可以对日期数组进行 ngFor。

如果您想使用异步管道,您需要创建一个将对象转换为数组的管道。

import  Pipe, PipeTransform  from '@angular/core';

@Pipe(
  name: 'toArray'
)
export class ToArrayPipe implements PipeTransform 
  transform(value: any): any 
    return Object.values(value);
  

并与它一起使用

*ngFor="let dateObj of (monthlyCalendar | async).dateObjList| toArray"

【讨论】:

我已经应用了keyvalue,但是你可以看到上面的错误信息。 monthlyCalendar 是可观察的吗?如果是这样,它应该按照命名标准命名为monthlyCalendar$,然后您需要使用 (monthlyCalendar | async).dateObjList | toArray 我不确定monthlyCalendar 是否可观察。我已将服务和组件添加到我的问题中。 我怎样才能使每月日历可观察? 而不是订阅您的服务,您将返回的 observable 分配给属性monthlyCalendar$ = this.monthlyCalendarService.getMonthlyCalendar(null, null) 然后您可以在该属性上使用异步管道。无需订阅。

以上是关于如何使用 *ngFor 循环我的数据对象?的主要内容,如果未能解决你的问题,请参考以下文章

如何在视图中仅循环一次对象(单个 *ngFor)?

ngFor - 按索引打印数组项

Angular 4 ngFor使用HTML循环遍历对象数组

如何在角度 2 中使用 ngFor 仅循环遍历数组到某些对象

如何在Angular中使用ngFor循环对象属性

如何根据对象属性字符串过滤“ngFor”循环内的项目