Angular 的格式化程序和解析器、ng-true-value 和 ng-false-value 指令变得可行

问题描述 投票:0回答:1

目前我创建了两个项目指令。但是一个指令用于如果复选框被选中,我将得到 1,如果选中为 false,我将得到 0。另一个指令用于将值“1”转换为 1。

我只使用 TrueFalseValueDirective 几个复选框

实现目标的意图 格式化器和解析器 - NumberDirective Angular 中的“ng-true-value”和“ng-false-value”替代方案 - TrueFalseValueDirective

问题:

这两个指令都不起作用。任一指令都有效。我需要使两个指令可行

模板:

<form #f="ngForm">
<input type="checkbox" [(ngModel)]="data.option" name="options" checked="data.checked" trueFalseValue>

<select  [(ngModel)]="data.selection" name="selection">
<option value="1">option 1</option>
<option value="2">option 2</option>
<option value="3">option 3</option>
</select>

<input type="text"  name="Text1" [disabled]="data.selection!==1" [(ngModel)]="data.Text1" />
<input type="text"  name="Text2" [disabled]="data.selection!==2" [(ngModel)]="data.Text2" />
<input type="text"  name="Text3" [disabled]="data.selection!==3" [(ngModel)]="data.Text3" />
</form>

@Directive({
      selector: '[ngModel]',     
    })
export class NumberDirective implements ControlValueAccessor {
      onChange;
    
      constructor( private renderer : Renderer2, 
                   private element : ElementRef, public ngControl: NgControl ) {
    
    if (ngControl) {
            ngControl.valueAccessor = this;
        }
      }
      @HostListener('input', [ '$event.target.value' ])
      input( value ) {
        this.onChange(parsrInt(value, 10);
      }
      writeValue( value : any ) : void {
        const element = this.element.nativeElement;
        this.renderer.setProperty(element, 'value', String(value));
      }
    
      registerOnChange( fn : any ) : void {
        this.onChange = fn;
      }
    
    }

import {
  Directive,
  Input,
  forwardRef,
  HostListener,
  ElementRef,
  Renderer2
} from '@angular/core';
import {
  ControlValueAccessor,
 
  NgControl
} from '@angular/forms';

@Directive({
  selector: 'trueFalseValue',
 
})
export class TrueFalseValueDirective implements ControlValueAccessor {
  private propagateChange = (_: any) => {};
  trueValue = 1; // some time true
 falseValue = 0;  // sometime true


  constructor(private elementRef: ElementRef, private renderer: Renderer2, public ngControl: NgControl, ) {
if (ngControl) {
        ngControl.valueAccessor = this;
    }}

  @HostListener('change', ['$event'])
  onHostChange(ev) {
    this.propagateChange(ev.target.checked ? this.trueValue : this.falseValue);
  }

  writeValue(obj: any): void {
    if (obj === this.trueValue) {
      this.renderer.setProperty(this.elementRef.nativeElement, 'checked', true);
    } else {
      this.renderer.setProperty(this.elementRef.nativeElement, 'checked', false);
    }
  }

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

  registerOnTouched(fn: any): void {}

  setDisabledState?(isDisabled: boolean): void {}
}

参考网址:

