0

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 Answer 1

0

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);
      })
    );
  }
}
3
  • I don't understand why you switched from "subscribe" in my approach to "pipe" + "tap" in yours, but now I cannot debug properly inside "tap" lambda (debugger doesnt enter this location - I can only stop on breakpoints after "tap" and "pipe" are finished). I used "usersList$() | async" in HTML and still nothing happens in view after processing response body from REST call. Is my approach with using Observable, BehaviorSubject and async pipe correct? Or can it be done in some other, more accessible way?
    – Dahaka
    Commented Oct 21, 2020 at 22:08
  • You need to add private userService: UserService in your mwl-calendar-month-view. Once it's done you can in that component use userList$(). Then you also need to use alteast once anywhere in your app this.getAll().subcribe() to load the data in your service. If I use pipe + tap, it's because when I use getAll() I subcribe and it allows me to do action there too. If you don't subscribe to your observable, you'll not recieve data. If you're using Observable, BehaviorSubject and async pipe it should work fine.
    – Leccho
    Commented Oct 22, 2020 at 13:41
  • I'd suggest you take the time to read the official doc to understand how observable works. rxjs-dev.firebaseapp.com/guide/overview
    – Leccho
    Commented Oct 22, 2020 at 13:44

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.