2017-04-21 16 views
1

更新問題はControlValueAccessorの実装にあり、その後、ControlValueAccessorを子要素に適用することに問題があると判断されました。質問は反映するように編集されました。イオン入力の通貨入力ディレクティブの実装方法

通貨の値を「ドル」形式(例:10.00)で表示するが、下位のモデルにセント(たとえば1000)形式で格納する属性ディレクティブを提供したいと考えています。

<!-- cost = 1000 would result in text input value of 10.00 
<input type="text" [(ngModel)]="cost" name="cost" currencyInput> 
<!-- or in Ionic 2 --> 
<ion-input type="text" [(ngModel)]="cost" name="cost-ionic" currencyInput> 

以前AngularJS 1.xでIは、解析を使用すると、次のように指示リンク機能でレンダリング:

(function() { 
    'use strict'; 

    angular.module('app.directives').directive('ndDollarsToCents', ['$parse', function($parse) { 
     return { 
      require: 'ngModel', 
      link: function(scope, element, attrs, ctrl) { 
       var listener = function() { 
        element.val((value/100).toFixed(2)); 
       }; 

       ctrl.$parsers.push(function(viewValue) { 
        return Math.round(parseFloat(viewValue) * 100); 
       }); 

       ctrl.$render = function() { 
        element.val((ctrl.$viewValue/100).toFixed(2)); 
       }; 

       element.bind('change', listener); 
      } 
     }; 
    }]); 
})(); 

イオン2で/角度2 IはControlValueAccessorインタフェースを使用してこれを実現以下有します。

import { Directive, Renderer, ElementRef, forwardRef } from '@angular/core'; 
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; 

const CURRENCY_VALUE_ACCESSOR = { 
    provide: NG_VALUE_ACCESSOR, 
    useExisting: forwardRef(() => CurrencyInputDirective), 
    multi: true 
} 

@Directive({ 
    selector: '[currencyInput]', 
    host: { 
     '(input)': 'handleInput($event.target.value)' 
    }, 
    providers: [ CURRENCY_VALUE_ACCESSOR ] 
}) 
export class CurrencyInputDirective implements ControlValueAccessor, AfterViewInit 
{ 
    onChange = (_: any) => {}; 
    onTouched =() => {}; 
    inputElement: HTMLInputElement = null; 

    constructor(private renderer: Renderer, private elementRef: ElementRef) {} 

    ngAfterViewInit() 
    { 
     let element = this.elementRef.nativeElement; 

     if(element.tagName === 'INPUT') 
     { 
      this.inputElement = element; 
     } 
     else 
     { 
      this.inputElement = element.getElementsByTagName('input')[0]; 
     } 
    } 

    registerOnChange(fn: (_: any) => void): void { this.onChange = fn; } 
    registerOnTouched(fn:() => void): void { this.onTouched = fn; } 

    handleInput(value : string) 
    { 
     if (value) 
     { 
      value = String(Math.round(parseFloat(value) * 100)); 
     } 

     this.onChange(value); 
    } 


    writeValue(value: any): void 
    { 
     if (value) 
     { 
      value = (parseInt(value)/100).toFixed(2); 
     } 

     this.renderer.setElementProperty(this.inputElement, 'value', value); 
    } 
} 

これはイオン入力に適用したときに直線入力エレメントに適用するとうまく動作しますが、機能しません。 ControlValueAccessorを取得してイオン入力の子入力要素に適用する方法はありますか?

+0

入出力サンプル – Aravind

+0

私の答えを参照してください – Aravind

答えて

2

を使用します(これに対する解決策に興味があります)。

あるいは次のディレクティブ、<ion-input>またはプレーン<input>に適用された場合に所望の結果を達成する:

import { Directive, Renderer, ElementRef, Input, Output, EventEmitter, AfterViewInit } from '@angular/core'; 

@Directive({ 
    selector: '[currencyInput]', 
    host: { 
     '(input)': 'handleInput($event.target.value)' 
    }, 
}) 
export class CurrencyInputDirective implements AfterViewInit 
{ 

    @Input('currencyInput') currency: number; 
    @Output('currencyInputChange') currencyChange: EventEmitter<number> = new EventEmitter<number>(); 
    inputElement: HTMLInputElement = null; 

    constructor(private renderer: Renderer, private el: ElementRef) { } 

