0

I am learning to use a reactive approach to my angular application. I'm able to add new items to an observable (array).

In my service, I am doing this to get all activities:

// activity.service.ts

private activityCreatedSubject = new Subject<IActivity>();

public activityCreatedAction$ = this.activityCreatedSubject.asObservable();

public storedActivities$: Observable<IActivity[]> = this.httpClient
        .get<IActivity[]>(`${this.baseURL}/v1/activities`)
        .pipe(
            catchError((err) => {
                return throwError(err);
            })
        );

...

public activities$ = merge(
        this.storedActivities$,
        this.activityCreatedAction$
    ).pipe(
        tap((data) => console.log('activities$: ', data)),
        scan(
            (acc, value) =>
                value instanceof Array ? [...value] : [...acc, value],
            [] as IActivity[]
        ),
        shareReplay(1)
    );

storedActivities$ is my http request to get all activities that are saved to my database.

public storedActivities$: Observable<IActivity[]> = this.httpClient
        .get<IActivity[]>(`${this.baseURL}/v1/activities`)
        .pipe(
            catchError((err) => {
                return throwError(err);
            })
        );

Using | async in my template, i'm able to list all activities and add new ones. When I add a new activity, it is reflected in my UI immediately which is great!

Now I am trying to leverage what I've got to handle a delete request.

Here is what my remove method looks like in my service:

remove(activity: IActivity): Observable<any> {
        const activityId = activity.id;

        return this.httpClient
            .delete<IActivity>(`${this.baseURL}/v1/activities/${activityId}`)
            .pipe(
                // TODO: filter activities to omit deleted.
                // this.activities$.filter(a => a.id !== activityId)
                catchError((err) => {
                    throw `Error deleting activity: ${err.error.message}`;
                })
            );
    }

I'm able to successfully remove the activity from my database, but refreshing my ui is throwing me. I have tried different versions of filter without much luck.

I have tried making my activities$ into a BehaviorSubject, but does that take away from being reactive? I think I would have to implement some sort of getAllActivites method or something then call that from my component.

What is the ideal way to update the ui using a reactive approach? I think since activities$ is an observable, that is causing my problems, but I'm not sure how to get around that and still maintain a reactive flow.

EDIT

Here is how I am adding new activities. I am saving the activity to my database (Postgres) and updating the ui.

// activity.service.ts

create(activity: IActivity): Observable<IActivity> {
        return this.httpClient
            .post<IActivity>(`${this.baseURL}/v1/activities`, activity)
            .pipe(
                tap((response) => {
                    this.activityCreatedSubject.next(response);
                }),
                catchError((err) => {
                    throw `Error creating activity: ${err.error.message}`;
                })
            );
    }

I'm able to update my list of activities by using merge and scan. It's probably painfully obvious I don't really quite grasp what I'm doing yet.

3
  • Can you share your code for adding a new activity, since that is working for you? Commented Nov 21, 2022 at 23:27
  • Sure thing! I've updated my question to include that now. Commented Nov 21, 2022 at 23:46
  • You might be able to adapt this answer to fit your needs. Commented Nov 23, 2022 at 22:48

1 Answer 1

1

I feel like there's multiple ways to approach this, but the reason deleting an activity isn't reflected in your situation is because the activities$ observable isn't aware of when you delete an activity.

I personally wouldn't complicate it too much and do something like the code below. And yes, using a BehaviourSubject is still reactive, if you use the Observable stream in your components. Whenever you want to emit a new value, you simply call next on the underlying BehaviourSubject and it will be reflected everywhere the activities$ observable is used (e.g. with async pipe).

private activities = new BehaviourSubject<IActivity[]>([]);
public activities$: Observable<IActivity[]> = this.activities.asObservable();

loadActivities(): void {
    this.httpClient
        .get<IActivity[]>(`${this.baseURL}/v1/activities`)
        .pipe(tap(x => this.activities.next(x));
}

create(activity: IActivity): void {
    this.httpClient
        .post<IActivity>(`${this.baseURL}/v1/activities`, activity)
        .pipe(tap(x => this.activities.next([x, ...this.activities.getValue()])))
}

delete(activity: IActivity): void {
    this.httpClient
        .delete<IActivity>(`${this.baseURL}/v1/activities/${activity.id}`)
        .pipe(tap(x => this.activities.next([...this.activities.getValue().filter(y => y.id !== activity.id])))
}

Once you get the hang of rxjs you might want to dive into learning a state management framework like NGRX. I won't go into detail here but they basically do what you do behind the scenes, by dispatching actions (e.g. deleting an activity) and updating state based on those actions.

Sign up to request clarification or add additional context in comments.

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.