Stepan Suvorov
Stepan Suvorov

Reputation: 26146

Parsers and Formatters in Angular2

What is the way of doing parsers and formatter in Angular2?

in Angular1 it was possible to do such manipulations with ngModelController:

//model -> view
ngModelController.$formatters.push(function(modelValue) {
  return modelValue.toUpperCase();
});

//view -> model
ngModelController.$parsers.push(function(viewValue) {
  return viewValue.toLowerCase();
});

could you provide me an example how to do it with Angular2?


UPD: Pipes are similar to Filters in Angular1, but I'm looking not for Filters, but for Parsers and Formatters for ngModel. So "Pipes" is not correct answer.

Upvotes: 22

Views: 8992

Answers (5)

sonnenhaft
sonnenhaft

Reputation: 1647

Methods writeValue(val: T) and updateChanges are your parsers and formatters

Much more heavy in ng2+ (honestly taken from ng9 project) look ngModel extension, here is short monster sample (not runnable - Stack Overflow not supporting ng2+):

import { Component, forwardRef, Input } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'multi-text-input',
  styles: [``],
  template: `
    <div *ngFor="let val of inputs; let idx = index" style="display: flex">
      <input type="text" pInputText [(ngModel)]="val.val" (ngModelChange)="updateChanges()" (mousedown)="onTouched()" />
    </div>
  `,
  // thx to google for this providers section below
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => MultiTextInput),
      multi: true,
    },
  ],
})
export class MultiTextInput<T = string | string[]> {
  inputs: { val: string }[] = [];
  @Input() separator = ',';

  writeValue(value): void {
    value = value || '';
    const val: string[] = Array.isArray(value) ? value : value.split(this.separator);
    this.inputs = val.filter((val) => val.trim()).map((val) => ({ val }));

    this.value = value;
  }

  updateChanges() {
    const arr = this.inputs.map(({ val }) => val.trim()).filter(Boolean);
    // ts-ignore to match T, but u don't even need it
    // @ts-ignore
    this.onChange(Array.isArray(this.value) ? arr : arr.join(this.separator));
  }

  // thx to google for all generic lines below
  value: T;

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

  registerOnTouched(fn): void {
    this.onTouched = fn;
  }

  onChange: (_: T) => void = (_: T) => {};
  onTouched: () => void = () => {};
}

Import, then <multi-text-input [(ngModel)]="variable"/> instead of ngModel can be formControl attribute also. If this is not working for you, please comment, and I will give a better sample.

Upvotes: 0

Ankit Singh
Ankit Singh

Reputation: 24945

could you provide me an example how to do it with Angular2?

                                            a.) model -> view


     1> Using Pipes

TS:

myString: string = "ABCDEFGH";

Template:

{{myString | lowercase}}

Output:

abcdefgh

     2> Using transformation directly

Template:

Below Input field will have lowercase string as value

<input type="text" [value]="myString.toLowerCase()">

I'm also lowercase:  {{myString.toLowerCase()}}

Output:

Input field with value "abcdefgh"

I'm also lowercase:  abcdefgh

     3> Using Getter/Setter

TS:

myString: string = "ABCDEFGH";

get stillMyString() {
  return this.myString.toLowerCase();
}
set stillMyString(v) {
  this.myString = v;
}

Template:

{{stillMyString}}

Output:

abcdefgh

     4> Using Directives


     5> Using ControlValueAccessor


     OR using a combination of any of the above


                                            b.) view -> model


     1> Using Output/Events

Template:

Below Input field will get lowercase string as value but will store uppercase string

<input type="text" [value]="myString.toLowerCase()" (change)="myString = $event.toUpperCase()">

I'm give uppercase values automatically:  {{myString}}

Output:

Input field with initial value "abcdefgh"

I'm given uppercase values automatically:  ABCDEFGH

     2> Using Setter/Getter

TS:

myString: string = "ABCDEFGH";

get stillMyString() {
  return this.myString;
}
set stillMyString(s) {
  this.myString = s.toUpperCase();
}

Template:

Below Input field will get lowercase string as value but will store uppercase string

<input type="text" [value]="stillMyString.toLowerCase()" (change)="myString = $event">

Now I'm Uppercase:  {{stillMyString}}

Output:

Input field with initial value "abcdefgh"

I'm given uppercase values automatically:  ABCDEFGH

AND/OR a combination of above methods and any other method that I can't think of right now.


                                            Wrapping UP

  • As you can see there are multiple ways to do the same thing, it just depends upon your need and choice to use any of it.

  • Validation is not the concern of transformation, but you can do it by improving upon the getter/setters and using FormControl on your input fields

  • I could just show you the tip of the iceberg here, there is so much to model <> view transformations, becasuse that's what Angular does, Data Binding, one way or two way.

Hope It Helps :)

Upvotes: 14

Vikash Dahiya
Vikash Dahiya

Reputation: 5801

There is no such concept of formatters or parsers in Angular 2 according to me but you can implement it using following code, its very simple

In HTML

 <input type="text"  [ngModel] = "formatter(ex)"  (ngModelChange)="parser($event)">

in code

export class Component{
     data:string = 'data';
     constructor(){}

     formatter(value){
        value = value.toUpperCase();   //manipulate the data according to your need
        return value;
     }

     parser(value){
         this.data = value.toLowerCase();  //manipulate the data according to your need
     }



}

you can also implement array of functions according to your need to fully implement $formatters and $parsers.

Upvotes: 5

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657018

According to angular 2 official documentation, pipes are renamed angular 1 filters. You don't use filters in angular 1 to convert viewModel to model, and vice versa. Mostly you use filters to filter or format data for templates, not for two-way data passing.

I have never worked with Angular1 and don't know how stuff works there.

I think what you are looking for is ControlValueAccessor that makes custom components work with ngModel and Angular2 forms and allows to map between display and model format of the value.

Plunker example from https://github.com/angular/angular/issues/10251#issuecomment-238737954

This is a bit more complex example than your question might be about, where a component contains a sub-form. A component can just be a form control as well.

The Plunker also doesn't yet use the now required NgModule but should be easy enough to migrate (I'll have another look myself later)

And how does validation relate to pipes? If user change is invalid, will it be piped anyway? That is hardly what would be wanted.

Validation doesn't relate to pipes at all.

I want to see a custom component with validation. The component should have different representation for user editable value, and different representation for model value that is passed outside the component.

The Address component above also implements custom validation

From the comment in https://github.com/angular/angular/issues/10251#issuecomment-238737954

Adding validation has a similar process to value accessors. You implement the validator interface (just a validate() function) and provide it as an NG_VALIDATOR on your custom form control. Here's an example:

See also

Upvotes: 2

rook
rook

Reputation: 3008

In Angular2 you use pipes. See documentation: https://angular.io/docs/ts/latest/guide/pipes.html

Upvotes: -3

Related Questions