Home > Software design >  Angular *ngFor with ambiguous dependency on another *ngFor
Angular *ngFor with ambiguous dependency on another *ngFor

Time:06-16

I have a Angular html template that has a ambiguous dependency between an observable and a piped function. The output of the functions are the events and schedule$ arrays, respectively.

Problem, the event array does not load into the html template unless the schedule$ array is also loaded with *ngFor, even when the function for schedule$ (getCalendar()) is run with ngOnInit()

Questions:

  1. I no longer have a functional need for schedule$ so want to completely omit that from my code. Currently, I can't do that because the event array's items disappear also. How can I remove the schedule$ *ngFor without impacting event *ngFor?
  2. What is the root cause of this dependency? Aren't both functions independent?
<div >
  CalendarEvent Div from getEvents()
<ul *ngFor="let evnt of events">
  <li>Title: {{evnt.title}}</li>
  <li>Start: {{evnt.start}}</li>
  <li>End: {{evnt.end}}</li>
  <li>Creator: {{evnt.created_by}}</li>
</ul>
</div>

<div >
  Schedule Div from  getCalendar()
<ul *ngFor="let event of schedule$ | async | slice:0:1">
  <li>Title: {{event.title}}</li>
  <li>Start: {{event.start}}</li>
  <li>End: {{event.end}}</li>
</ul>
</div>

My component module where the functions are defined is:

schedule$!: Observable<Schedule[]>;
events: CalendarEvent[] = [];

ngOnInit(): void {
    this.getCalendar(),
    this.getEvents()
  }

public getEvents(): any {
    this.apiService.getEvent()
    .subscribe(
    (data: any)  => {
      this.events = data;
      for (let event of this.events) {
        event.start = new Date(event.start);
        event.end = new Date(event.end);

      }
    },
    (err:any) => console.error(err),
    () => console.log('done loading events')
  );
}

public getCalendar(): any {
    this.schedule$ = this.apiService.getCalendar().pipe(tap((dataset: any) =>console.log('getCalendar()', dataset)))
  }

The API end points used in these functions is the same

public getCalendar(): Observable<Schedule[]> {
    return this.http.get<Schedule[]>(`${this.API_URL}/schedule/`,
            {
              responseType: 'json',
              headers: new HttpHeaders().set('Authorization', `Bearer ${this.auth.accessToken}`)
            });
  }

public getEvent(): Observable<CalendarEvent[]> {
    return this.http.get<CalendarEvent[]>(`${this.API_URL}/schedule/`,
            {
              responseType: 'json',
              headers: new HttpHeaders().set('Authorization', `Bearer ${this.auth.accessToken}`)
            });
  }

The interface are

export interface CalendarEvent<MetaType = any> {
    id?: string | number;
    start: Date;
    end: Date;
    title: string;
    color?: EventColor;
    actions?: EventAction[];
    allDay?: boolean;
    cssClass?: string;
    resizable?: {
        beforeStart?: boolean;
        afterEnd?: boolean;
    };
    draggable?: boolean;
    meta?: MetaType;
    isEditable?: boolean;
    created_by?: string;
    event_skaper?: string;
    event_booker?: string;
    
}

export interface Schedule {
    id: number;
    title: string;
    start: Date;
    end: Date;
    created_by: string;
    event_skaper: string;
    event_booker: string;
  }

CodePudding user response:

First of all, this is weird that you have 2 of the same API calls in service that should return different types without any pipes. Second, I recommend you transform the simple events array to Observable or BehaviorSubject.

so your code will look like this:

public events: BehaviorSubject<CalendarEvent[]> = new BehaviorSubject([]);

ngOnInit(): void {
    this.getEvents();
}

public getEvents(): void {
    this.apiService.getEvent()
    .subscribe((data: any)  => {
      // do what you need or want with data
      const mappedData = data.map((event: any) => {
        event.start = new Date(event.start);
        event.end = new Date(event.end);
        return event;
      });
      this.events.next(mappedData);
    },
    (err:any) => console.error(err),
    () => console.log('done loading events')
  );
}

after this, simply subscribe to events in the template

<div >
<ul *ngFor="let evnt of events | async">
  <li>Title: {{evnt.title}}</li>
  <li>Start: {{evnt.start}}</li>
  <li>End: {{evnt.end}}</li>
  <li>Creator: {{evnt.created_by}}</li>
</ul>
</div>

CodePudding user response:

I fixed the issues following: https://blog.angular-university.io/angular-2-rxjs-common-pitfalls/.

  • Related