Search code examples
angularjsangulardata-bindinggetter-setter

Angular 2 keep input form value in bind with the model using getter and setter


I would like to bind an input value to the model using getter and setters. In this way i can prevent and/or manipulate the input's value while writing inside it.

For example i want the prevent numbers inside an input box. So, if write 'abc' all is ok, then if I start writing a number nothing should happen (to the model and to the input's value). The issue is that with the following code i'm able to write anything inside the input box (but the model it's correct). This means that the input box value is not really representing my model.

NOTE: The reason beyond this questions is that I want to use my models to validate forms, preventing for example specific characters. I would like to not use reactive forms as i want to keep my validations inside my models not components. Also note that in a real scenario i would have a UserModel class with inside name and other fields with their validations.

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2><input type="text" [(ngModel)]="name"> {{name}}</h2>
    </div>
  `,
})
export class App {
  _name:string = 'ss';
  constructor() {
  }

  // In real scenario those 2 methods are in a separate class UserModel
  get name() {
    return this._name;
  }

  set name() {
    if ((new RegExp(/^[a-zA-Z]*$/).test(val))) {
        this._name = val;
    }
  }
}

Solution

  • Based on @Günter Zöchbauer answer i made a workaround. It's not definitive and could be more abstract, but for now it's ok.

    export class App implements OnInit {
        @Input() userModel: UserModel = null;
        public _vm;
    
        constructor(private _changeDetectionRef: ChangeDetectorRef) {
        }
    
        /**
         * Initalize view model, it's important to keep names specular
         */
        ngOnInit() {
            this._vm = {
                name: this.userModel.name,
                surname: this.userModel.surname,
            };
        }
    
        /**
         * Helper for avoid detectchanges inside the modal, and reduce boilerplate. We could also ad an interface/type of the possibile field value, ie type fieldT= 'name' | 'surname';
         * @param field
         * @param val
         */
        protected updateModel(field, val: string): void {
            this._vm[field] = null;
            this._changeDetectionRef.detectChanges();
            this.userModel[field] = val;
            this._vm[field] = this.userModel[field];
            this._changeDetectionRef.detectChanges();
        }
    }
    

    In userModel:

    ....
    
        public get name(): string {
            return this.name';
        }
    
        public set name(val: string) {
            if ((new RegExp(/^[a-zA-Z]*$/).test(val))) {
                this.name = val;
            }
        }
    

    In template:

    <input type="text"  name="userName"  [ngModel]="_vm.name" (ngModelChange)="updateModel('name', $event)">