Angular observable with async pipe

1k views Asked by At

I'm creating my first Angular app and I'm struggling with observables (surprise, surprise). I have this in my HTML tag:

  <mwl-calendar-month-view
    *ngSwitchCase="'month'"
    [viewDate]="viewDate"
    [events]="observable | async"
    [activeDayIsOpen]="true"
  >

where I try to use async pipe with my observable. In order to update value of my observable I make some REST calls (after user clicks a button) and here is how I handle the click:

  observable: Observable<CalendarEvent[]>;
  behaviourSubject: BehaviorSubject<CalendarEvent[]> = new BehaviorSubject([{
    title: 'title12345',
    start: new Date(),
    end: new Date()
  }]);

(...)

  onCreateCalendarEventClick() {
    let calendarEvent: CalendarEvent;
    calendarEvent = {
      title: (document.getElementById('calendarEventName') as HTMLInputElement).value,
      start: new Date((document.getElementById('calendarEventStartDate') as HTMLInputElement).value),
      end: new Date((document.getElementById('calendarEventEndDate') as HTMLInputElement).value),
      color: undefined
    };
    let calendarEventApi: CalendarEventApi;
    let calendarEventColor;
    if (calendarEvent.color) {
      calendarEventColor = calendarEvent.color;
    }
    calendarEventApi = {
      title: calendarEvent.title,
      start: ToApiMapper.formatDateTimeToApi(calendarEvent.start),
      end: ToApiMapper.formatDateTimeToApi(calendarEvent.end),
      color: ToApiMapper.mapColorToApi(calendarEventColor)
    };
    this.calendarService.saveCalendarEvent(calendarEventApi).subscribe( () => {
      this.fetchCalendarData();
    }, error => {
      this.alertService.displayAlert('saving calendar event failed: ' + error);
    });
  }


  private fetchCalendarData() {
      const calendarEvents: CalendarEvent[] = [];
      this.calendarService.fetchCalendarData(this.userService.getLoggedUser()).subscribe(calendarEventsApi => {
        calendarEventsApi.forEach(calendarEventApi => {
          const calendarEvent: CalendarEvent = {
            title: calendarEventApi.title,
            start: new Date(calendarEventApi.startDate),
            end: new Date(calendarEventApi.endDate),
            color: {
              primary: calendarEventApi.eventColour,
              secondary: calendarEventApi.eventColour
            }
          };
          calendarEvents.push(calendarEvent);
        });
        this.behaviourSubject.next(calendarEvents);
        this.observable = this.behaviourSubject.asObservable();
      });
  }

I was trying to reproduce behaviour described here: https://malcoded.com/posts/angular-async-pipe/ How I understand what's going on in my code: after getting response body from REST call I update next value which behaviourSubject will have. Then I create a new observable with the next value already set to my desired response. Then I update my observable in HTML. HTML should be reloaded (because it listens for value change and value of my observable just changed). New value from my REST call should be visible in HTML. What am I missing?

1

There are 1 answers

3
Leccho On

I believe you're trying to pass your observable via an input and I'm not sure that change will be detected and send. What I would suggest is to store your observable into your service so it's possible to get it via dependency injection.

Here's an example showing you how to use a subject (I suggest BehaviorSubject). You can then inject your service in your calandar month view component and use your observable there. (FYI: standard for an observable is to add a $ at the end so you know it's one)

@Injectable({
  providedIn: 'root'
})
export class UserService {

  private userList: User[];
  private userListSubject: BehaviorSubject<User[]> = new BehaviorSubject<User[]>([]);

  public constructor(private http: HttpClient) { }

  public usersList$(): Observable<User[]> {
    return this.userListSubject.asObservable();
  }

  public getAll$(): Observable<User[]> {
    return this.http.get<User[]>('/api/users').pipe(
      tap(userList => {
        this.userList = userList;
        this.userListSubject.next(this.userList);
      })
    );
  }
}