Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ContentChildren, get the component and the native Element

Tags:

angular

I have been looking for ages on the documentation and source code of Angular, but so far no luck.

import {
  ContentChildren,
  QueryList
} from '@angular/core';
import { AwesomeComponent } from './awesome.component';

@Component({
  selector: 'cool-dad',
  templateUrl: './cool-dad.template.html'
})
export class CoolDadComponent {
  @Input() loop = false;
  @Input() automatic = false;
  @ContentChildren(AwesomeComponent) items: QueryList<AwesomeComponent>;

  someFunc() {
    this.items.forEach(item => { console.log(item)});
  }
}

With the above code I get a reference to the component, I can set it's properties and call it's public methods. That's great, but I also need access to it's html properties, such as width, height, etc.

import {
  ContentChildren,
  ElementRef,
  QueryList
} from '@angular/core';
import { AwesomeComponent } from './awesome.component';

@Component({
  selector: 'cool-dad',
  templateUrl: './cool-dad.template.html'
})
export class CoolDadComponent {
  @Input() loop = false;
  @Input() automatic = false;
  @ContentChildren(AwesomeComponent, { read: ElementRef }) items: QueryList<AwesomeComponent>;

  someFunc() {
    this.items.forEach(item => { console.log(item)});
  }
}

With the above code I get the native element and therefore I can get access to all the html properties, but I loose all the access to the components methods.

How can I get both?

like image 725
Fabio Antunes Avatar asked May 24 '17 14:05

Fabio Antunes


People also ask

How do you get an element reference in component?

Getting ElementRef in Component ClassCreate a template reference variable for the element in the component/directive. Use the template variable to inject the element into component class using the ViewChild or ViewChildren.

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.

How do you use ContentChildren?

To use ContentChild , we need to import it first from the @angular/core . import { Component, ContentChild, ContentChildren, ElementRef, Renderer2, ViewChild } from '@angular/core'; Then use it to query the header from the projected content.

What is the purpose of the ContentChildren decorator in this component class?

ContentChildren is a parameter decorator that is used to fetch the QueryList of elements or directives from the content DOM. The QueryList is updated whenever the child element/component is added or removed. The child element reference is set in QueryList just before the ngAfterContentInit lifecycle Hook method.


2 Answers

Simply declare the following in the constructor of AwesomeComponent:

constructor(public elem: ElementRef) {}

This way you'll be able to access the public property elem and access the html properties of each component:

someFunc() {
 this.items.forEach(item => {console.log(item.elem.nativeElement.style.someHTMLProperty)});
}
like image 139
Unsinkable Sam Avatar answered Dec 08 '22 00:12

Unsinkable Sam


After hacking around to make this work on my own project, I realized why it's not supported natively in any obvious way: It's a bad idea.

If CoolDadComponent can actually change the HTML properties of AwesomeComponent, then it obviously needs to make various assumptions about that component and encapsulation goes belly-up. Also, view encapsulation rules could complicate styling.

So the correct way of doing this, is to let CoolDadComponent work with @ContentChildren and set properties on the AwesomeComponent instances. Those instances should manipulate their own HTML, preferably in a template or at least with Renderer.

export class CoolDadComponent {
// Other stuff as in OP
  @ContentChildren(AwesomeComponent) items: QueryList<AwesomeComponent>;
  someFunc() {
    this.items.forEach(item => item.configure({prop1: 'value', prop2: 'value'}));
  }
}

And in AwesomeComponent:

export class AwesomeComponent {
// Other stuff
  public configure(options: MyOptionsInterface) {
    // Set related properties
  }

It will also work if CoolDadComponent (bonus points for that name, by the way) simply sets the individual properties on AwesomeComponent directly.

like image 32
Cobus Kruger Avatar answered Dec 08 '22 00:12

Cobus Kruger