Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ERROR: Cannot read property 'apply' of undefined and ERROR: this.onModelChange is not a function #74

Open
mikeveltman opened this issue Mar 6, 2020 · 11 comments

Comments

@mikeveltman
Copy link

When I use the example:

<input
    currencyMask
    value="numberValue1"
    [options]="{ prefix: 'R$ ', thousands: '.', decimal: ',' }"
/>

I get the following error when I click on the input and then click somewhere else:

core.js:5828 ERROR TypeError: Cannot read property 'apply' of undefined
    at CurrencyMaskDirective.handleBlur (ngx-currency.js:437)
    at CurrencyMaskDirective_blur_HostBindingHandler (ngx-currency.js:482)
    at executeListenerWithErrorHandling (core.js:21593)
    at wrapListenerIn_markDirtyAndPreventDefault (core.js:21635)
    at HTMLInputElement.<anonymous> (platform-browser.js:934)
    at ZoneDelegate.invokeTask (zone-evergreen.js:400)
    at Object.onInvokeTask (core.js:40744)
    at ZoneDelegate.invokeTask (zone-evergreen.js:399)
    at Zone.runTask (zone-evergreen.js:168)
    at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:481)

And when I'm changing the values in the input I get the following error:

core.js:5828 ERROR TypeError: this.onModelChange is not a function
    at InputHandler.handleKeypress (ngx-currency.js:363)
    at CurrencyMaskDirective.handleKeypress (ngx-currency.js:456)
    at CurrencyMaskDirective_keypress_HostBindingHandler (ngx-currency.js:482)
    at executeListenerWithErrorHandling (core.js:21593)
    at wrapListenerIn_markDirtyAndPreventDefault (core.js:21635)
    at HTMLInputElement.<anonymous> (platform-browser.js:934)
    at ZoneDelegate.invokeTask (zone-evergreen.js:400)
    at Object.onInvokeTask (core.js:40744)
    at ZoneDelegate.invokeTask (zone-evergreen.js:399)
    at Zone.runTask (zone-evergreen.js:168)

The application is running on Angular 9

@mikeveltman mikeveltman changed the title Cannot read property 'apply' of undefined Cannot read property 'apply' of undefined and this.onModelChange is not a function Mar 6, 2020
@mikeveltman mikeveltman changed the title Cannot read property 'apply' of undefined and this.onModelChange is not a function ERROR: Cannot read property 'apply' of undefined and ERROR: this.onModelChange is not a function Mar 6, 2020
@Alexander-Fuchs
Copy link

Any solutions for this bug?

@mikeveltman
Copy link
Author

@Alexander-Fuchs Still no solution, but I started using toLocaleString as a temporary solution and a currency pipe where needed.

@jginorio
Copy link

Is there an alternative or workaround available for this bug?

@mikeveltman
Copy link
Author

@jginorio As a workaround I started using toLocaleString and a currency pipe where needed

@jginorio
Copy link

Thanks, but how does that work exactly when a user is doing the input on a form? I've tried using the "valueChanges" observable and setting the value again with the currency pipe but on the first time it works because the current value is a number, on the second time the value is a currency(ex: $100.00) so it throws and error when trying to parse the value to currency.

@mikeveltman
Copy link
Author

mikeveltman commented Jul 16, 2020

Bear with me, I will try my best to explain my solution. I've almost never explained stuff like this on git or stackoverflow so if you have more questions let me know.

For every input field in a form I keep track of a controlValue (ISO value/number: 1234.4).
I also keep track of the displayValue (locale number string: $1,234.40).

I've made two functions: fromLocaleNumberStringToISO and fromISOToLocaleNumberString.

  1. When the form opens I receive a ISO value/number from the server (because we have a form where a user can save their progress), with fromISOToLocaleNumberString I convert that number to a string to display in the input field.
  2. The user can only enter numbers + the locale currency symbol (or any other specified currency) + the locale thousand seperator symbol + the locale decimal symbol. I check that at the onKeyDown event. I will also check if for instance a '-' symbol was entered at an incorrect place.
  3. After the blur() event of an input element I get the value entered in the input component.
  4. Convert it to a controlValue (and check for wrong syntax/characters/symbols) with fromLocaleNumberStringToISO.
  5. Then I convert the controlValue to a displayValue and throw that back into the input component (via ngModel) with fromISOToLocaleNumberString.
  6. The controlValue will be later on sent to the database.
    this.controlValue = NumberParser.fromLocaleNumberStringToISO(event.target.value, this._currentLocale, this.amountOfDecimals);
    this.displayValue = NumberParser.fromISOToLocaleNumberString(this.controlValue, this._currentLocale, this.amountOfDecimals);
      <input
        pInputText
        type="text"
        [(ngModel)]="displayValue"
        (keydown)="onKeyDown($event)"
        (keyup)="onKeyUp($event)"
        (click)="onFocusInput($event)"
        (blur)="eventHandler($event)"
        [attr.id]="id"
        [attr.name]="name"
        [attr.required]="required"
        [readonly]="readonly ? true : null"
        [disabled]="disabled"
      />

@mkydouglas
Copy link

Hey guys, let me share with you what I did..
I just used the both attributes in the input: ngModel and ngModelChange. Like this:

<input currencyMask type="text" [(ngModel)]="valor" (ngModelChange)="teste()" [options]="{ align: 'left', prefix: 'R$ ', thousands: '.', decimal: ',', allowNegative: 'false', min: 0, max: 500000 }">

After this, the error message disappeared.

I hope it can help you!

@ViteRoberto
Copy link

Hey guys, let me share with you what I did..
I just used the both attributes in the input: ngModel and ngModelChange. Like this:

<input currencyMask type="text" [(ngModel)]="valor" (ngModelChange)="teste()" [options]="{ align: 'left', prefix: 'R$ ', thousands: '.', decimal: ',', allowNegative: 'false', min: 0, max: 500000 }">

After this, the error message disappeared.

I hope it can help you!

Brou! It works like charm!

@tayambamwanza
Copy link

I'm not using ngModel, using reactive forms, any way around this?

@ranawaqartufail
Copy link

ranawaqartufail commented Dec 8, 2021

@tayambamwanza check this are you using both of them
import { NgxCurrencyModule } from "ngx-currency";
import { CurrencyMaskModule } from 'ng2-currency-mask';
only use one and remove other

@lucasluizss
Copy link

lucasluizss commented Mar 7, 2025

Hello everyone! I made a workaround for this problem that now works with Reactive Forms. I only was able to fix it with the blur method.

Internally, the component uses bidirectional data binding with value conversion, as suggested by @mikeveltman. Externally only needs to pass your formControlName.

import { NgxCurrencyConfig } from 'ngx-currency';
import { Component, forwardRef, Input } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-input-currency',
  template: `
    <label
      *ngIf="label"
      [class.invisible]="!label"
      class="text-sm font-medium text-gray-300 hover:bg-dark-interactive-12"
    >
      {{ label }}
    </label>
    <input
      pInputText
      currencyMask
      [options]="config"
      (input)="onInput($event)"
      (blur)="onBlur()"
      type="text"
      [(ngModel)]="displayValue"
      [placeholder]="placeholder"
      [readonly]="readonly"
      [disabled]="disabled"
      class="select text-lg w-full bg-dark-blue-1 focus:outline-none border-b border-white"
    />
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputCurrencyComponent),
      multi: true,
    },
  ],
})
export class InputCurrencyComponent implements ControlValueAccessor {
  @Input() label?: string;
  @Input() placeholder?: string;
  @Input() config: Partial<NgxCurrencyConfig> = {
    prefix: '',
    suffix: ' €',
    thousands: '.',
    decimal: ',',
  };

  @Input() readonly?: boolean = false;
  @Input() disabled: boolean = false;

  value?: string;
  displayValue?: string;
  onChange: (value: string) => void = () => {};
  onTouched: () => void = () => {};

  writeValue(value: string): void {
    this.value = value;
    this.displayValue = this.fromISOToLocaleNumberString(value);
  }

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

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

  setDisabledState?(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  onInput(event: Event): void {
    const input = event.target as HTMLInputElement;
    this.value = this.fromLocaleNumberStringToISO(input.value);
    this.displayValue = this.fromISOToLocaleNumberString(this.value);
    this.onChange(this.value);
  }

  onBlur(): void {
    this.onChange(this.displayValue!);
    this.onTouched();
  }

  fromLocaleNumberStringToISO(value: string): string {
    const thousands = this.config.thousands ?? '';
    const decimal = this.config.decimal ?? '.';
    return value.replace(new RegExp(thousands, 'g'), '').replace(decimal, '.');
  }

  fromISOToLocaleNumberString(value: string): string {
    if (!value) {
      return '';
    }

    const decimal = this.config.decimal ?? '.';
    return value.replace('.', decimal);
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants