[Angular] @ContentChild with Directive ref

For example you have a component, which take a trasclude input element:

            <au-fa-input id="password-field" icon="lock" >
                <input placeholder="Password" class="test-class">
            </au-fa-input>

There is many ways to get ElementRef of the input, for example using Reference:

            <au-fa-input id="password-field" icon="lock" >
                <input #input placeholder="Password" class="test-class">
            </au-fa-input>

We add a #input ref, so that inside component, we can using @ContentChild and AfterContentInit lifecycle:

export class AuFaInputComponent implements AfterContentInit{

    @Input()
    icon: string;

    @ContentChild('input')
    input: HTMLInputElement;

    ngAfterContentInit () {
      console.log("input", this.input);
    }

This approach works fine, but it requires us to put a element ref onto the input field.

One way to avoid putting a reference is creating a directive ref, and we can add common functionalitiy to it, such as focus and blur events:

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

@Directive({
    selector: 'au-fa-input input'
})
export class InputRefDirective {

  focus = false;

  @HostListener('focus')
  isFocus() {
    console.log("now focus");
    this.focus = true;
  }

  @HostListener('blur')
  isBlur() {
    console.log("now blur");
    this.focus = false;
  }
}

And we our component, we need to change @ContentChild:

    @ContentChild(InputRefDirective)
    input: InputRefDirective;

So for now, in html, we don't need #input any more:

            <au-fa-input id="password-field" icon="lock" >
                <input placeholder="Password" class="test-class">
            </au-fa-input>

Now, let's say if we want to add some styling base on whether input field is focused or not.

First, some css class:

// component

:host(.input-focus) {
  border-color: #4D90FE;
  -webkit-box-shadow: 0 0 5px  #4D90FE;
  box-shadow: 0 0 5px  #4D90FE;
}

Use the function form to apply host styles conditionally by including another selector inside parentheses after :host. So here, we check if .input-focus is present on the host element, then we apply the styling, otherwise not.

Now we only need to apply .input-focus class to the host element when input.focus is true, we can do this easily by @HostBinding:

  @HostBinding('class.input-focus')
  get isInputFocus() {
    return this.input ? this.input.focus : false;
  }

------

component:

import {Component, Input, ContentChild, AfterContentInit, HostBinding} from '@angular/core';
import {InputRefDirective} from 'app/lib/common/input-ref.directive';

@Component({
  selector: 'au-fa-input',
  templateUrl: './au-fa-input.component.html',
  styleUrls: ['./au-fa-input.component.css']
})
export class AuFaInputComponent implements AfterContentInit {

  @Input()
  icon: string;

  @ContentChild(InputRefDirective)
  input: InputRefDirective;

  @HostBinding('class.input-focus')
  get isInputFocus() {
    return this.input ? this.input.focus : false;
  }

  ngAfterContentInit() {
    if (!this.input) {
      console.error('You forgot pass in the input field');
    }
  }

  get classes() {

    const cssClasses = {};

    if (this.icon) {
      cssClasses['fa-' + this.icon] = true;
    }

    return cssClasses;
  }

}

Directive:

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

@Directive({
    selector: 'au-fa-input input'
})
export class InputRefDirective {

  focus = false;

  @HostListener('focus')
  isFocus() {
    this.focus = true;
  }

  @HostListener('blur')
  isBlur() {
    this.focus = false;
  }
}
原文地址:https://www.cnblogs.com/Answer1215/p/6926959.html