【问题标题】:Angular 11 Accessing template driven model in a directiveAngular 11在指令中访问模板驱动模型
【发布时间】:2021-08-03 02:48:05
【问题描述】:

Angular 8.2 中,我有一个货币指令,可以为用户格式化货币字段,并且可以完美地工作:

<input [(ngModel)]="currentEmployment.monthlyIncome" currency>
@Directive({
  selector: '[ngModel][currency]',
  providers: [CurrencyPipe, NgModel],
})
export class CurrencyDirective implements OnDestroy {
  // tracking
  private focused: boolean = false;
  private modelSubscription: Subscription;

  // fraction size
  @Input() size: number = 2;
  // positive only
  @Input() positiveOnly: boolean = false;

  // model update
  @Output() ngModelChange: EventEmitter<any> = new EventEmitter();

  constructor(
    private renderer: Renderer2,
    private el: ElementRef,
    private currencyPipe: CurrencyPipe,
    private model: NgModel
  ) {
    // set mobile keyboard to numpad
    this.renderer.setAttribute(this.el.nativeElement, 'pattern', 'd*');
    // input needs to be text when formatted
    this.renderer.setAttribute(this.el.nativeElement, 'type', 'text');

    // format when model is changed from somewhere else
    this.modelSubscription = this.model.valueChanges.subscribe(value => {
      // dont format while user is editing directly
      if (!this.focused) {
        this.setViewModel(this.currencyPipe.transform(value, this.size));
      }
    });
  }

  ngOnDestroy() {
    // unsubscribe from model subscription
    this.modelSubscription.unsubscribe();
  }

  @HostListener('focus')
  focus() {
    // user is editing, disable formating
    this.focused = true;
    // needs to be type number when unformatted
    this.renderer.setAttribute(this.el.nativeElement, 'type', 'number');
    // set the view model to raw data value
    this.setViewModel(this.model.value);

    // select all in input for easy editing
    $(this.el.nativeElement)
      .one('mouseup', event => {
        event.preventDefault();
      })
      .select();
  }

  @HostListener('blur')
  onBlur() {
    // prepare model value
    let newValue = Number(this.model.value);

    // get absolute value if only positive values are allowed
    if (this.positiveOnly) {
      newValue = Math.abs(this.model.value);
    }

    // emit model change before clearing this.focused
    this.ngModelChange.emit(newValue);
    // input needs to be text when formatted
    this.renderer.setAttribute(this.el.nativeElement, 'type', 'text');
    // format view model
    this.setViewModel(this.currencyPipe.transform(newValue, this.size));
    // clear user focus
    this.focused = false;
  }

  private setViewModel(value: any = null): void {
    if (value !== null) {
      this.el.nativeElement.value = value;
    }
  }
}

但是当更新到 Angular 11.2 我得到:

  • 没有错误
  • this.model.value 始终为空
  • this.model.valueChanges.subscribe 永远不会触发

是否存在一些配置问题或模型现在的工作方式不同?

【问题讨论】:

    标签: angular typescript angular-directive angular-forms angular-ngmodel


    【解决方案1】:

    原来我只需要从提供程序中删除 NgModel。 Angular 9 | ngModel Provider in Directive not working as expected

    我不完全明白为什么这会有所不同,但这解决了它:

    @Directive({
      selector: '[ngModel][currency]',
      providers: [CurrencyPipe],
    })
    export class CurrencyDirective implements OnDestroy {
      // tracking
      private focused: boolean = false;
      private modelSubscription: Subscription;
    
      // fraction size
      @Input() size: number = 2;
      // positive only
      @Input() positiveOnly: boolean = false;
    
      // model update
      @Output() ngModelChange: EventEmitter<any> = new EventEmitter();
    
      constructor(
        private renderer: Renderer2,
        private el: ElementRef,
        private currencyPipe: CurrencyPipe,
        private model: NgModel
      ) {
        // set mobile keyboard to numpad
        this.renderer.setAttribute(this.el.nativeElement, 'pattern', 'd*');
        // input needs to be text when formatted
        this.renderer.setAttribute(this.el.nativeElement, 'type', 'text');
    
        // format when model is changed from somewhere else
        this.modelSubscription = this.model.valueChanges.subscribe(value => {
          // dont format while user is editing directly
          if (!this.focused) {
            this.setViewModel(this.currencyPipe.transform(value, this.size));
          }
        });
      }
    
      ngOnDestroy() {
        // unsubscribe from model subscription
        this.modelSubscription.unsubscribe();
      }
    
      @HostListener('focus')
      focus() {
        // user is editing, disable formating
        this.focused = true;
        // needs to be type number when unformatted
        this.renderer.setAttribute(this.el.nativeElement, 'type', 'number');
        // set the view model to raw data value
        this.setViewModel(this.model.value);
    
        // select all in input for easy editing
        $(this.el.nativeElement)
          .one('mouseup', event => {
            event.preventDefault();
          })
          .select();
      }
    
      @HostListener('blur')
      onBlur() {
        // prepare model value
        let newValue = Number(this.model.value);
    
        // get absolute value if only positive values are allowed
        if (this.positiveOnly) {
          newValue = Math.abs(this.model.value);
        }
    
        // emit model change before clearing this.focused
        this.ngModelChange.emit(newValue);
        // input needs to be text when formatted
        this.renderer.setAttribute(this.el.nativeElement, 'type', 'text');
        // format view model
        this.setViewModel(this.currencyPipe.transform(newValue, this.size));
        // clear user focus
        this.focused = false;
      }
    
      private setViewModel(value: any = null): void {
        if (value !== null) {
          this.el.nativeElement.value = value;
        }
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-12-24
      • 2017-01-30
      • 2018-07-10
      • 2016-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多