angularpioneer
angularpioneer

Reputation: 41

Angular - why Subscriptions ? why NOT to use service's properties in components directly?

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

Answers (4)

christo8989
christo8989

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

Jecfish
Jecfish

Reputation: 4189

Depending on your use case, generally I think it's a baad idea to do the way you mention above:

  1. 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.

  2. 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.

  3. 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.

  4. 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

Hamid Asghari
Hamid Asghari

Reputation: 5889

ReactiveX Observable

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

Akj
Akj

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

Related Questions