简体   繁体   中英

Angular *ngFor with ambiguous dependency on another *ngFor

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 class="boxed">
  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 class="boxed">
  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;
  }

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 class="boxed">
<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>

I changed the code several times and am now using the map function to get the data from the API having tried to subscribe in the ngOnInit() and the constructor(); the same issue persists where the template does not render any *ngFor loops at page load. Again the array is presented with console.log() immediately, but it does not show up in the template, until I interact with the template.

I am pretty sure I am not subscribing to the Observable correctly, any tips would help!

getEventers() { 
    return this.apiService
    .getEvent()
    .pipe(map(
    (data: any) => this.events = data
    ))
  };
 ngOnInit(): void {

    this.getEventers().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(this.events)
    )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM