📜 ⬆️ ⬇️

Typical use of Observable objects in Angular 4

I present to your attention the typical options for using Observable objects in components and services of Angular 4.



Subscribe to a router parameter and map to another Observable


Task: When opening the example.com/#/users/42 page, get user data by userId .


Solution: When initializing the UserDetailsComponent component UserDetailsComponent we subscribe to the parameters of the router. That is, if the userId changes, our subscription will trigger. Using the obtained userId , we get Observable with user data from the userService service.


 // UserDetailsComponent ngOnInit() { this.route.params .pluck('userId') //  userId   .switchMap(userId => this.userService.getData(userId)) .subscribe(user => this.user = user); } 




Subscribe to a router parameter and query string


Task: When you open the example.com/#/users/42?regionId=13 page, you need to execute the load(userId, regionId) function load(userId, regionId) . Where userId we get from the router, and regionId - from the request parameters.


Solution: We have two sources of events, so let's use the Observable.combineLatest function, which will work when each of the sources generates an event.


 ngOnInit() { Observable.combineLatest(this.route.params, this.route.queryParams) .subscribe(([params, queryParams]) => { //    const userId = params['userId']; const regionId = queryParams['regionId']; this.load(userId, regionId); }); } 

Please note that the created subscriptions to the router will be deleted when the object is destroyed, an angular is following this, so you do not need to unsubscribe from the parameters of the router:


Router manages it observables it provides and localizes the subscriptions. Do you need to keep track of the observable params? Mark rajcok

Stop loading animation after subscription completion


Task: Show download icon after starting saving data and hide it when data is saved or an error occurs.


Solution: We have the loading variable responsible for displaying the bootloader; after clicking on the button, we set it to true . And to set it to false use the Observable.finally functions, which is executed after the subscription is completed or if an error has occurred.


 save() { this.loading = true; this.userService.save(params) .finally(() => this.loading = false) .subscribe(user => { //   }, error => { //   }); } 

Creating your own event source


Task: Create a lang$ variable in configService , to which other components will subscribe and respond when the language changes.


Solution: Use the BehaviorSubject class to create the lang$ variable;


Differences between BehaviorSubject and Subject :


  1. BehaviorSubject must be initialized with an initial value;
  2. The subscription returns the last value of Subject a;
  3. You can get the last value directly through the getValue() function.

Create a variable lang$ and immediately initialize it. Also add the setLang function to set the language.


 // configService lang$: BehaviorSubject<Language> = new BehaviorSubject<Language>(DEFAULT_LANG); setLang(lang: Language) { this.lang$.next(this.currentLang); //    } 

Subscribing to change the language in the component. The variable lang$ is a "hot" Observable object, that is, a subscription requires unsubscribe when the object is destroyed.


 private subscriptions: Subscription[] = []; ngOnInit() { const langSub = this.configService.lang$ .subscribe(() => { // ... }); this.subscriptions.push(langSub); } ngOnDestroy() { this.subscriptions .forEach(s => s.unsubscribe()); } 

Use takeUntil to unsubscribe


You can unsubscribe with a more elegant option , especially if there are more than two subscriptions in the component:


 private ngUnsubscribe: Subject<void> = new Subject<void>(); ngOnInit() { this.configService.lang$ .takeUntil(this.ngUnsubscribe) //    .subscribe(() => { // ... }); } ngOnDestroy() { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); } 

That is, in order not to lose memory on hot subscriptions, the component will work until the value of ngUnsubscribe changes. And it will change when ngOnDestroy is ngOnDestroy . The advantages of this option are that it is enough to add just one line to each of the subscriptions so that the answer works on time.


Use Observable for autocomplete or search


Task: Show page suggestions when entering data on a form


Solution: Sign up for changing the form data, take only the variable input data, set a small delay so that there are not too many events and send the request to Wikipedia. The result is displayed in the console. An interesting point is that switchMap will cancel the previous request if new data arrived. This is very useful, to avoid non-spam effects from slow queries, if, for example, the penultimate query was executed for 2 seconds and the last 0.2 seconds, then the result of the last query will be displayed in the console.


 ngOnInit() { this.form.valueChanges .takeUntil(this.ngUnsubscribe) //    .map(form => form['search-input']) //   .distinctUntilChanged() //    .debounceTime(300) //    .switchMap(this.wikipediaSearch) //  Observable     .subscribe(data => console.log(data)); } wikipediaSearch = (text: string) => { return Observable .ajax('https://api.github.com/search/repositories?q=' + text) .map(e => e.response); } 

Query caching


Task: You need to cache Observable request


Solution: Let's use a bunch of publishReplay and refCount . The first function will cache one function value for 2 seconds, and the second will read the created subscriptions. That is, the Observable will end when all subscriptions are completed. Here you can read more.


 // tagService private tagsCache$ = this.getTags() .publishReplay(1, 2000) //     2  .refCount() //   .take(1); //  1  getCachedTags() { return tagsCache$; } 

Sequential combineLatest


Task: Critical situation on the server! The backend team said that in order to correctly update the product, it is necessary to perform strictly consistently:


  1. Update product data (title and description);
  2. Update the list of product tags;
  3. Update the list of product categories;

Solution: We have 3 Observables obtained from the productService . Use concatMap :


 const updateProduct$ = this.productService.update(product); const updateTags$ = this.productService.updateTags(productId, tagList); const updateCategories$ = this.productService.updateCategories(productId, categoryList); Observable .from([updateProduct$, updateTags$, updateCategories$]) .concatMap(a => a) //    .toArray() //     .subscribe(res => console.log(res)); // res     

Riding Riddle


If you have a desire to practice a little, solve the previous problem, but to create a product. That is, first we create the product, then we update the tags, and only then - the categories.


useful links



')

Source: https://habr.com/ru/post/337512/


All Articles