Edit Form Dialog Not Prepopulating

Issue

I am using Angular 11 and Ionic as my UI library. The issue that I’m having is that I created an upsert dialog component and while the edit form returns the correct value when I get the value manually from the console, the selected item is not reflected in the UI when I pass it an existing object.

This is my component template:

<ion-card>
    <ion-card-header>
        <ion-card-title>{{title}} Car Seat</ion-card-title>
    </ion-card-header>
    <ion-card-content>
        <form [formGroup]="form" (submit)="save()">
            <app-input type="select" formControlName="CarSeatTypeId" label="Car Seat Type" [selectItems]="carSeatTypes" dataValue="CarSeatTypeId" textValue="CarSeatTypeName"></app-input>
            <div Flex justifyContent="flex-end">
                <ion-button type="submit">Save</ion-button>
                <ion-button type="button" color="light" (click)="close()">Close</ion-button>
            </div>
        </form>
    </ion-card-content>
</ion-card>

This is the component typescript:

import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

import { ICarSeat } from '../car-seats.interface';
import { ICarSeatType } from '../../core/interfaces/car-seat-type.interface';
import { ModalController } from '@ionic/angular';

@Component({
    templateUrl: './upsert-car-seat.component.html'
})
export class UpsertCarSeatDialogComponent implements OnInit {

    @Input() carSeatTypes: ICarSeatType[] = [];
    @Input() existing: ICarSeat;
    form: FormGroup;

    get title(): string {
        return (!this.existing ? 'Create' : 'Edit');
    }

    constructor(private formBuilder: FormBuilder,
                private modalController: ModalController) { }

    ngOnInit(): void {
        this.form = this.buildForm();
    }

    close(): void {
        this.modalController.dismiss();
    }

    save(): void {
        if (this.form.invalid) {
            Object.keys(this.form.controls).forEach(key => {
                this.form.get(key).setValue(this.form.get(key).value);
            });
            return;
        }
        this.modalController.dismiss(this.form.value);
    }

    private buildForm(): FormGroup {
        return this.formBuilder.group({
            CarSeatTypeId: [this.existing?.CarSeatTypeId, Validators.required]
        });
    }
}

This is what I’m doing to open the dialog:

async edit(carSeat: ICarSeat): Promise<void> {
    const modal = await this.modalController.create({
        component: UpsertCarSeatDialogComponent,
        componentProps: {
            carSeatTypes: this.carSeatTypes,
            existing: carSeat
        }
    });
    modal.onWillDismiss().then(results => {
        if (results?.data) {
            carSeat.CarSeatTypeId = results.data.CarSeatTypeId;
            this.carSeatsService.updateCarSeat(carSeat).subscribe(result => {
                this.alertService.showConfirmation({
                    header: 'Car Seat Updated',
                    message: `The ${carSeat.CarSeatTypeName} has been updated.`,
                    buttons: [
                        { text: 'Close' }
                    ]
                });
            });
        }
    });
    modal.present();
}

And this is the app-input template (it ultimate gets converted to an ion-select):

<!-- dropdown -->
<ng-container *ngIf="elementType === 'ion-select'">
    <ion-item>
        <ion-label position="stacked">{{label}}</ion-label>
        <ion-select [(ngModel)]="value"
                    [cancelText]="cancelText"
                    [compareWith]="compareWith"
                    [disabled]="disabled"
                    [interface]="interface"
                    [interfaceOptions]="interfaceOptions"
                    [mode]="mode"
                    [multiple]="multiple"
                    [name]="name"
                    [okText]="okText"
                    [placeholder]="placeholder"
                    [selectedText]="selectedText"
                    [value]="value">
            <ion-select-option *ngFor="let item of selectItems" value="{{item[dataValue]}}">{{item[textValue]}}</ion-select-option>
        </ion-select>
        <ion-text *ngIf="ngControl?.control?.errors && ngControl?.control?.dirty"
                style="font-size:small;"
                color="danger"
                [innerHTML]="validationMessage"></ion-text>
    </ion-item>
</ng-container>

Here is a screenshot demonstrating the issue:
enter image description here

I’m sort of at a loss as to what to do. I’ve tried moving the build form to the constructor and then to the OnAfterViewInit, I’ve tried to manually update the UI by calling changeDetectionRef.detectChanges(), and I’ve tried calling this.form.get('CarSeatTypeId').setValue(this.existing?.CarSeatTypeId) after this.buildForm() but none of that seems to matter.

From the screenshot, the form obviously has a value and it is referencing an existing CarSeatType. Any help is appreciated.

Update

I have also attempted to use a setTimeout with the timeout set to 3,000ms to manually set the control value. This does not work either.

Solution

The underlying issue had to do with the wrapper we were using for the ion-select, a coworker made the following change which fixed the issue I was having:

<ng-container *ngIf="elementType === 'ion-select'">
    <ion-item>
        <ion-label position="stacked">
            <ion-text *ngIf="labelPrefix" [color]="labelPrefixColor">{{labelPrefix}}</ion-text>
            {{label}}
        </ion-label>
        <ion-select [(ngModel)]="value"
                    [cancelText]="cancelText"
                    [compareWith]="compareWith"
                    [disabled]="disabled"
                    [interface]="interface"
                    [interfaceOptions]="interfaceOptions"
                    [mode]="mode"
                    [multiple]="multiple"
                    [name]="name"
                    [okText]="okText"
                    [placeholder]="placeholder"
                    [value]="value">
            <ion-select-option *ngFor="let item of selectItems" value="{{item[dataValue]}}">{{item[textValue]}}</ion-select-option>
        </ion-select>
        <ion-text *ngIf="ngControl?.control?.errors && ngControl?.control?.dirty"
                style="font-size:small;"
                color="danger"
                [innerHTML]="validationMessage"></ion-text>
    </ion-item>
</ng-container>

and

compareWith: ((currentValue: any, compareValue: any) => boolean) | null | string | undefined = (currentValue: any, compareValue: any) => {
    return currentValue[this.dataValue] === compareValue[this.dataValue];
};

Specifically the diff between the old and new is that the compareWith binding was added and the selectedValue binding was removed.

Answered By – David

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published