2

hello guys I am trying to create a custom directive in angular 2 which will change the value of an input ngModel to percent for example

number / 100

example image

this is an example image of what I am trying to achieve.

For example the user sees the 5.12 but the value that will be send in the back end should be 5.12 / 100, but the user doesn't see this

I have come to an approach but I think that is a wrong approach can you guys help me out.

this is my code.

for example:

when it comes from backend to be multiplied by *100 and when it goes to backend to be divided by /100

  • [implement a custom `ControlValueAccessor`.](http://stackoverflow.com/questions/42254077/angular-2-date-pipe-inside-a-formcontrol-input/42254218#42254218) The question is about dates, but that's the same approach. – n00dl3 Apr 10 '17 at 09:17
  • @n00dl3 I was thinking for that too, would that make me any problems if than later comes to dynamic content ? –  Apr 10 '17 at 09:19
  • What do you mean by "dynamic content" ? – n00dl3 Apr 10 '17 at 09:19
  • @n00dl3 for example if I have to handle events in the custom input –  Apr 10 '17 at 09:21
  • the custom input is a directive or a component, so you are free to do what you want regarding event handling, even creating custom ones if needed. Note that DOM events are bubbling, so even in a really complex component, you will be able to "catch" them from the outside world... – n00dl3 Apr 10 '17 at 09:22
  • Add the code inside question rather than providing link. That will help community help you better. – Satish Prakash Garg Apr 10 '17 at 09:49

3 Answers3

4
import { Directive, ElementRef, Input, forwardRef, EventEmitter } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgModel } from "@angular/forms";

@Directive({
    selector: '[ngModel][toPercentage]',
    providers: [{
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() => ToPercentDirective),
        multi: true
    }],
    host: {
        "[value]": 'ngModel',
        '(blur)': 'onBlur()'
    }
})
export class ToPercentDirective implements ControlValueAccessor {

    constructor(private el: ElementRef) { }

    private innerValue: string;
    private toNumber: number;
    private toPercent: number;

    public onChangeCallback: any = (_) => { console.log(_) }
    public onTouched: any = () => { /*Empty*/ }

    onBlur() {
        var input = this.el.nativeElement.value;
        this.toPercent = parseFloat(input) / 100;

        if (input != this.toNumber) {
            this.onChangeCallback(this.toPercent);
        }
    }

    get value(): any {
        console.log(this.innerValue);
        return this.innerValue;
    };

    //set accessor including call the onchange callback
    set value(v: any) {
        if (v !== this.innerValue) {
            this.innerValue = v;
            this.onChangeCallback(v);
        }
    }

    writeValue(val: string): void {
        // this.ngModel.ControlValueAccessor.writeValue(val);
        this.toNumber = parseFloat(val) * 100;
        this.el.nativeElement.value = this.toNumber;
    }

    registerOnChange(fn: any): void {
        // console.log(fn);
        this.onChangeCallback = fn;
    }

    registerOnTouched(fn: any): void {
        // console.log(fn);
        this.onTouched = fn;
    }
}

I am posting this answer for anyone facing a similar issue.

it handles this problem: converts a number * 100 when it comes from backend and converts a number / 100 when it sends to backend.

but the user doesn't see these operations.

for example:

response  = 0.5

toNumber = 0.5 * 100 -> <input number> 50 
toPercent= 50 / 100 -> <input number> 50, value = 0.5

hope it helps anyone

Simon C
  • 109
  • 1
  • 8
0

Hope this will be of some use.

https://blog.ngconsultant.io/custom-input-formatting-with-simple-directives-for-angular-2-ec792082976

Franklin Pious
  • 3,670
  • 3
  • 26
  • 30
0

You can add a EventEmitter to directive that outputs the changed value, but you shouldn't change the ngModel value in the directive. The [ngModel]="employerShare" that should be added to the component to hold the percentage value.

This is Plunker

ToPercentageDirective:

@Directive({
  selector: '[ngModel][toPercentage]',
  providers: [NgModel],

})
export class ToPercentageDirective implements OnInit {


@Output() sharedVarChange = new EventEmitter();

  constructor(private el: ElementRef, 
              private ngModel: NgModel) { }

  ngOnInit() {
    let that = this;
    let toPercent:any;


    this.ngModel.valueChanges.subscribe(function (value){
      toPercent = value / 100;

      // that.ngModel.update.emit(toPercent);
      that.sharedVarChange.emit(toPercent);
    });


    // $(that.el.nativeElement.value).bind(function(newVal){
    //     newVal = toPercent;
    //     that.ngModel.update.emit(newVal);

    //     console.log(newVal);
    // });

  }


  // @HostListener("focus", ["$event.target.value"])
  // onFocus(value) {
  //   this.elementRef.nativeElement.value  = this.convertPercentage(value);
  // }

  // onInputChange(event) {
  //   console.log('Change');
  // }

  // @HostListener("blur", ["$event.target.value"])
  // onBlur(value) {
  //   this.elementRef.nativeElement.value  = this.convertPercentage(value);
  // }

  convertPercentage(number){
        return number / 100;
    }

}

App component:

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Example</h2>

      <form>

        <input type="number" class="form-control" id="employerShare" required
                            name="data.insObj.stat['AHV,IV,EO'].employerShare" placeholder="5.125%"
                                    [ngModel]="employerShare"
                                    (ngModelChange)="onChange($event)" 
                                    toPercentage 
                                    (sharedVarChange)="onSharedVarChange($event)">


      </form>
    </div>
  `,
})
export class App {
  employerShare: number;
  data = {};

  constructor() {

  }

  onChange ($event){
    console.log("onChange: " + $event);

  }

  onSharedVarChange ($event){
    this.data.employerShare = $event;
    console.log("onSharedVarChange: " + this.data.employerShare);

  }
}
Roman C
  • 49,761
  • 33
  • 66
  • 176