Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are all the valid selectors for ViewChild and ContentChild?

Tags:

angular

I'm looking for a comprehensive list of the valid selectors I can use to access child components / DOM elements via @ViewChild and @ContentChild.

Say I have a child HelloComponent:

I know I can add a template #ref and query for it, like so:

<hello #myHello></hello>  @ViewChild('myHello') myHello: HelloComponent; 

Or I can look for that component directly (without the template #ref):

@ViewChild(HelloComponent) myHello: HelloComponent; 

In this issue, it is mentioned that one can use the following selectors:

we are currently supporting a sub-set of CSS selectors:
* element selectors
* attribute selectors (including values)
* :not(...) pseudo-selector
* combination of the above (including ,)

But when I tested these in Stackblitz to verify (here's the link to that), I couldn't actually get any of the first three to work. (Check the console to see undefined for the types of selectors I couldn't get working. I'm unsure if I'm doing something wrong with those selectors or if the actual list is different.)

So, which selectors will work? Also, is the list the same for @ViewChild, @ContentChild, @ViewChildren, and @ContentChildren?

like image 532
vince Avatar asked Mar 07 '18 22:03

vince


People also ask

What is the difference between ViewChild () and ContentChild ()?

Any directive, component, and element which is part of component template is accessed as ViewChild. Whereas, any element or component which is projected inside <ng-content> is accessed as ContentChild.

What is ContentChild and ContentChildren?

The ContentChild & ContentChildren are decorators, which we use to Query and get the reference to the Projected Content in the DOM. Projected content is the content that this component receives from a parent component. The ContentChild & ContentChildren is very similar to the ViewChild & ViewChildren.

What is ViewChild and ViewChildren example of ViewChildren?

Both ViewChild and ViewChildren are used to communicate between the components to access the data. @ViewChild and @ViewChildren are the types of decorators used to access the child component class and its different properties into the parent component. It's similar to the inheritance.

What do you understand by ViewChild and ContentChild?

ViewChild is used to select an element from component's template while ContentChild is used to select projected content.


2 Answers

First of all, as @JB Nizet has already mentioned in comments the comment in the issue is wrong: it has nothing to do with query selector but rather refers to directive selector.

Let's examine what kind of selectors we can use for queries.

Angular documentation states that for queries:

selector - the directive type or the name used for querying.

The directive type

Seems it should be clear for anyone that (1) we can query any classes adorned by @Component or @Directive decorator.

@Component({   selector: 'some-comp',   template: '...' }) export class SomeComp {}  @Directive({   selector: '[someDir]' }) export class SomeDir {}  @Component({   selector: 'host-comp',   template: `     <some-comp someDir></some-comp>   ` }) export class HostComp {   @ViewChild(SomeComp) someComp: SomeComp;   @ViewChild(SomeDir) someDir: SomeDir; } 

The name used for querying

As for me this is confusing description.

As it turned out the name here is (2) the name of template reference variable which is a string:

@Component({   selector: 'host-comp',   template: `     <some-comp #someComp></some-comp>   ` }) export class HostComp {   @ViewChild('someComp') someComp: SomeComp; } 

we could end up here but it's time to take a look at angular source code and dive a bit deeper.

Hidden behavior

Let's look at the code that angular compiler uses to read query metadata:

private _queryVarBindings(selector: any): string[] { return selector.split(/\s*,\s*/); }  private _getQueryMetadata(q: Query, propertyName: string, typeOrFunc: Type|Function):       cpl.CompileQueryMetadata {   let selectors: cpl.CompileTokenMetadata[];   if (typeof q.selector === 'string') {     selectors =         this._queryVarBindings(q.selector).map(varName => this._getTokenMetadata(varName));   } else {     if (!q.selector) {       this._reportError(           syntaxError(               `Can't construct a query for the property ...`),           typeOrFunc);       selectors = [];     } else {       selectors = [this._getTokenMetadata(q.selector)];     } } 

From the preceding code we can conclude that:

  • if selector is a string divided by , then we can construct more than one selector.

  • on the other hand if selector is not a string then we can get only one selector

  • compiler uses this._getTokenMetadata method to extract information from passed selector but it's the same method that is used for extracting provider metadata https://github.com/angular/angular/blob/4c089c1d931c0ea35591837706de205a75a61ccb/packages/compiler/src/metadata_resolver.ts#L1073-L1077

Let’s apply our learnings from above code.

We (3) can query several values by using several template reference variables divided by ,:

@Component({   selector: 'a',   template: '...' }) export class A {}  @Component({   selector: 'b',   template: '...' }) export class B {}  @Component({   selector: 'host-comp',   template: `     <a #a></a>     <b #b></b>   ` }) export class HostComp {   @ViewChildren('a, b') components;    ngAfterViewInit() {     console.log(this.components); // [A, B]   } } 

(4) Provider defined on a component or directive can be queried. (see also example added by @Ilia Volk)

@Component({   selector: 'a',   template: '...',   providers: [SomeService] }) export class A {}  @Component({   selector: 'host-comp',   template: `<a></a>` }) export class HostComp {   @ViewChild(SomeService) someService: SomeService; } 

Since string can be a token for providers we can (5) query several providers that were defined through string token

@Component({   selector: 'a',   providers: [{ provide: 'tokenA', useValue: 'TokenAValue' }],   template: '...' }) export class A { }  @Component({   selector: 'b',   providers: [{ provide: 'tokenB', useValue: 'TokenBValue' }],   template: '...' }) export class B { }  @Component({   selector: 'host-comp',   template: `     <a #a></a>     <b #b></b>   ` }) export class HostComp {   @ViewChildren('tokenA, tokenB') stringTokenProviders;    ngAfterViewInit() {     console.log(this.stringTokenProviders); // ['TokenAValue', 'TokenBValue']   } } 

The next our stop is the place in core package where angular returns us the value of particular query:

export function getQueryValue(     view: ViewData, nodeDef: NodeDef, queryValueType: QueryValueType): any {   if (queryValueType != null) {     // a match     switch (queryValueType) {       case QueryValueType.RenderElement:         return asElementData(view, nodeDef.nodeIndex).renderElement;       case QueryValueType.ElementRef:         return new ElementRef(asElementData(view, nodeDef.nodeIndex).renderElement);       case QueryValueType.TemplateRef:         return asElementData(view, nodeDef.nodeIndex).template;       case QueryValueType.ViewContainerRef:         return asElementData(view, nodeDef.nodeIndex).viewContainer;       case QueryValueType.Provider:         return asProviderData(view, nodeDef.nodeIndex).instance;     }   } } 

RenderElement in the code above is some internal token that we can't query.

ElementRef can be queried through template reference variable or by using read option

(6) TemplateRef can be queried through selector:

@Component({   selector: 'host-comp',   template: `     <ng-template></ng-template>   ` }) export class HostComp {   @ViewChild(TemplateRef) template; } 

and of course as well as ViewContainerRef through read option.

Provider can be obtained by using read option or through selector as I've described in the middle of this answer.

like image 169
yurzui Avatar answered Sep 30 '22 22:09

yurzui


IN short View child You can use anglular directive, component selector, reference variable

enter image description here

like image 45
Xelian Avatar answered Sep 30 '22 21:09

Xelian