如何为离子输入实现货币输入指令
Posted
技术标签:
【中文标题】如何为离子输入实现货币输入指令【英文标题】:How to implement a currency input directive for ion-input 【发布时间】:2017-09-19 00:55:05 【问题描述】:更新:最初虽然问题出在 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 中,我会在指令链接函数中使用解析和渲染,如下所示:
(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);
;
]);
)();
在 Ionic 2/Angular 2 中,我使用 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 应用于 ion-input 的子输入元素?
【问题讨论】:
输入输出??样本 在下面查看我的答案 【参考方案1】:虽然 ControlAccessorValue 方法适用于普通的 <input>
元素,但我无法让指令充当 <ion-input>
指令的 <input>
子元素的控制访问器(对此感兴趣的解决方案)。
或者,以下指令在应用于<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
而不是 ionic 初始化(我欢迎更好的选择)。
这很好用,但它只提供一种方式流程,即如果 item.cost
在输入元素之外更改,则不会反映在输入元素值中。可以使用 currencyInput
的 setter 方法解决此问题,如下面的这个性能较低的解决方案所示:
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;
【讨论】:
【参考方案2】:您无需使用ControlValueAccessor
来实现此目的。使用下面的代码
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
【讨论】:
当您在字段中键入时,它并不能完全提供所需的功能,它会从您下方更改值。我所追求的是初始化文本字段的值设置为模型/100 的值,随后当文本字段值直接更改时,模型值设置为文本字段值 * 100。跨度> 我没听懂你。当初始化你需要一些东西并改变你需要一些其他的东西?或聚焦或模糊 模型值将以美分格式从数据库中提取,即 1000。初始化文本字段时,其值将设置为 10.00。从那里,文本字段中的任何按键事件都会将模型值设置为文本字段值乘以 100。以上是关于如何为离子输入实现货币输入指令的主要内容,如果未能解决你的问题,请参考以下文章
AngularJS – 如何在 Jasmine 中为输入事件指令编写单元测试