【问题标题】:Manually trigger input change event手动触发输入变化事件
【发布时间】:2020-10-05 15:26:19
【问题描述】:

我正在尝试制作一个自定义计数器输入组件,但我正在努力让输入值由自定义递增/递减按钮控制。

这就是我想要做的:

我想使用内容投影并公开输入,以便在表单中使用它并添加属性,就像在常规数字输入上一样。所以关于输入的指令似乎是要走的路:

<my-counter-input>
  <input [(ngModel)]="count" myInputRef />
</my-counter-input>

组件本身会有两个按钮和一个ng-content-slot:

<button (click)="decrement()">-</button>
<ng-content select="input"></ng-content>
<button (click)="increment()">+</button>

到目前为止,一切似乎都很好。现在我可以使用 @ContentChild 装饰器与指令进行交互。

@Component({
  selector: 'my-counter-input',
  templateUrl: './counter-input.component.html',
  styleUrls: ['./counter-input.component.scss'],
})
export class CounterInputComponent {
  @ContentChild(InputRefDirective)
  public input: InputRefDirective;

  constructor() {}

  public increment() {
    this.input.increment();
  }
  public decrement() {
    this.input.decrement();
  }
}

我想这就是问题所在。我将输入元素的值绑定到指令,但增量/减量方法都不会对本机输入值产生任何影响。我想对本机输入值进行双向绑定,但似乎情况并非如此。

@Directive({
  selector: 'input [myInputRef]',
})
export class InputRefDirective {
  @HostBinding('type') readonly type = 'number';

  @HostBinding('value') value = 5;

  public increment() {
    this.value++;
  }
  public decrement() {
    this.value--;
  }
}

我不知道该怎么做。如何更新原生输入的值并触发原生更改事件?

【问题讨论】:

    标签: angular typescript angular-directive


    【解决方案1】:

    使用NgControl

    所有基于 FormControl 的指令扩展的基类。它绑定了一个 FormControl 对象到 DOM 元素。

    我们可以在指令中注入 NgControl,Angular DI 框架将为我们提供最接近的表单控制指令。然后我们就可以动态设置formControl的值了。

    import { Directive, HostBinding, AfterViewInit } from "@angular/core";
    import { NgControl } from "@angular/forms";
    
    @Directive({
      selector: "[appInput]"
    })
    export class InputDirective implements AfterViewInit {
      @HostBinding("type") readonly type = "number";
    
       value = 5;
     
    
      ngAfterViewInit(){
         setTimeout(()=>{this.control.control.setValue(this.value)})
      }
    
      constructor(private control: NgControl) {
        
      }
    
      public increment() {
        this.control.control.setValue(this.value++);
      }
      public decrement() {
        this.control.control.setValue(this.value--);
      }
    }
    

    Forked Working Example

    【讨论】:

    • 谢谢!我最终将它与另一个答案结合起来,并有一个可选的 NgControl 和可选的 NgModel 依赖项。
    【解决方案2】:

    您可以告诉 ngModel 在初始化和每次更改时更新

    public increment() {
      this.value++;
      this.updateNgModel(this.value);
    }
    
    public decrement() {
      this.value--;
      this.updateNgModel(this.value);
    }
    
    private updateNgModel(value: number) {
      this.ngModel.update.emit(value);
    }
    

    https://stackblitz.com/edit/angular-ivy-mw2ltg?file=src/app/input-ref.directive.ts

    【讨论】:

    • 谢谢!我最终将它与另一个答案结合起来,并有一个可选的 NgControl 和可选的 NgModel 依赖项。
    【解决方案3】:

    除非您必须使用内容投影并让父级提供输入控件/模板,否则您可以将可重用组件创建为处理所有事情的独立组件。这种方式稍微简单一些。 Here is a forked example.

    但是已经给出的其他两个示例确实解决了您面临的更新问题。

    【讨论】:

    • 谢谢!我正在考虑这一点,但我发现在组件中隐藏输入存在一些问题。
    • 我很想了解更多关于这些问题的信息。我们已经很好地将这种模式用于可共享的组件。您还可以使用 ControlValueAccessor 模式使输入与响应式表单一起工作(而不是如图所示执行输出事件)。该策略可能会有所帮助?
    • 这是另一个分叉版本,为了简单起见,它接受 FormControl 作为输入(而不是执行 controlValueAccessor),并且易于在表单中重用 - stackblitz.com/edit/…。这样,您可以使用响应式表单并将控制行为封装在可重用的组件中。也许这有帮助:)
    • 我主要指的是this article中提出的问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-19
    • 1970-01-01
    • 1970-01-01
    • 2019-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多