"Angular" about asyncpipe 3 things you don't know!

Source: Internet
Author: User
Tags emit export class

My ANGULAR2 project:
Http://git.oschina.net/zt_zhong/CodeBe
Original address: https://blog.thoughtram.io/angular/2017/02/27/three-things-you-didnt-know-about-the-async-pipe.html

You must have heard about angular's asyncpipe, right? It is a very handy conduit (Pipe) that allows us to use in the stencil, so we don't have to deal with data from observables or promises. The result is that Asyncpipe has some magical features that may not be so obvious. In this article, we will explain the internal workings of this useful gadget. Subscribe to Observables

Usually when we consider using asyncpipe, we only use it to resolve the data returned from the HTTP call. We emit an HTTP call, return a observable object, and then do some transformations (e.g., map (...)). Filter (...) And finally exposes a observable to our component template. Here is what we usually do when we use it:

...
@Component ({
  ...
  ) Template: '
    <md-list>
      <a md-list-item
        *ngfor= ' let contacts of contacts | async '
        title= ' View { Contact.name} Details ">
        
        

In this case, our observable is what we call a short-lived (original short-lived). Observable in this case, only one value is emitted, which is a contacts array and ends after launch. This is a typical case of using HTTP, which is basically the only scenario using promises.

However, we can fully have observables that emit multiple values. Think about using the websockets example. We might have an array built over time. Let's simulate a observable that emits an array of numbers. However, instead of emitting only one array at a time, it emits an array each time a new item is added. To keep the array from growing indefinitely, we limit it to the last five items.

...
@Component ({
  selector: ' My-app ',
  Template: '
    <ul>
      <li *ngfor= ' let item of items | Async ' >{ {Item}} </li>
    </ul> '
})
Export class Appcomponent {
  items = observable.interval
                    . Scan ( (ACC, cur) =>[cur, ... acc].slice (0, 5), []);             
}

Note how our list maintains a good sync, needless to say, thanks to Asyncpipe. Trace References

Without the help of asyncpipe, let's go back to the code above and refactor the code above. However, here we use a button to regenerate the number and randomly select the background color of the element each time the sequence is regenerated.

...
@Component ({
  selector: ' My-app ',
  Template: '
    <button (click) = ' Newseq () ' >new random sequence</ button>
    <ul>
      <li [style.background-color]= "Item.color"
          *ngfor= "let item of items" >{{ item.num}}</li>
    </ul> '
})
Export class Appcomponent {
  items = [];

  Constructor () {
    this.newseq ();
  }

  Newseq () {

    //Generate a random color let
    color = ' # ' + math.random (). toString (). Slice ( -6);

    Observable.interval (1000)
          . Scan ((ACC, num) =>[{num, color}, ... acc].slice (0, 5), [])
          . Subscribe (items = > this.items = items);
  }


Run Address online

Run this example and notice anything. With each click of the button, the color will scroll back and forth between the more and more different colors. That is because in this case, observable is what we call Longevity (original: long-lived).
Also, each time we click on a button, we are creating another of these long-lived observable without cleaning up the previous one.

Let's refactor the code to track subscriptions and delete our long-lived observable every time we create a new observable.

...
Export class Appcomponent {
  ...
  subscription:subscription;

  Newseq () {

    if (this.subscription) {
      this.subscription.unsubscribe ();
    }

    Generate a random color let
    color = ' # ' + math.random (). toString (). Slice ( -6);

    This.subscription = observable.interval (1000)
          . Scan ((ACC, num) =>[{num, color}, ... acc].slice (0, 5), [])
          . Subscribe (Items => this.items = items);
  }

Run Address online
Each time we subscribe to a observable, we save it to an attribute in the component instance. Then, when we run newseq again, we check if this.subscription exists, and if so, we need to call unsubscribe to unsubscribe. That's why we can't see our list jump between colors, no matter how many times we click on the button.

Now let's look at the asyncpipe again. Let's change ngfor to apply Asyncpipe.

@Component ({
  selector: ' My-app ',
  Template: '
    <button (click) = ' Newseq () ' >new random sequence</ button>
    <ul>
      <li [style.background-color]= "Item.color"
          *ngfor= "let Item of items | async" >{{item.num}}</li>
    </ul> '
})
Export class Appcomponent {
  Items:observable<any >;

  Constructor () {
    this.newseq ();
  }

  Newseq () {

    //Generate a random color let
    color = ' # ' + math.random (). toString (). Slice ( -6);

    This.items = observable.interval (1000)
                           . Scan ((ACC, num) =>[{num, color}, ... acc].slice (0, 5), []);
  }

Run Address online
I'm sure you've heard of asyncpipe. Once the component is destroyed, the subscription is canceled from observables. However, once the expression's reference changes, you know it cancels the subscription. Yes, as long as we assign a new observable to this, Asyncpipe will automatically unsubscribe from the previously bound observable. This not only makes our code beautiful and clean, but also protects us from very subtle memory leaks. mark something to check.

Good. We offer you the last beautiful Asyncpipe feature. If you have read our article on angular change detection, you should know that you can use the OnPush strategy to further speed up the change detection process. We refactor our example and introduce a seqcomponent to display the sequence, and our root component manages the data and passes it through the input bindings.

We started to create a very simple seqcomponent.

@Component ({
  selector: ' My-seq ',
  Template: '
    <ul>
      <li [style.background-color]=] Item.color " 
          *ngfor=" let item ">{{item.num}}</li> </ul>"
})
Export class seqcomponent {
  @Input ()
  items:array<any>;
}

Note the @input () adorner items property, which means that the component will be received from the outside through property bindings.
Our root component maintains an array of seqs and pushes the new long-lived observables into it by clicking a button.
It uses the * ngfor to pass each of these observables to a new seqcomponent instance. Also note that we are in our attribute-binding expression ([items] = "seq | Async "), using Asyncpipe to pass pure arrays instead of observable, as this is what Seqcomponent expects.

@Component ({
  selector: ' My-app ',
  Template: '
    <button (click) = ' Newseq () ' >new random sequence</ button>
    <ul>
      <my-seq *ngfor= "let seq of Seqs" [items]= "seq | async" ></my-seq>
    </ Ul> '
})
Export class Appcomponent {
  seqs = [];

  Constructor () {
    this.newseq ();
  }

  Newseq () {

    //Generate a random color let
    color = ' # ' + math.random (). toString (). Slice ( -6);

    This.seqs.push (Observable.interval (1000)
                           . Scan ((ACC, num) =>[{num, color}, ... acc].slice (0, 5), []);
  }
}

So far, we have not made any changes to the potential change detection strategy. If you click on a button several times, please note how we get multiple lists and update them independently at different times.
Run Address online
However, in the aspect of change detection, this means that all components can be inspected every observables. This is a waste of resources. By setting Seqcomponent's change detection to OnPush, we can do better, which means that if the input (the array in our case) changes, it will only check its bindings.

@Component ({
  changeDetection:ChangeDetectionStrategy.OnPush,
  selector: ' My-seq ',
  ...
})

This is very effective. But here's the problem: it works only in this case, because our observable creates a new array and value each time. Even if it's not really too bad, and it's actually beneficial in most cases, let's consider using a different implementation to modify an existing array instead of recreating it every time.

Observable.interval (1000)
          . Scan ((ACC, num) =>{
            acc.splice (0, 0, {num, color});
            if (Acc.length > 5) {
              acc.pop ()
            } return
            acc;
          }, [])

If we try, OnPush doesn't seem to work anymore because the project's references don't change at all. In fact, when we try to do this, we see that each list will not exceed its first element.
Run Address online

Know Asyncpipe again. We're going to change our seqcomponent, so it needs a observable instead of an array as its input.

@Component ({
  changeDetection:ChangeDetectionStrategy.OnPush,
  selector: ' My-seq ',
  Template: '
    <ul>
      <li [style.background-color]= "Item.color" 
          *ngfor= "let item | async" >{{item.num}} </li>
    </ul> '
})
Export class Seqcomponent {
  @Input ()
  items:observable< array<any>>;
}

Also note that it now applies asyncpipe to its template because it no longer handles a simple array. Our appcomponent also needs to be changed so that asyncpipe is no longer applied in property bindings.

<ul>
  <my-seq *ngfor= "let seq of Seqs" [items]= "seq" ></my-seq>
</ul>

Run Address online

Cool Now it seems to have begun to work.

Let's recall that our array instance does not change, nor does the instance of our observable change. So why does OnPush work in this situation? The reason can be found in the source code of Asyncpipe itself

Private _updatelatestvalue (Async:any, value:object): void {
  if (async = = this._obj) {
    This._latestvalue = value ;
    This._ref.markforcheck ();
  }

Asyncpipe marks the changedetectorref of the component to be checked, effectively telling the change detection that the component may change. If you would like to know more about how we work, we recommend that you read our in-depth change detection article. Summary

We use Asyncpipe as a nice little tool to save a few lines of code in our component. In fact, it hides a lot of complexity from our management asynchronous task.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.