    ngAfterViewInit() 
    { 
     let element = this.el.nativeElement; 

     if(element.tagName === 'INPUT') 
     { 
      this.inputElement = element; 
     } 
     else 
     { 
      this.inputElement = element.getElementsByTagName('input')[0]; 
     } 

     setTimeout(() => { 
      this.renderer.setElementProperty(this.inputElement, 'value', (this.currency/100).toFixed(2)); 
     }, 150); 
    } 

    handleInput(value: string) 
    { 
     let v : number = Math.round(parseFloat(value) * 100) 
     this.currencyChange.next(v); 
    } 
} 

そして次のように要素上に適用:

<ion-input type="text" name="measured_cost" [(currencyInput)]="item.cost"> 

setTimeoutが必要とされます入力フィールドがイオンよりもむしろCurrencyInputDirectiveによって初期化されるようにします(私はよりよい選択肢を歓迎します)。

これはうまくいきますが、一方向フローのみを提供します。すなわち、item.costが入力要素の外で変更された場合、それは入力要素値に反映されません。この問題は、以下に示すパフォーマンスの低い解決策に示すように、currencyInputのセッターメソッドを使用して解決できます。

import { Directive, Renderer, ElementRef, Input, Output, EventEmitter, AfterViewInit } from '@angular/core'; 
@Directive({ 
    selector: '[currencyInput]', 
    host: { 
     '(input)': 'handleInput($event.target.value)' 
    }, 
}) 
export class CurrencyInputDirective implements AfterViewInit 
{ 
    _cents: number; 
    myUpdate: boolean = false; 
    inputElement: HTMLInputElement = null; 
    @Input('currencyInput') 
    set cents(value: number) { 
     if(value !== this._cents) 
     { 
      this._cents = value; 
      this.updateElement(); 
     } 
    } 
    @Output('currencyInputChange') currencyChange: EventEmitter<number> = new EventEmitter<number>(); 

    constructor(private renderer: Renderer, private el: ElementRef) { } 

    ngAfterViewInit() 
    { 
     let element = this.el.nativeElement; 

     if(element.tagName === 'INPUT') 
     { 
      this.inputElement = element; 
     } 
     else 
     { 
      this.inputElement = element.getElementsByTagName('input')[0]; 
     } 

     setTimeout(() => { 
      this.renderer.setElementProperty(this.inputElement, 'value', (this._cents/100).toFixed(2)); 
     }, 150); 
    } 

    handleInput(value: string) 
    { 
     let v : number = Math.round(parseFloat(value) * 100); 
     this.myUpdate = true; 
     this.currencyChange.next(v); 
    } 

    updateElement() 
    { 
     if(this.inputElement) 
     { 
      let startPos = this.inputElement.selectionStart; 
      let endPos = this.inputElement.selectionEnd; 

      this.renderer.setElementProperty(this.inputElement, 'value', (this._cents/100).toFixed(2)); 
      if(this.myUpdate) 
      { 
       this.inputElement.setSelectionRange(startPos, endPos); 
       this.myUpdate = false; 
      } 
     } 
    } 
} 
0

これを行うには、を使用する必要はありません。 ControlAccessorValueアプローチは、プレーン<input>要素に適しでしたが、私は<ion-input>ディレクティブの<input>子要素のための制御アクセサとして動作するように指令を得ることができませんでした以下のコード

import { Directive, HostListener, Renderer2, ElementRef } from '@angular/core'; 
@Directive({ 
    selector: '[currency]' 
}) 
export class CurrencyDirective{ 

    constructor(
     private renderer: Renderer2, 
     private el: ElementRef 
    ){} 

    @HostListener('keyup') onKeyUp() { 
     this.el.nativeElement.value=this.el.nativeElement.value/100; 
     console.log(this.el.nativeElement.value) 
    console.log('some thing key upped') 

    } 
} 

LIVE DEMO

+0

それはあなたの下で値を変更するフィールドに入力すると、必要な機能を提供していません。私が後にしているのは、テキストフィールドの値をmodel/100の値に設定した後、テキストフィールドの値を直接変更すると、モデルフィールドの値がテキストフィールドの値* 100に設定されます。 – cubiclewar

+0

あなたを得られなかった。 initが必要なときに何か変更が必要な場合は、何か別のものが必要ですか?またはフォーカスまたはぼかしで – Aravind

+0

モデルの値は、セントから1000の形式でデータベースから取得されます。テキストフィールドが初期化されると、その値は10.00として設定されます。そこから、テキストフィールドのキー入力イベントは、モデル値をテキストフィールド値に100を掛けた値に設定します。 – cubiclewar

関連する問題