1

I'm trying to populate a calendar based on the response I receive from a restful service (currently returning a list of dates).

I'm calling the restful service method from the ngInit() method of the component.

 ngOnInit() {
    this.refreshCalendar(); 

    for (var calendarEntryShortvar of this.calendarEntriesShort) {
      console.log("cal entry short from ngInit: " + calendarEntryShortvar.start);
    }

  }

The above loop to print the contents fails due to an undefined length.

Here is the code for this.refreshCalendar();

 refreshCalendar(){
    this.calendarService.retrieveAllCalendarEntriesShort('test')
    .subscribe (
      response => {
        console.log("printing response")
        console.log(response)
        this.calendarEntriesShort = response; 

        for (var calendarEntryShortvar of this.calendarEntriesShort) {
          console.log("cal entry short: " + calendarEntryShortvar.start);
          this.calendarEntry =  new CalendarEntry(calendarEntryShortvar.start, calendarEntryShortvar.start, 
          'title event 1', this.colors.redx, this.actions);
          console.log("pushing calendar entry")
          this.events.push(this.calendarEntry);     
        }

       }
    )

  }

The above code successfully prints the contents of the array, meaning it's executing after it's received the response from service.

Here's the code for of the service call:

retrieveAllCalendarEntriesShort(username) {

console.log("retrieveAllCalendarEntries"); 

return this.http.get<CalendarEntryShort[]>(`${CALENDER_API_URL}/users/${username}/calendarentries`);

}

Needless to say, the calendar's HTML is getting rendered before the calendar date array is populated. Here's the HTML component:

<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet">

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"/>
<angular-calendar-year-view  [events]="events"  [viewDate]="viewDate" [customTemplate]="Customtemplate" ></angular-calendar-year-view>
<ng-template #Customtemplate>
        My custom templatex
</ng-template>
<angular-calendar-year-view  
       [themecolor]="themecolor" 
       [events]="events"  
       [viewDate]="viewDate"  
       [nothingToshowText]="nothingToshowText"
       (eventClicked)="eventClicked($event)" 
       (actionClicked)="actionClicked($event)" >
</angular-calendar-year-view>

Somehow I need to get Angular to pause until the response is received. Is this just a question of making it a synchronous call? I'm not sure why this isn't working.

1
  • Do you see any errors in the console? Commented Jun 20, 2019 at 22:36

2 Answers 2

1

There's several ways to handle asynchronous calls in Angular. My first option is typically to use the async pipe whenever possible (so there's no need to worry about unsubscribing). Something like this should work for you:

Update your api service call to do a map on the observable instead and add a variable for it:

calendarResults$: Observable<CalendarEntry[]>;

refreshCalendar() {
  this.calendarResults$ = this.calendarService.retrieveAllCalendarEntriesShort('test')
      .pipe(map((response: any[]) => {
        return response.map(calendarEntryShortvar => {
          return new CalendarEntry(calendarEntryShortvar.start, calendarEntryShortvar.start, 'title event 1', this.colors.redx, this.actions)
        });
      }));
}

Then you can use the async pipe in an *ngIf your template (with the bonus of being able to display some loading animation):

<ng-container *ngIf="calendarResults$ | async as calendarResults; else loading"
    <angular-calendar-year-view  [events]="calendarResults"  [viewDate]="viewDate" [customTemplate]="Customtemplate" ></angular-calendar-year-view>
    // your other code dependent on this async data
</ng-container>
<ng-template #loading>
    Loading...
</ng-template
Sign up to request clarification or add additional context in comments.

8 Comments

This makes sense. But the updated function doesn't get into the "pipe(map..." section. I can tell from console logging that it's calling the service....Here is the code - what do you think? refreshCalendar() { this.calendarResults$ = this.calendarService.retrieveAllCalendarEntriesShort('test') .pipe(map((response: any[]) => { return response.map(calendarEntryShortvar => { return new CalendarEntry(calendarEntryShortvar.start, calendarEntryShortvar.start, 'title event 1', this.colors.redx, this.actions) }); })); }.
That looks good to me overall except for the unnecessary space before the pipe and possibly a missing semicolon. Here's a stackblitz example. Check out the hello.component.ts. There's a simulated delay of 2 seconds in my mock service call.
Thanks - I used your mock service and html to verify the subscribe code was working. I thought I needed to subscribe to the calendarResults$ and add the calendarEntries from there. However, that piece still isn't working - it's not highlighting the dates that I pass into it. Here's the code that does it: asyncDisplayCall() { // called from ngInit this.calendarResults$.subscribe( calendarEntries => { for (var calendarEntry of calendarEntries) console.log("asyncDisplayCall: " + calendarEntry.start); this.events.push(calendarEntry) }); }
Sounds like there's some requirement that I can't see with the limited context. If you do actually need to push to that events variable rather than creating a new array you can add an extra step to the map pipe's function.
refreshCalendar() { this.calendarResults$ = this.calendarService.retrieveAllCalendarEntriesShort('test').pipe(map( (response: any[]) => { let mappedResults = response.map(calendarEntryShortvar => { return new CalendarEntry(calendarEntryShortvar.start, calendarEntryShortvar.start, 'title event 1', this.colors.redx, this.actions); }); this.events.concat(mappedResults); return mappedResults; } )); }
|
0

You have to wait for the response before dealing with it. Plus, you can wait for it before adding the html part that deal with it to the view.

Put the api call into a service, it's the best pratice:

In your service

// service.ts
refreshCalendar() {
    return this.calendarService.retrieveAllCalendarEntriesShort('test');
}

In your component:

events: any = [];

constructor(private service: Service) {}

ngOnInit() {
    this.service.refreshCalendar()
    .subscribe(response => {
        this.calendarEntriesShort = response;
        for (var calendarEntryShortvar of this.calendarEntriesShort) {
          console.log("cal entry short from ngInit: " + calendarEntryShortvar.start);
          for (var calendarEntryShortvar of this.calendarEntriesShort) {
              this.calendarEntry =  new CalendarEntry(calendarEntryShortvar.start, 
             calendarEntryShortvar.start, 
             'title event 1', this.colors.redx, this.actions);
             this.events.push(this.calendarEntry);     
           }
        }
    });
}

And in your view

<div *ngIf="events.length > 0">
    <angular-calendar-year-view  [events]="events"  [viewDate]="viewDate" 
    [customTemplate]="Customtemplate" >
    </angular-calendar-year-view>
    <ng-template #Customtemplate>
        My custom templatex
    </ng-template>
    <angular-calendar-year-view  
        [themecolor]="themecolor" 
        [events]="events"  
        [viewDate]="viewDate"  
        [nothingToshowText]="nothingToshowText"
        (eventClicked)="eventClicked($event)" 
        (actionClicked)="actionClicked($event)" >
    </angular-calendar-year-view>
</div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.