Jase
Jase

Reputation: 91

Can I reuse an Observable inside a function?

My question is simple as a I've read many articles talking about Observables, Subscriptions and, of course, plenty ways to unsubscribe.

I have this code

merge(this.sort.sortChange, this.paginator.page)
            .pipe(
                startWith({}),
                switchMap(() => {
                    this.isLoadingResults = true;
                    return this.ProductionService.get__customers()
                }),
                map((data: QueryResult<CustomerDTO>) => {
                    this.isLoadingResults = false;
                    this.items__length = data.totalCount;
                    return data.records;
                }),

            ).subscribe((data: CustomerDTO[]) => {
                this.d__source = data;
            });

This is inside a function called getDataSource()

It's inside a function because I want to manually refresh this dataSource (d__source) when I close a flyout. Let's imagine in my flyout adds a Customer and I want to close this flyout and refresh my dataSource to show the user the brand new created Customer.

My problem is, as you can see I have a sortChange and a MatPaginator but I have encountered and error I cannon solve. I want to reuse this Observable for sorting or paginating. But when I close the flyout and call the getDataSource function again, the function is subscribing again and gets twice the Customers resulting in a double HTTP GET call to my API.

If I unsuscribe in the subscribe part like

const subscription = merge(this.sort.sortChange, this.paginator.page)
            .pipe(
                startWith({}),
                switchMap(() => {
                    this.isLoadingResults = true;
                    return this.ProductionService.get__customers()
                }),
                map((data: QueryResult<CustomerDTO>) => {
                    this.isLoadingResults = false;
                    this.items__length = data.totalCount;
                    return data.records;
                }),

            ).subscribe((data: CustomerDTO[]) => {
                this.d__source = data;
                subscription.unsubscribe();
            });

I cannot use the sort function.

Any idea would be appreciated, I'm stuck in this for a while.

Thanks beforehand!

Upvotes: 0

Views: 789

Answers (1)

Francisco Santorelli
Francisco Santorelli

Reputation: 1338

An ugly solution would be to make 'const subscription' component-wide this.subscription and check it before subscribing if (!this.subscription.closed) this.subscription.unsubscribe() and then call again your subscription. But this is an ugly approach and there might be better alternatives.

Another would be to simply next what you already got. If on flyout close() you want to refresh your d__source, and this is dependent on this.sort.sortChange, this.paginator.pagewhich I assume both are Subjects, and you have control over them, can you just next their current value? to just refresh the call... still kinda ugly but better than the previous one.

One last attempt that I would actually do, is to create a BehaviorSubject which is what triggers the d__source update... like so

private updateHandler = new BehaviorSubject({}); // notice it already starts with a value

...


    this.updateHandler // this onInit, and only once
        .pipe(
            switchMap(() => {
                this.isLoadingResults = true;
                return this.ProductionService.get__customers()
            }),
            map((data: QueryResult<CustomerDTO>) => {
                this.isLoadingResults = false;
                this.items__length = data.totalCount;
                return data.records;
            }),

        ).subscribe((data: CustomerDTO[]) => {
            this.d__source = data;
            subscription.unsubscribe();
        });

then in your component you can make your previous observables to next this behavior subject:

merge(this.sort.sortChange, this.paginator.page).subscribe(() => this.updateHandler.next());

this way they are doing the same.

You can control their closing with another Subject, let's call it 'this.destroy$'

private destroy$ = new Subject();

and then you can just pipe to all takeUntil(this.destroy$)(flavorfull), and add this onDestroy

ngOnDestroy() {
  this.destroy$.next();
  this.destroy$.complete();
}

and finally, wherever you control your flyout closing, you simply this.updateHandler.next() and that should do the job.

Upvotes: 1

Related Questions