3

I have created a custom input component that extends primeng spinner component but when I use it with Angular Reactive Form, the model value of my component is not updated. I have stackblitzed my issue in order it to be easy to debug. Here is the code of my component :

import {Component, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild} from '@angular/core';
import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
import {Spinner} from 'primeng/primeng';

@Component({
  selector: 'rd-spinner',
  template: `
    <p-spinner [formControl]="rdSpinner"
               [step]="step"
               [min]="min"
               [max]="max"
               [placeholder]="placeholder"
               [disabled]="disabled"
               [readonly]="readonly"
               [maxlength]="maxlength"
               [size]="size"
               [tabindex]="tabindex"
               [inputId]="tabindex"
               [type]="type"
               [required]="required"
               [name]="name"
               [inputStyle]="inputStyle"
               [inputStyleClass]="inputStyleClass"></p-spinner>
  `,
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => RdSpinnerComponent),
    multi: true
  }]
})
export class RdSpinnerComponent implements ControlValueAccessor, OnInit {

  @ViewChild(Spinner) private _spinner: Spinner;
  rdSpinner: FormControl;

  // Required to implement interface ControlValueAccessor
  private _onChange : Function = () => {};
  private _onTouched : Function = () => {};

  // Those inputs at given directly to primeng spinner 
  @Input() step: number = 1;
  @Input() min: number;
  @Input() max: number;
  @Input() placeholder: string;
  @Input() disabled: boolean;
  @Input() readonly: boolean;
  @Input() maxlength: number;
  @Input() size: number;
  @Input() tabindex: number;
  @Input() inputId: string;
  @Input() type: string = 'text';
  @Input() required: boolean;
  @Input() name: string;
  @Input() inputStyle: any;
  @Input() inputStyleClass: string;
  @Output() onChange: EventEmitter<any> = new EventEmitter();
  @Output() onFocus: EventEmitter<any> = new EventEmitter();
  @Output() onBlur: EventEmitter<any> = new EventEmitter();

  constructor() {
  }

  ngOnInit() {
    this.rdSpinner = new FormControl();

    if (this._spinner) {
      const origParseValue = this._spinner.parseValue;
      this._spinner.parseValue = (val: string): number => {
        let value: number;

        if (val.trim() === '') {
          value = null;
        }
        else {
          value = origParseValue.call(this._spinner, val);
        }

        return value;
      }
    }
  }

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

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

  writeValue(value: any): void {
    if (value) {
      this.rdSpinner.setValue(value);
    }
  }

}

1 Answer 1

1

You should be calling the _onchange method when the value of the original spinner changes its value. You can do this by subscribing to the valueChanges of the original component and calling this._onChange() when it emits a new value.

I fixed the example by adding

this.rdSpinner.valueChanges.subscribe(result => this._onChange(result));

as the last line in your current ngOnInit().

new stackblitz: stackBlitz

Sign up to request clarification or add additional context in comments.

3 Comments

However in the console I get the warning "It looks like you're using the disabled attribute with a reactive form directive... We recommend using this approach to avoid 'changed after checked' errors.". @Korfoo Do you know how to fix this issue?
You can remove the [disabled]=disabled in the html, but then you will have to use if (this.disabled) { this.rdSpinner.disable() } in the ngOnInit() to disable it. The problem is that if you change the disabled input from your custom component it won't propagate to the rdSpinner and you will have to use something else to inform your custom component to disable the rdSpinner.
Thanx for your update, I see that interface ControlValueAccessor has a setDisabledState method, shouldn't I implement this interface to avoid the warning message?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.