Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to intercept the value of FormControl before setting or getting it?

The question is pretty self-explanatory. I would like to intercept the incoming value for a FormControl's value property, and be able to intercept the outgoing value to the HTML control that it's hooked up to.

Let's say I have a FormControl named "firstName" and I hook it up to a textbox as such:

<input type="text" formControlName="firstName" />

By default, when the user inputs the value in the textbox and submits, the FormControl's value gets set to the value in the textbox. Is there any way I can intercept the value that gets set and modify it before setting it?

Similarly, is there any way to intercept the value that the FormControl sends to the HTML control? For example if I have a FormControl's value set to something but I want to modify the value that shows up in the textbox.

I know I can use ngModel to act as a mediator between the form and the control, but that gets cumbersome when using more than a few controls. I also know you can create your own control and implement the ControlValueAccessor, but this is also cumbersome as I would have to create a corresponding control for each control I want to use.

For more information about why I'm asking this question, see https://github.com/ionic-team/ionic/issues/7121

like image 231
Anshul Avatar asked Aug 08 '17 23:08

Anshul


People also ask

How do you access value from FormControl?

To fetch the value of a form control, we have to use value property on the instance of FormControl in our class. In the same way we can fetch the value in HTML template.

What is the purpose of the ValueChanges method on a FormControl?

The ValueChanges is an event raised by the Angular forms whenever the value of the FormControl, FormGroup or FormArray changes. It returns an observable so that you can subscribe to it. The observable gets the latest value of the control. It allows us to track changes made to the value in real-time and respond to it.

What is the first parameter in New FormControl ()?

The first parameter, is the FormState, in which we can set the Form Control initial value and if it should be disabled initially.


1 Answers

You can write a reusable directive which intercepts the value coming from and going into the view:

const MODIFIER_CONTROL_VALUE_ACCESSOR: Provider = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => ValueModifierDirective),
  multi: true,
};

@Directive({
  selector: '[valueModifier]',
  host: { '(keyup)': 'doSomething($event)' },
  providers: [MODIFIER_CONTROL_VALUE_ACCESSOR],
})
export class ValueModifierDirective implements ControlValueAccessor {

  @Input() valueModifier: [Function, Function];

  private writeToForm;

  constructor(public _el: ElementRef) { }

  doSomething(event: Event) {
    const viewToForm = this.valueModifier[0];
    this.writeToForm(viewToForm(event.target.value));
  }

  registerOnChange(fn: (value: any) => void) {
    this.writeToForm = fn;
  }

  registerOnTouched(fn: any) {
    // nothing to do
  }

  writeValue(value: any) {
    const formToView = this.valueModifier[1];
    this._el.nativeElement.value = formToView(value);
  }
}

To use it, just add the directive to the same element you apply formControlName on and pass the transform-functions:

@Component({
  selector: 'my-app',
  template: `
  <form [formGroup]="form">
  <input [valueModifier]="[viewToForm, formToView]" name="value" type="text" formControlName="input"  />
  <button (click)="random()">Set Random Value</button>
  </form>
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  form = new FormGroup({
    input: new FormControl(1)
  });
  viewToForm = (text: string) => "toForm" + text;
  formToView = (text: string) => "toView" + text;

  constructor() {
    this.form.valueChanges.subscribe(value => console.log(value));
  }

  random () {
    this.form.patchValue({
      input: Math.random()
    })
  }
}

Live example (Stackblitz):

https://stackblitz.com/edit/angular-afmkxl?file=src%2Fapp%2Fapp.component.ts

The above works for text inputs. I think you can write similar directives for other kinds of inputs.

like image 121
dummdidumm Avatar answered Sep 21 '22 20:09

dummdidumm