model values not trim automatically in angular 2

2020-02-28 04:59发布

问题:

I am using angular 2 form validation, I set required validation on text box, when I enter nothing into textbox it shows required error message it is ok but when I enter only spaces then it does not show required error message, which means angular 2 not trim the model values.

In angular 1.x it automatically trims the model values but in angular 2 I don't see this feature.

回答1:

Well, there is a long discussion on github with the following result: we must implement our own validators.

This is what I use so far:

import { ValidatorFn, AsyncValidatorFn, Validators as V, FormControl } from '@angular/forms';

// the need in this validators is the non-trimming angular standard behavior
// see https://github.com/angular/angular/issues/8503
export class Validators {

  public static required(control: FormControl) {
    if (!control.value || typeof control.value === 'string' && !control.value.trim()) {
      return {
        required: true
      };
    }

    return null;
  }

  public static minLength(length: number): ValidatorFn {
    return (control: FormControl) => {
      if (!control.value || typeof control.value === 'string' && control.value.trim().length < length) {
        return {
          minlength: true
        };
      }

      return null;
    };
  }

  public static maxLength(length: number): ValidatorFn {
    return (control: FormControl) => {
      if (control.value && typeof control.value === 'string' && control.value.trim().length > length) {
        return {
          maxlength: true
        };
      }

      return null;
    };
  }

  public static pattern(pattern: string): ValidatorFn {
    return V.pattern(pattern);
  }

  public static minAmount(amount: number): ValidatorFn {
    return (control: FormControl) => {
      if (control.value && control.value.length < amount) {
        return {
          minamount: true
        };
      }

      return null;
    };
  }

  public static maxAmount(amount: number): ValidatorFn {
    return (control: FormControl) => {
      if (control.value && control.value.length > amount) {
        return {
          maxamount: true
        };
      }

      return null;
    };
  }

  public static compose(validators: ValidatorFn[]): ValidatorFn {
    return V.compose(validators);
  }

  public static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn {
    return V.composeAsync(validators);
  }

};

This mimics the standard maxLength, minLength and required validations for string input but with trimming and proxies other functions to the standard ones.

To use it just import your Validators instead of @angular/forms one, e.g.:

import { FormControl } from '@angular/forms';
import { Validators } from 'path/to/validators';

...
let control = new FormControl('', Validators.compose(
  Validators.required, Validators.minLength(6)
));
...

This maybe does not trim the model but it solves the validation problem specified in the request.



回答2:

I think that you need to implement a custom value accessor for this. Something like that:

const TRIM_VALUE_ACCESSOR = new Provider(
  NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => TrimValueAccessor), multi: true});

@Directive({
  selector: 'input[trim]',
  host: { '(keyup)': 'doOnChange($event.target)' },
  providers: [ TRIM_VALUE_ACCESSOR ]
})
export class TrimValueAccessor extends DefaultValueAccessor {
  onChange = (_) => {};
  onTouched = () => {};

  constructor(private renderer:Renderer) {
  }

  writeValue(value:any):void {
    if (value!=null) {
      super.writeValue(value.toString().trim());
    }
  }

  doOnChange(elt) {
    let val = elt.value.trim();
    this.renderer.setElementProperty(elt, 'value', val);
    this.onChange(val);
  }
}

When you want to use this value accessor, add the corresponding directive into your component and the trim attribute on your input:

@Component({
  (...)
  template: `
    <input type="text" trim/>
  `,
  directives: [ TrimValueAccessor ]
})

See this article for more details (section "NgModel-compatible component"):

  • http://restlet.com/blog/2016/02/17/implementing-angular2-forms-beyond-basics-part-2/


回答3:

Just my two cents:

  • https://www.npmjs.com/package/ng2-trim-on-blur

  • https://www.npmjs.com/package/ngx-trim-directive

Both directives lie on a simple fact that Angular listens to input event to bring the view-to-model binding into being (not for cases of blur or submit updateOn options. These cases are also handled).

The second one also deals with the caret position if the trim is triggered by input event.

These directives leave the states of dirty and touched to be handled by the original Angular ValueAccessor, so you won't meet with some strange behaviors.

ngx-trim-directive Demo: https://angular-86w6nm.stackblitz.io, editor: https://stackblitz.com/edit/angular-86w6nm



回答4:

You can simply install the TrimValueAccessor npm package that I created:

npm install ng-trim-value-accessor --save

and then import it into your application:

import { NgModule } from '@angular/core';
import { TrimValueAccessorModule } from 'ng-trim-value-accessor';

@NgModule({
  imports: [
    TrimValueAccessorModule
  ]
})
export class AppModule { }


回答5:

Here is the code for Angular4 or install it from npm. https://www.npmjs.com/package/ngx-trim

const TRIM_VALUE_ACCESSOR = {
  provide: NG_VALUE_ACCESSOR,
  // tslint:disable-next-line:no-forward-ref
  useExisting: forwardRef(() => NgxTrimDirective),
  multi: true
};

@Directive({
  selector: 'input[ngxTrim]',
  providers: [TRIM_VALUE_ACCESSOR]
})
export class NgxTrimDirective implements ControlValueAccessor {
  _onChange(_: any) { }
  _onTouched() { }
  registerOnChange(fn: (value: any) => any): void { this._onChange = fn; }
  registerOnTouched(fn: () => any): void { this._onTouched = fn; }
  constructor (
    private _renderer: Renderer2,
    private _elementRef: ElementRef
  ) { }

  writeValue(value: any): void {
    value && this._renderer.setProperty(this._elementRef.nativeElement, 'value', value);
  }

  @HostListener('keyup', ['$event.target'])
  _onKeyUp(element: HTMLInputElement) {
    let val = element.value.trim();
    this.writeValue(val);
    this._onChange(val);
  }
}

BTW, I think it should implement ControlValueAccessor instead of extending DefaultValueAccessor.



回答6:

you can give a try (blur)="modelName= modelName.trim();". it will trim white spaces on blur event. you can change event according to your need.



回答7:

Another solution might be to put the trim logic inside a FormControl wrapper. This approach is simple, doesn't require third party dependencies and works with default validators such as required or minLength.

export class TrimFormControl extends FormControl {
    private _value!: string | null;

    get value(): string | null {
        return this._value;
    }

    set value(value: string | null) {
        this._value = value ? value.trim() : value;
    }
}


回答8:

Hello try this in HTML or JSP

(keydown.space)="$event.preventDefault()"

this will not allow space in text box.

This worked for me.