Как я могу использовать * ngFor для зацикливания моего объекта данных? - PullRequest
0 голосов
/ 20 сентября 2019

Это мой объект «monthCalendar»:

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

Это мой component.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);
      },
    );
  }
.........

Это мой monthCalendarService.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>
...........

Я хочу показать тег span, когда атрибут dateObj.isPublicHoliday равен true.

Однако браузер запрашивает следующее сообщение об ошибке:

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'

Как я могу заставить это работать?

Ответы [ 5 ]

2 голосов
/ 20 сентября 2019

Ну, проблема в том, что ваши данные не являются наблюдаемыми, и вы используете асинхронный канал.Асинхронная труба работает, если бы 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>

Подробнее об этом можно прочитать по здесь

1 голос
/ 20 сентября 2019

Попробуйте следующим образом

@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>

Подробнее, пожалуйста, посетите: Как отобразить объект json, используя * ngFor

1 голос
/ 20 сентября 2019

Потеря асинхронного канала, ваш ежемесячный календарь не является асинхронным.Вы назначаете его вручную после подписки.

pipe keyvalue возвращает и объект с 2 реквизитами key и value, используя атрибут value, перемещается к вашему объекту.

Проверьтеvalue атрибут из канала keyvalue

dateObj.value.isPublicHoliday

Таким образом, ваш код становится окончательно

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

Редактировать 2:

Правильный путь, рекомендованный Angular

Если вы хотите сделать это как наблюдаемое в вашем компоненте

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>
1 голос
/ 20 сентября 2019

ngДля итераций массивов, а не объектов, вы можете присвоить данные массиву

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"
0 голосов
/ 20 сентября 2019

Наконец, я объединяю все ваши идеи, чтобы исправить проблему,

Я изменяю component.ts для изменения объекта monthCalendar, чтобы он был наблюдаемым.

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

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

И затем я создаютруба под названием 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: не может прочитать свойство dateObjList из null».
Наконец, следующеевеб-страница рассказывала мне, почему оператор безопасной навигации не работает с асинхронным каналом.

Оператор безопасной навигации по наблюдаемой асинхронной трубе и каналу в Angular 2

Итак,окончательная версия toArray pipe выглядит следующим образом:

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);
    }
  }
}

Спасибо за вашу помощь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...