Angular2 将值绑定到结构指令

Angular2 bind value to structural directive

所以我一直在尝试为输入创建我自己的自定义下拉菜单,所以我创建了一个结构指令来在我想要使用它的输入元素下创建一个下拉列表。最好我想在我使用指令的组件中绑定一个值,这样我就可以更新表单控件,而不必直接访问 DOM。

我觉得应该有一种简单直接的方法来做到这一点,我很可能会错过。 * 装饰器似乎消除了从指令创建输出的可能性,并且还混淆了 Elementref,因为它将元素变成了嵌入式模板。

非常欢迎任何帮助,我已经尝试解决这个问题一段时间了,但似乎找不到答案。

笨蛋:https://embed.plnkr.co/OPxSY7PKTCo1sDpksF8j/

我认为满足您要求的最佳解决方案是使用 ControlValueAccessor。使用这种方法,您不需要使用创建组件的指令等来搞砸(正如我们从您的演示中看到的那样)。通过这种方式,您可以创建一个组件来完成您需要的所有工作。

Angular 文档说:

ControlValueAccessor creates a bridge between Angular FormControl instances and native DOM elements.

这是一个 ControlValueAccessor 的工作 StackBlitz demo 和你的 plunker 代码。

这是实现 ControlValueAccessor 下拉菜单的方式:

dropdown.component.ts:

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

const noop = () => { };

const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => DropdownComponent),
  multi: true
};

@Component({
  selector: 'appDropdown',
  templateUrl: './dropdown.component.html',
  providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class DropdownComponent implements ControlValueAccessor {
  private _value = false;
  private isDisabled = false;
  private onTouched: () => void = noop;
  private onChange: (value: any) => void = noop;

  get value() {
    return this._value;
  }

  isShowDropdown = false;

  public clickedOutside: EventEmitter<void> = new EventEmitter<void>();
  rows = [{ name: 'One', value: 1 }, { name: 'Two', value: 2 }, { name: 'Three', value: 3 }];

  constructor(private elementRef: ElementRef) { }

  @HostListener('document:click', ['$event.target'])
  public onDocumentClick(targetElement) {
    if (!this.elementRef.nativeElement.contains(targetElement)) {
      this.isShowDropdown = false;
    }
  }

  onRowSelected(value: any) {
    this.onTouched();
    if (!this.isDisabled) {
      this.writeValue(value);
    }
    this.isShowDropdown = false;
  }

  //#region ControlValueAccessor implement

  writeValue(value: any): void {
    console.log(value);
    this._value = value;
    this.onChange(value);
  }
  registerOnChange(fn: any): void {
    this.onChange = fn;
  }
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
  setDisabledState?(isDisabled: boolean): void {
    this.isDisabled = isDisabled;
  }

  //#endregion ControlValueAccessor implement
}

dropdown.component.html:

<input [value]="value">
<div *ngIf="isShowDropdown" style="position: absolute">
    <h4 style="border: 1px solid grey; padding: 5px; margin: 0px" *ngFor="let row of rows" (click)="onRowSelected(row.name)">
        {{ row.name }}
    </h4>
</div>
<button (click)="isShowDropdown = !isShowDropdown;">*</button>

最后,在 app.component.html 中使用它:

<appDropdown #inp="ngModel" name="inp" [(ngModel)]="startValue"></appDropdown>