0

Angular - Understanding How To Use QueryList Properly. - Talking HighTech

 3 years ago
source link: https://www.talkinghightech.com/en/angular-undertanding-querylist/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Once you master the basics of Angular and you start to extend your knowledge in the framework, you start to use more sophisticated types of inputs to your component instead of the regular @Input decorator. In this post we will go through all the ways we can leverage the QueryList object understanding the differences between the different children it binds to and finally registering the changes including the init value.

ng-content

This tag is used for content projection meaning it’s a place holder to hold dynamic content. Once Angular parsed the dynamic content it will replace the ng-content tag with the parsed dynamic content. You can think about it as using the {{ }}( curly braces) in the template but the difference is the value will not be parsed from the state in the component but from the content the user of your component placed inside your tag.

Let’s say we are creating a UI Card widget we call CardCompnent with the selector my-card that will be used as a general cardholder in our application. We would like to project dynamic content into it so we can reuse the design but with a different body as needed. The card template will look something like this:

<div class="card ">
  
  <p class="card-body"
     >
    <ng-content></ng-content> 
  </p>
 
</div>

And now whenever someone wishes to use this widget he will invoke it as the following:

<my-card>
 <h3> This is my dynamic - changing content</h3>
</my-card>

Notice the h3 tag – this is what will be projected and will replace the ng-content tag after the parse.

Now that we understand the difference between dynamic content and static content we can move on with the explanation.

@ViewChildren

Use to get the QueryList of elements or directives from the view DOM. Meaning nondynamic content that will be injected into an ng-content tag. Any time a child element is added, removed, or moved, the query list will be updated, and the changes observable of the query list will emit a new value. View queries are set before the ngAfterViewInit callback is called.

@ContentChildren

Use to get the `QueryList` of elements or directives from the content DOM. Meaning elements that will be projected into the ng-contnent tag and are not statically rendered. Any time a child element is added, removed, or moved, the query list will be updated, and the changes observable of the query list will emit a new value. Content queries are set before the `ngAfterContentInit` callback is called. Do not retrieve elements or directives that are in other components’ templates.

@ViewChildren vs @ContentChildren

What is the difference? Lets take a look at the following example:

templeate:
     <my-card>
           <my-card-body></my-card-body>
     </my-card>

The first descendent of the parent element is the my-card component that we added ourselves so to query child elements we will use the @ViewChildren since its static, as for the grand-child – my-card-body component, this element will be projected into the my-card once it parsed using an ng-content tag so we should use the @ContentChildren since its the dynamic content of the my-card component.

When we are looking for direct elements in our template we will use @ViewChildren when we are querying for elements that will be used not directly by us as the my-card-body component then we go with @ContentChildren.

QueryList

An unmodifiable list of items that Angular keeps up to date when the state * of the application changes. Changes can be observed by subscribing to the changes `Observable`.

Take a look at the class definition – we can notice that this is an array like object with some more extra properties

class QueryList<T> implements Iterable {
  constructor(_emitDistinctChangesOnly: boolean = false)
  dirty: true
  length: number
  first: T
  last: T
  changes: Observable<any>
  __@iterator: () => Iterator<T>
  get(index: number): T | undefined
  map<U>(fn: (item: T, index: number, array: T[]) => U): U[]
  filter(fn: (item: T, index: number, array: T[]) => boolean): T[]
  find(fn: (item: T, index: number, array: T[]) => boolean): T | undefined
  reduce<U>(fn: (prevValue: U, curValue: T, curIndex: number, array: T[]) => U, init: U): U
  forEach(fn: (item: T, index: number, array: T[]) => void): void
  some(fn: (value: T, index: number, array: T[]) => boolean): boolean
  toArray(): T[]
  toString(): string
  reset(resultsTree: (any[] | T)[], identityAccessor?: (value: T) => unknown): void
  notifyOnChanges(): void
  setDirty()
  destroy(): void
}

Next we choose to use one of the above decorators will end up with an QueryList object that we can track for changes

@ContentChildren() myChildren: QueryList<SomeComponent>;

We can keep track of changes using the property

changes: Observable<any>;

Since it’s just an observable we can just subscribe to is

myChildren.changes.subscribe(change=> doSomething());

But….. don’t forget that these changes will be effected after the ngAfterViewInit and ngAfterContentInit depending if it’s @ViewChildren or @ContentChildren respectability. meaning you will miss the first change that will occur with the initial value. This can be simply solved using the startWith operator

myChildren.changes.pipe(startWith(0)).subscribe(change=> doSomething());

Using the Rxjs startwith observable

Since A BehaviorSubject can also start with an initial value and changes will occur after the initial value is already set we are going to miss the inital value. – startWith(0) solves this issue by omitting the initial value also. So now we will pick up changes in the QueryList object but we will also receive the inial value that it was inited with!

Example of on going changes:

templeate:
     <my-card>
           <my-card-body *ngIf="shouldDisplay"></my-card-body>
     </my-card>

Notice the inner my-card-body has an ngIf which mean it can change on the fly so the changes Observable will emit.

Learning Angular – checkout the rest of our posts here.

Resources:


Also published on Medium.

Like this:

Loading...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK