1

I have a service which provides me an observable through a http get request and a following map operation. In my component I subscribe to the observable in the ngOnInit method. As I have a pagination functionality I would like to fire another request to the service when the user clicks on another page of the paginator. How can I achieve this? Do I have to subscribe to the service in a new method again or is it possible to initialize an observable within the component so I can use it in any method?

export class ListItemsComponent implements OnInit {
  private _listItems = [];
  page = 1;
  constructor(private _listItemsService:ListItemsServiceService) { }

  ngOnInit() {
    this._listItemsService.getListItems (this.page)
     .subscribe(listItems => this._listItems = listItems);
  }

  pageChange(page){
     this._listItemsService.getListItems (this.page)
     .subscribe(listItems => this._listItems = listItems);
  }

}
Max
  • 1,053
  • 1
  • 13
  • 34

2 Answers2

1

Your service can expose listItems$ as an observable event, using a private BehaviourSubject:

@Injectable()
export class ListItemsService{
    public listItems$: Observable<[]>
    private _listItems: BehaviorSubject<[]>;

    private dataStore: {
        listItems: []
    };

    constructor( @Inject(Http) private http: Http) {
        this.baseUrl = 'http://localhost/listitemservice/';
        this.dataStore = { listItems: [] };
        this._listItems = new BehaviorSubject<[]>([]);
        this.listItems$ = this._listItems.asObservable();

    }
    loadAll() {
       this.pageChange(1);
    }

    pageChange(page) {
         this.http.get(this.baseUrl  + page.id)
         .subscribe(items => {
             this.dataStore.listItems = items;
             this._listItems.next(items);
         });
    }

Then in your component, subscribe to the listItems$ event. When it fires, store the list in a member variable of your component class:

export class ListItemsComponent implements OnInit {
  private _listItems = [];
  constructor(private _listItemsService:ListItemsServiceService) { }

  ngOnInit() {
    this._listItemService.listItems$.subscribe(t=> {
       this._listItems = t;
    });
    this._listItemsService.loadAll();    
  }

  pageChange(page){
     this._listItemsService.pageChange(page);
  }

}

On pageChange, call the pageChange method of the ListItemsService whcich in turn triggers the listItems$ event.

Michael Kang
  • 52,003
  • 16
  • 103
  • 135
  • I like your approach. I have a question, what happens if user opens one page and instantly opens another. And first request will finish later than second for some reasons. Do you know some rxjs method to "forget" previous request? – Nikolai Feb 16 '17 at 08:03
  • Thats the definition of an observable, isn´t it? An observable stops one request when another is fired. – Max Feb 16 '17 at 08:17
  • `pageChange(page) { this.http.get(this.baseUrl + page.id) .subscribe(items => { this.dataStore.listItems = items; this._listItems.next(items); }); }` I think that if you call this method 3 times, each new call do not cancel previous one. And all the onSuccess callbacks will be fired after finishing of each request. For example if you call it for 2 page, and then for 3, and request for 2 page will finish later than for 3. At user screen we will see results for 2 page. – Nikolai Feb 16 '17 at 08:30
  • @pixelbits : But in your example you still subscribe to the observe everytime a pageChange event fires, right? I don´t see the additional value. – Max Feb 16 '17 at 08:54
  • The value is less coupling between components. Nikolai, you can use switchMap to only take the latest request. – Michael Kang Feb 17 '17 at 00:47
1

If you want to avoid code duplication you can do something like this:

export class ListItemsComponent implements OnInit {
  private _listItems = [];
  page = 1;
  constructor(private _listItemsService:ListItemsServiceService) { }

  ngOnInit() {
    this.pageChange(this.page);
  }

  subscription;
  pageChange(page){
     if(this.subscription){
         this.subscribtion.unsubscribe();
     }

     this.subscription = this._listItemsService.getListItems(this.page)
         .subscribe(listItems => this._listItems = listItems);
  }
}

If your observable always use same data and you do not want to send requests on each subscribing you need to add caching to your observable. For example:

this.yourObservable
       .first()
       .publishReplay(1)
       .refCount();

More about caching here: What is the correct way to share the result of an Angular 2 Http network call in RxJs 5?

Community
  • 1
  • 1
Nikolai
  • 489
  • 5
  • 12