1. Anuncie Aqui ! Entre em contato fdantas@4each.com.br

Why isn't the validation errors in this custom Angular control visible from the parent...

Discussão em 'Angular' iniciado por James Skemp, Outubro 25, 2024 às 13:22.

  1. James Skemp

    James Skemp Guest

    I'm working on my first custom control in Angular (16), for a Reactive Form, that contains a slider and an input. If the slider is true, then the input should have a value. If it is not, then the input shouldn't have a value.

    Custom component HTML:

    <div class="benefit-field">
    <div class="slide-toggle">
    <mat-slide-toggle [checked]="slideEnabled" (change)="updateEnabled($event)">Enabled?</mat-slide-toggle>
    </div>
    <div [ngClass]="{'disabled': !slideEnabled}">
    <mat-form-field>
    <mat-label>{{ typeName }}</mat-label>
    <input type="text" matInput [value]="valueId" [disabled]="!slideEnabled" (change)="onValueChanged($event.target)" />
    </mat-form-field>
    </div>
    </div>


    Custom component TypeScript:

    import { Component, forwardRef, Input } from '@angular/core';
    import { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from '@angular/forms';
    import { MatSlideToggleChange } from '@angular/material/slide-toggle';

    @Component({
    selector: 'app-optional-benefit-field',
    templateUrl: './optional-benefit-field.component.html',
    styleUrls: ['./optional-benefit-field.component.scss'],
    providers: [
    {
    provide: NG_VALUE_ACCESSOR,
    multi: true,
    useExisting: OptionalBenefitFieldComponent
    },
    {
    provide: NG_VALIDATORS,
    multi: true,
    useExisting: forwardRef(() => OptionalBenefitFieldComponent)
    },
    ]
    })
    export class OptionalBenefitFieldComponent implements ControlValueAccessor, Validator {
    @Input() typeId: number;
    @Input() typeName: number;

    valueId: number;
    slideEnabled = false;

    onChange = (_valueId) => { /* nothing needs to be done here, let it trickle up */ };
    onTouched = () => { /* nothing needs to be done here, let it trickle up */ };
    touched = false;
    disabled = false;

    writeValue(valueId: number): void {
    if (valueId && valueId > 0) {
    this.slideEnabled = true;
    }
    this.valueId = valueId;
    }

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

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

    markAsTouched(): void {
    if (!this.touched) {
    this.onTouched();
    this.touched = true;
    }
    }

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

    updateEnabled(event: MatSlideToggleChange): void {
    this.markAsTouched();
    this.slideEnabled = event.checked;
    if (!this.slideEnabled) {
    this.onChange(null);
    } else {
    this.onChange(this.valueId);
    }
    }

    onValueChanged(input: HTMLInputElement): void {
    if (input.value && (+input.value) > 0) {
    this.onChange(+input.value);
    } else {
    this.onChange(null);
    }
    }

    validate(control: AbstractControl): ValidationErrors | null {
    return (!this.slideEnabled || (this.slideEnabled && control.value))
    ? null
    : { required: true };
    }
    }


    Parent component HTML:

    <app-optional-benefit-field
    [typeId]="..."
    [typeName]="..."
    formControlName="valueId"
    ></app-optional-benefit-field>


    When I put the custom control into an invalid state, I can see that validate is returning a { required: true }.

    If I look at the parent form, however, .value has the current control's values but .errors is null.

    What am I missing in my custom control or parent component for it to properly send/receive the validation error?

    Continue reading...

Compartilhe esta Página