Vivek Kumar
Vivek Kumar

Reputation: 5040

How to query input element of ng-content from parent component

Creating a component like below

<div class="test-search-field"
  [ngClass]="{'expanding': expanding}">
  <ng-content></ng-content>
</div>

The user of this component will add an input element while using this component.

<my-input-container>
    <input type="search" placeholder="Search">
</my-input-container>

From the component I want to get hold of this input element, but getting errors while querying using @ContentChild.

@ContentChild('input') inputElemRef: ElementRef;

  ngAfterContentInit() {
    setInterval(() => {
      console.log(this.inputElemRef);
    }, 2000);

But this inputElemRef is always coming undefined.

Can anyone please tell me what mistake I am doing here.

Upvotes: 3

Views: 5865

Answers (2)

Tomasz Kula
Tomasz Kula

Reputation: 16837

  1. Query by the directive class token.

my-input.directive.ts

import { Directive } from '@angular/core';

@Directive({
  selector: '[myInput]',
})
export class MyInputDirective  {}

my-input-container.component.ts

import { Component, Input, ContentChild, AfterContentInit, ElementRef } from '@angular/core';
import { MyInputDirective } from './my-input.directive';

@Component({
  selector: 'my-input-container',
  template: `<ng-content></ng-content>`,
  styles: [`h1 { font-family: Lato; }`]
})
export class MyInputContainerComponent implements AfterContentInit {
  @ContentChild(MyInputDirective, { read: ElementRef }) child: MyInputDirective;

  ngAfterContentInit() {
    console.log(this.child);
  }
}

usage:

<my-input-container>
  <input myInput />
</my-input-container>

Live demo

  1. Query by the template variable.

If you only care about the ElementRef, you can skip the custom directive entirely, and query by the template variable instead.

my-input-container.ts

import { Component, Input, ContentChild, AfterContentInit, ElementRef } from '@angular/core';

@Component({
  selector: 'my-input-container',
  template: `<ng-content></ng-content>`,
  styles: [`h1 { font-family: Lato; }`]
})
export class MyInputContainerComponent {
  @ContentChild('myInput') child: ElementRef;

  ngAfterContentInit() {
    console.log(this.child);
  }
}

usage:

<my-input-container>
  <input #myInput />
</my-input-container>

Live demo

In general the first option is preferrable and its what the @angular/material does with their input component. They pair the container component (mat-form-field) with a directive applied to the native input element (matInput).

It is more flexible, because you can query either for the Directive, or for the ElementRef.

@ContentChild(MyInputDirective) child: MyInputDirective;

@ContentChild(MyInputDirective, { read: ElementRef }) child: MyInputDirective;

Upvotes: 7

user4676340
user4676340

Reputation:

I would have used the renderer to do that.

Somethin like this stackblitz

ngAfterViewInit() {
  const el = this.renderder.selectRootElement('input');
  console.log(el);
}

Upvotes: 2

Related Questions