Reputation: 41
Everywhere you look you see subscribe here, subscription there, unsubscribe here, unsubscribe there
. I ask, what do we NEED all this boilerplate code for, if its possible to use a service properties in components directly.
@injectable()
export class MyService {
public logItems: Log[] = [];
getLatestLogItems() {
return this.httpService.GetLatestLogItems() // some http service
.subscribe(
(items) => {
this.logItems = items;
},
...
);
}
}
and then in component:
@Component({
selector: 'app-logs',
template: `<ul>
<li *ngFor="let log of myService.logItems"> {{ log | json}} </li>
</ul> `
})
export class MyComponent implements OnInit {
constructor(public myService: MyService) { }
ngOnInit() {
this.myService.getLatestLogItems();
}
if you want to refresh the data, simply invoke getLatestLogItems()
again.
what am I missing here ? why is Subjects required at all?
Upvotes: 2
Views: 778
Reputation: 6826
I wouldn’t say your code is wrong. Http requests automatically unsubscribe so there shouldn’t be a memory leak. You also have control over when the request is happening. Code wise, I don’t see any issues but maybe I’m missing something.
I still would not use this pattern. My argument is that services will begin to define your components. That feels kinda weird. You’re also limiting your use of RxJs. Each component should handle the api call as they see fit for that specific component.
However, I don’t see any loss of capability but I do see hacks creeping into your code. So it’s not terrible but it’s a little selfish on the services part. Let the components decide how they consume the data.
Upvotes: 0
Reputation: 4189
Depending on your use case, generally I think it's a baad idea to do the way you mention above:
Potential Memory leak: When will the unsubscribe happen? By calling getLatestLogItems
again and again, it may cause memory leak. Though in this case it might not because httpClient handled that, but other observable method might.
Value update is not track: Your value of myService.logItems
is not track. If you have multiple same components in same page (e.g. ngFor) or using the component in other pages + your service is singleton (most of the case it is), you may end up with unexpected result. See the simple demo here: https://stackblitz.com/edit/so-rxjs-test. The value is not updated in template correctly.
Separation of concerns: Service should be private in component. You are exposing service to public. This is not a good practice. It's because service (another layer) is used for code separation. If you are doing the way above, might as well you write the getLatestLogItems
function in component straight away.
If you do not like the boilerplate code, maybe you can consider convert all observable
to promise
by calling .toPromise
. This way, you don't need to .subscribe
and .unsubscribe
but you need to .then
or async await
...
I hope this provide some reasoning behind the boilerplate. If you are using other frameworks like React or Vue or even just jquery without rxjs, item no 4 is mostly unavoidable (need to .then
or async await) for all api calls.
Upvotes: 1
Reputation: 5889
To get around this, you need to understand the Observable
and Observer
concepts.
Observables
do not return anything, Instead they emit items so Observers
can subscribe. In this way the Observer
stands ready to react appropriately at whatever future time the Observable does so.
For further reading please refer to ReactiveX documentation.
Yes, subscribing and unsubscribing can sometimes seem frustrating, but in your code:
getLatestLogItems() {
return this.httpService.GetLatestLogItems().subscribe((items) => {
this.logItems = items;
});
}
the getLatestLogItems
is returning nothing.
Upvotes: 0
Reputation: 7231
You should subscribe in component and return observable from service
@injectable()
export class MyService {
public logItems: Log[] = [];
getLatestLogItems(): Observable<any> {
return this.httpService.GetLatestLogItems() // some http service
}
}
In Component :
ngOnInit() {
this.myService.getLatestLogItems().subscribe(response => {console.log(response)})
}
Upvotes: 0