  1. https://netbasal.com/angular-formatters-and-parsers-8388e2599a0e
  2. https://juri.dev/blog/2018/02/ng-true-value-directive/
angular angular-directive angular-forms angular-template-form
1个回答
0
投票

这里有两种方法来解决这个问题,我更喜欢第一个选项,因为它适用于绑定数据和表单。


方法一:

您采取的方法是部分正确的,因为写入值是模拟格式化程序和解析器的正确方法,但是您可以创建一个自定义的自定义方法,而不是弄乱内部角度

ngModel
指令,这可能会导致整个应用程序出现奇怪的错误可以执行相同功能的组件。

我创建了一个自定义表单字段,它将接受 true 和 false 值作为输入,并根据复选框选择来设置它们,这种方法似乎是最好的,唯一的缺点是您需要创建一个组件。

完整代码:

import {
  Component,
  Directive,
  HostBinding,
  HostListener,
  Input,
  Output,
  EventEmitter,
} from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import {
  FormsModule,
  NG_VALUE_ACCESSOR,
  NgControl,
  ControlValueAccessor,
} from '@angular/forms';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-true-false-value',
  standalone: true,
  template: `
    <input type="checkbox" [checked]="_value === this.trueValue" (input)="valueChange($event)"/>
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      multi: true,
      useExisting: TrueFalseValueComponent,
    },
  ],
})
export class TrueFalseValueComponent implements ControlValueAccessor {
  _value = 0;
  @Input()
  value: any;
  onChange = (value: any) => {};
  onTouched = () => {};
  touched = false;
  disabled = false;
  @Input() trueValue: any = 1; // some time true
  @Input() falseValue: any = 0; // sometime true

  writeValue(value: number) {
    this._value = value;
  }

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

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

  markAsTouched() {
    if (!this.touched) {
      this.onTouched();
      this.touched = true;
    }
  }

  setDisabledState(disabled: boolean) {
    this.disabled = disabled;
  }

  valueChange(event: any) {
    const value = event.target.checked;
    this.markAsTouched();
    this.onChange(value ? 'Y' : 'N');
  }
}

@Component({
  selector: 'app-root',
  imports: [FormsModule, TrueFalseValueComponent, CommonModule],
  standalone: true,
  template: `
    <form #f="ngForm">
        <app-true-false-value name="options" [(ngModel)]="data.option"  falseValue='N' trueValue='Y' trueFalseValue/>
        <br/>
        <select  [(ngModel)]="data.selection" name="selection">
        <option [ngValue]="1">option 1</option>
        <option [ngValue]="2">option 2</option>
        <option [ngValue]="3">option 3</option>
        </select>
        <br/> 
        <input type="text"  name="Text1" [disabled]="data.selection!==1" [(ngModel)]="data.Text1" />
        <input type="text"  name="Text2" [disabled]="data.selection!==2" [(ngModel)]="data.Text2" />
        <input type="text"  name="Text3" [disabled]="data.selection!==3" [(ngModel)]="data.Text3" />
    </form>

    {{f.form.value | json}}
  `,
})
export class App {
  name = 'Angular';
  data = {
    option: 'Y',
    selection: 1,
    Text1: 'Text1',
    Text2: 'Text2',
    Text3: 'Text3',
  };
}

bootstrapApplication(App);

Stackblitz 演示


方法二:

AngularJS formatters and parsers
,部分被 Angular 的
getters and setters
取代。顾名思义,当访问值或设置值时会调用该方法。

如 stackblitz 中所示,此方法的问题在于,数据对象已完美更新,但表单未使用正确的值进行更新。

get optionTransformed() {
  return this.data.option === 'Y';
}

set optionTransformed(value: boolean) {
  this.data.option = value ? 'Y' : 'N';
}

在 HTML 上,我们将属性设置为

ngModel
,它既访问数据又设置数据,这里我们应用转换。

<input type="checkbox" name="options" [(ngModel)]="optionTransformed"/>

完整代码:

import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-root',
  imports: [FormsModule, CommonModule],
  standalone: true,
  template: `
    <form #f="ngForm">
        <input type="checkbox" name="options" [(ngModel)]="optionTransformed"/>
        <br/>
        <select  [(ngModel)]="data.selection" name="selection">
        <option [ngValue]="1">option 1</option>
        <option [ngValue]="2">option 2</option>
        <option [ngValue]="3">option 3</option>
        </select>
        <br/> 
        <input type="text"  name="Text1" [disabled]="data.selection!==1" [(ngModel)]="data.Text1" />
        <input type="text"  name="Text2" [disabled]="data.selection!==2" [(ngModel)]="data.Text2" />
        <input type="text"  name="Text3" [disabled]="data.selection!==3" [(ngModel)]="data.Text3" />
    </form>
    <br/>
    <br/>
    <br/>
    Form: 
    <br/>
    {{f.form.value | json}}
    <br/>
    <br/>
    <br/>
    <br/>
    Data:
    <br/>

    {{data | json}}
  `,
})
export class App {
  name = 'Angular';
  data = {
    option: 'Y',
    selection: 1,
    Text1: 'Text1',
    Text2: 'Text2',
    Text3: 'Text3',
  };

    get optionTransformed() {
      return this.data.option === 'Y';
    }

    set optionTransformed(value: boolean) {
      this.data.option = value ? 'Y' : 'N';
    }
}

bootstrapApplication(App);

Stackblitz 演示

© www.soinside.com 2019 - 2024. All rights reserved.