Как использовать директиву в пользовательском вводе с ControlValueAccessor Angular - PullRequest
1 голос
/ 19 февраля 2020

Я создал простой пользовательский компонент input в моем приложении angular ControlValueAccessor. Поэтому, когда я хочу создать элемент формы input, мне не нужно вызывать <input />, только вызывать <my-input>.

У меня возникает проблема, когда я использую <input />, я можно использовать myDirective. Например:

<input type="text" class="form-control" formControlName="name" myDirective />

Но когда я использую my-input, тогда я не могу использовать myDirective. Например:

<my-input formControlName="name" myDirective></my-input>

myDirective не работает в my-input

Это my-input использование компонента ControlValueAccessor код :

import { Component, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';

@Component({
  selector: 'my-input',
  templateUrl: './my-input.component.html',
  styleUrls: ['./my-input.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(()  => MyInputComponent ),
      multi: true
    }
  ]
})

export class MyInputComponent implements ControlValueAccessor {

  onChange: () => void;
  onTouched: () => void;

  value: string;

  writeValue(value: string): void {
    this.value = value ? value : '';
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
}

Обновлено: myDirective код:

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

@Directive({
    selector: '[myDirective]'
})

export class MyDirective{
    constructor(private formControlName: FormControlName) { }

    @HostListener('input', ['$event']) 
    onInputChange() {
        this.formControlName.control.setValue(this.formControlName.value.replace(/[^0-9]/g, ''));
    }
}

Можно ли использовать myDirective в компоненте my-input?

Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 19 февраля 2020

Есть проблема с вашей директивой. Введите NgControl и управляйте этим ngControl

export class MyDirective{
    constructor(private control: NgControl) { } //<--inject NgControl

    @HostListener('input', ['$event']) 
    onInputChange() {
        this.control.control.setValue(this.control.value.replace(/[^0-9]/g, ''));
    }
}

Вы можете увидеть в stackblitz

ПРИМЕЧАНИЕ. Не забудьте включить в объявления модуля

@NgModule({
  imports:      [ BrowserModule, FormsModule,ReactiveFormsModule ],
  declarations: [ AppComponent, MyInputComponent,MyDirective ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }
1 голос
/ 19 февраля 2020

Вы не можете напрямую, но вы можете сделать несколько хаков с запросами директив

Согласно документации Angular Директив:

Декоратор, который помечает класс как Angular директива. Вы можете определить свои собственные директивы для присоединения пользовательского поведения к элементам в DOM.

декоратор - это поведение, присоединенное к элементу в DOM, поэтому оно будет влиять на элемент, который использует директиву.

НО (а это большое НО по причине - проверьте примечания), вы можете взломать это поведение с помощью директивных запросов.

НО помните вы должны определить специфи c директивы, которые будут работать только с заданными c запросами и не могут использоваться для любого элемента DOM.

Решение

Решение основано на следующем:

Я хочу определить директиву, которая работает с дочерними элементами, а не с самим элементом.

Вы можете использовать @ ContentChildren и QueryList, чтобы проверить, есть ли у вас дети с указанием директивы c.

Например, у меня есть фоновая директива, примененная к входному родительскому элементу, и CustomFormControlDirective для запроса дочерних элементов:

import {
  Directive,
  ElementRef,
  Input,
  ContentChildren,
  ViewChildren,
  QueryList
} from "@angular/core";

@Directive({
  selector: "[customformcontrol]"
})
export class CustomFormControlDirective {
  constructor(public elementRef: ElementRef) {}
}

@Directive({
  selector: "[backgroundColor]",
  queries: {
    contentChildren: new ContentChildren(CustomFormControlDirective),
    viewChildren: new ViewChildren(CustomFormControlDirective)
  }
})
export class BackgroundColorDirective {
  @Input()
  set backgroundColor(color: string) {
    this.elementRef.nativeElement.style.backgroundColor = color;
  }

  @ContentChildren(CustomFormControlDirective, { descendants: true })
  contentChildren: QueryList<CustomFormControlDirective>;
  viewChildren: QueryList<CustomFormControlDirective>;

  constructor(private elementRef: ElementRef) {}

  ngAfterContentInit() {
    // contentChildren is set
    console.log("%o", this.contentChildren);
  }
}
[...]
<div backgroundColor="red">
  <input customformcontrol />
</div>
* 103 3 * Конечно, эта директива будет применять цвет bg к родительскому div:

Div with red bg]

Итак, как мы можем установить это свойство для детей?

У нас есть дочерние элементы в нашей переменной contentChildren :

Console log

Таким образом, нам нужно применить желаемый фон к нашему дочернему элементу, мы можем oop через результаты запроса и попробуйте применить стиль:

  [...]
  _backgroundColor = null;
  @Input()
  set backgroundColor(color: string) {
    /// save the bg color instead of apply style
    this._backgroundColor = color;
  }
  ngAfterContentInit() {
    if (this.contentChildren.length) {
      /// then loop through childrens to apply style
      this.contentChildren.forEach(customFormControl => {
        customFormControl.elementRef.nativeElement.style.backgroundColor = this._backgroundColor;
      });
    }
  }
  [...]

И он будет применять стиль к детям: Children with red bg

также, если есть более 1 ребенка: More fancy red bgs

Примечания

  • это пример, не принимайте эту реализацию как есть, вы можете определить используйте собственный метод или используйте лучший, но попытайтесь понять селекторы QueryList и ContentChildren.
  • Использование пользовательской директивы для извлечения дочерних элементов может быть необязательным, вы можете использовать директивы ReactiveForm напрямую (AbstractControlDirective / FormControlDirective), но я не не думаю, что они позволят вам DOM как частный.
  • эти директивы будут работать только с дочерними элементами, поэтому вы можете выбрать лучшее соглашение об именах (например, ApplyToControlBackgroundDirective)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...