Reputation: 1265
I have two components: NewItemComponent and ListComponent. When I create new item inside corresponding component I notify ListComponent so it can refresh it's data model:
export class NewItemComponent implements OnInit {
constructor(private itemService: ItemService, private notificationService: NotificationService) {
}
ngOnInit() {
}
createNewItem(item: Item) {
this.itemService.persist(item).subscribe((response: Item) => {
console.log(response);
this.notificationService.notifyNewItemHasBeenCreated(response);
});
}
}
export class ListComponent implements OnInit {
items: Item[];
constructor(private listService: ListService, private notificationService: NotificationService) {
}
ngOnInit() {
this.loadItems();
this.notificationService.item$.subscribe((item) => {
if (item != null) {
this.loadItems();
}
})
}
loadItems(){
this.istService.getItems().subscribe((data: Item[]) => {
this.items= data;
console.log(this.items);
});
}
}
@Injectable({
providedIn: 'root'
})
export class NotificationService {
private _item: BehaviorSubject<Item> = new BehaviorSubject<Item>(null);
public item$ = this._item.asObservable();
constructor() {
}
notifyNewItemHasBeenCreated(item: Item) {
this._item.next(item);
}
}
What makes me worried is that loadItems() calls subscribe() multiple times. Is this ok or there is better way to refetch items based on notification?
loadItems(){
this.listService.getItems().subscribe((data: Item[]) => {
this.items= data;
console.log(this.items);
});
}
ListService returns Observable:
export class ListService {
basePath = 'my-api.com';
apiPath = "item";
constructor(private httpClient: HttpClient) {
}
getItems(): Observable<Item[]> {
return this.httpClient.get<Item[]>(this.basePath + '/' + this.apiPath);
}
}
Thanks in advance and any help will be really appreciated.
Upvotes: 3
Views: 29366
Reputation: 2173
Edit:
After looking around I found these posts:
Is it necessary to unsubscribe from observables created by Http methods?
Prevent memory leaks in Angular 2?
The comments and answers explain HttpClient.get()
subscriptions clean up after themselves so you don't need to unsubscribe from them. That means calling .subscribe()
on them multiple times is fine.
Upvotes: 1
Reputation: 99
loadItems()
calls subscribe
only once.
What actually happens is that you call loadItems()
multiple times in notificationService.item$
's subscription.
If you need NotificationService
only for this purpose I would suggest you little refactoring.
new-item.component.ts
export class NewItemComponent {
constructor(private itemService: ItemService, private listService: ListService) {
}
createNewItem(item: Item) {
this.itemService.persist(item).subscribe((response: Item) => {
this.listService.addItem(item);
});
}
}
list.service.ts
export class ListService {
basePath = 'my-api.com';
apiPath = 'item';
private itemsSubject: BehaviorSubject<Item[]> = new BehaviorSubject<Item[]>([]);
private items$: Observable<Item[]>;
constructor(private httpClient: HttpClient) {
}
getItems(): Observable<Item[]> {
const http$ = this.httpClient.get<Item[]>(this.basePath + '/' + this.apiPath);
return combineLatest(http$, this.items$).pipe(
map(([httpResponse: Item[], localItems: Item[]]) => httpResponse.concat(localItems)),
);
}
addItem(item: Item) {
this.itemsSubject.next([...this.itemsSubject.value, item]);
}
}
Upvotes: 1
Reputation: 137
What you're doing right now is:
So in a nutshell, for every new item you make you make 2 requests to the server and get 2 responses.
You can see that there's alot of redundancy here. Let's try to simplify things:
Now you only make one request each time instead of two. And you don't have to deal with so many Observables anymore.
Upvotes: 0
Reputation: 2455
If you subscribe call multiple time, than unsubscribe it when you component destroy.
Do change in your component like this :
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
export class ListComponent implements OnInit, OnDestroy {
items: Item[];
constructor(private listService: ListService, private notificationService: NotificationService) {
}
subscriptions: Subscription[] = [];
ngOnInit() {
this.subscriptions.push(this.loadItems());
this.subscriptions.push(this.notificationService.item$.subscribe((item) => {
if (item) {
this.loadItems();
}
}));
}
ngOnDestroy() {
this.subscriptions.forEach(x => x.unsubscribe());
}
loadItems(){
this.istService.getItems().subscribe((data: Item[]) => {
this.items= data;
console.log(this.items);
});
}
}
Upvotes: 1
Reputation: 2454
As an experiment, if you do the following:
this.httpClient.get("<some url>")
.subscribe({
next: () => {
console.log("received response")
},
error: err => {
console.log("error occurred")
},
complete: () => {
console.log("subscription completed")
},
})
You should see:
received response
subscription completed
This means that the observable for each web request is being completed after the request finishes, and therefore it is safe to not unsubscribe, as this is automatically done at the completion of an observable.
Upvotes: 9