I have an application that consist of 3 components and 1 service:
- app.component. This holds my navbar etc. In the navbar there is a mat-select where you can choose the customer.
- orders.component. Lists the orders of the current selected customer.
- products.component. Lists the products of the current selected customer.
- customer.service. Some functions used for customers, but also a variable with the current selected customer
Desired state:
- When a customer is not selected and user navigates to orders or products, no call should be made to the API.
- When a customer is selected in the navbar while the user is on the orders/products page, the API should trigger to get the orders/products for the choosen customer.
- When the customer is changed, the API should be triggered to load the new customers orders/products
- When a customer is selected and the user navgiates to orders/products, the API should be called directly to get the data
Current state
- Currently I have added
customerObserver: Subject<boolean> = new Subject<boolean>();
to the customer.service. - When a customer is selected in the navbar (app.components), it calls a function in customer.service which in the end will do
this.customerObserver.next(this.customer);
- In both the orders and products components, I do this:
ngOnInit(): void {
if (this.customerService.customer) {
this.loadOrders();
}
this.customerService.customerObserver.subscribe(customer => {
if(customer) {
this.loadOrders();
}
});
}
The functionality worked fine, but I realised that as I do never unsubscribe this messes up the application totally as it creates a lot of subscribes when navigating around. I added unsubscribe in orders (see snippet below), but then when I am navigating around I get the following error: ObjectUnsubscribedErrorImpl {message: 'object unsubscribed', name: 'ObjectUnsubscribedError'}
ngOnDestroy() {
this.customerService.customerObserver.unsubscribe();
}
I have tried to read up on subscribe for a while now but is stuck how to proceed.. Any tips on how to arrange to achieve the desired state?
CodePudding user response:
You can emit data from the service to the components observers.
private customerSubject = new Subject<number>();
customerSelectedAction$ = this.customerSubject.asObservable();
products$ = this.customerSelectedAction$.pipe(
switchMap(customerId=>this.http.get<Product[]>(`${this.url}?customerId=${customerId}`))
.pipe(
tap(data => console.log(data)),
catchError(this.handleError)
));
orders$ = this.customerSelectedAction$.pipe(
switchMap(customerId=>this.http.get<Orders[]>(`${this.url}?customerId=${customerId}`))
.pipe(
tap(data => console.log(data)),
catchError(this.handleError)
));
selectedCustomerChanged(customer: Customer): void {
this.customerSubject.next(customer.id);
}
After that, you subscribe to the products$
or orders$
observables depending on which component you are. And use the selectedCustomerChanged(customer: Customer)
method from navbar.
CodePudding user response:
You can manage the subscription in each component in a subscription array, you can look here for the best answer: Unsubscribing an array of subscriptions
Another tip, you can use a private BehaviorSubject (and a public asObservable prop), starting with null, so you can remove the customerObserver: Subject<boolean> = new Subject<boolean>();
altogether.
You just subscribe to that observable and load orders if the customer is not null (you can also use distinct until changed).