r3plica
r3plica

Reputation: 13367

Angular FormArray not working as I expect

I hope you can help me out with this. I have a form which allows me to dynamically add items to an array, the array can be pre-populated if we are editing and looks something like this:

<mat-list class="hall-list"
    *ngIf="f.halls.length">
    <mat-list-item *ngFor="let hall of f.halls.controls;">
        <ng-container [formGroup]="hall">
            <img [src]="hall.url"
                matListAvatar />

            <p matLine>{{ hall.url }}</p>
            <p matLine>{{ hall.startTime }}</p>

            <p matLine>
                <mat-form-field class="full-width-field">
                    <mat-label for="url">Start time *</mat-label>
                    <input matInput
                        placeholder="Enter the start time"
                        formControlName="startTime">
                </mat-form-field>
            </p>
        </ng-container>
    </mat-list-item>
</mat-list>

As you can see I am trying to display an image based on the url of the "hall". I have set that up as follows:

ngOnInit() {
  this.buildForm();
  this.getHall();
}

get f() {
  return this.form.controls;
}

private buildForm(): void {
  this.form = this.formBuilder.group({
    videoUrl: ["", Validators.required],
    tags: ["", Validators.required],
    halls: this.formBuilder.array([])
  });
}

private getHall(): void {
  this.querySubscription = this.route.queryParams.subscribe(params => {
    const id = params.hallId || 0;
    const halls = this.form.controls.halls as FormArray;

    if (id) {
      this.hallService.get(id, "Videos").subscribe(hall => {
        const group = new FormGroup({
          id: new FormControl(hall.id),
          url: new FormControl(hall.url, Validators.required),
          startTime: new FormControl("", Validators.required)
        });
        halls.push(group);
      });
    }
  });
}

But when I view my page, although I get no errors, it does not display an image. I added the <p matLine> to try to debug, but they are empty also. Does anyone know what I am doing wrong?

Just because some people are stating errors, here is the total html file:

<form [formGroup]="form"
    (ngSubmit)="save(form.value)"
    novalidate>

    <app-stepper #stepper>
        <app-step name="Video"
            [expanded]="stepper.step === 0">

            <mat-form-field class="full-width-field">
                <mat-label for="videoUrl">Youtube link *</mat-label>
                <input matInput
                    placeholder="Enter a valid image url"
                    formControlName="videoUrl">
            </mat-form-field>

            <app-tags (change)="onChangeTags($event)"></app-tags>

            <mat-action-row>
                <button mat-raised-button
                    type="button"
                    color="primary"
                    (click)="getSnippet()"
                    [disabled]="!f.videoUrl.valid || !f.tags.valid">Next</button>
            </mat-action-row>
        </app-step>

        <app-step name="Halls"
            [expanded]="stepper.step === 1"
            [disabled]="stepper.step < 1">

            <p>Chanell: {{ model?.channelTitle }}</p>
            <p>Title: {{ model?.title }}</p>

            <mat-list class="hall-list"
                *ngIf="f.halls.length">
                <div formArrayName="halls">
                    <mat-list-item *ngFor="let hall of f.halls.controls;">
                        <ng-container [formGroup]="hall">
                            <img [src]="hall.url"
                                matListAvatar />

                            <p matLine>{{ hall.url }}</p>
                            <p matLine>{{ hall.startTime }}</p>

                            <p matLine>
                                <mat-form-field class="full-width-field">
                                    <mat-label for="url">Start time *</mat-label>
                                    <input matInput
                                        placeholder="Enter the start time"
                                        formControlName="startTime">
                                </mat-form-field>
                            </p>
                        </ng-container>
                    </mat-list-item>
                </div>
            </mat-list>

            <app-image-previewer [files]="files"
                [urls]="urls"
                [progress]="progress"></app-image-previewer>

            <mat-action-row>
                <app-file [files]="files"
                    [urls]="urls"></app-file>
                <button mat-raised-button
                    type="submit"
                    color="primary"
                    [disabled]="!form.valid || !f.halls.length">Save</button>
            </mat-action-row>

        </app-step>
    </app-stepper>
</form>

Here is a stackblitz showing the issue: https://angular6-dynamic-form-array-vnnye9.stackblitz.io

Upvotes: 0

Views: 921

Answers (3)

Maniraj Murugan
Maniraj Murugan

Reputation: 9084

With reference to the latest stackblitz code you have provided, the value of the image src is null and you can set the value using the reference from the formgroup to array level and with each index like myForm.value.arr[i].url..

Hence

Change,

<div><img [src]="url" alt="some url" /></div>

To:

 <div><img [src]="myForm.value.arr[i].url" alt="some url" /></div>

Reason for the change:

You are iterating an array to display a url which doesn't have any value so it gives null..

You can look at the image below,

enter image description here

Here normally to retrieve the form values we use, {{myForm.value | json}} which will give the json format of the formvalue so using that you need to find out where exactly does the url lies, then you can find out the path.

So here in your case it is available here myForm.value.arr[i].url.. We need to access it from the form level object to array with index then url of that item.

Working Stackblitz Here

Upvotes: 1

Ken
Ken

Reputation: 96

Possible problems:

  1. hall.url should be hall.value.url.
  2. [formGroup] under formArrayName should be the index of the looping f.halls.controls.

In your html file:

<mat-list class="hall-list" *ngIf="f.halls.length">
    <div formArrayName="halls"> 
        <mat-list-item *ngFor="let hall of f.halls.controls; index as i">
            <ng-container [formGroup]="i">
                <img [src]="hall.value.url"
                matListAvatar />

                <p matLine>{{ hall.value.url }}</p>
                <p matLine>{{ hall.value.startTime }}</p>

                <p matLine>
                    <mat-form-field class="full-width-field">
                        <mat-label for="url">Start time *</mat-label>
                        <input matInput
                            placeholder="Enter the start time"
                            formControlName="startTime">
                    </mat-form-field>
                </p>
            </ng-container>
        </mat-list-item>
    </div>
</mat-list>

Upvotes: 1

Aravind
Aravind

Reputation: 41543

You should be using formArrayName

<mat-list class="hall-list"
    *ngIf="f.halls.length">
    <div formArrayName="halls"> 
    <mat-list-item *ngFor="let hall of f.halls.controls;">
        <ng-container [formGroup]="hall">
            <img [src]="hall.url"
                matListAvatar />

            <p matLine>{{ hall.url }}</p>
            <p matLine>{{ hall.startTime }}</p>

            <p matLine>
                <mat-form-field class="full-width-field">
                    <mat-label for="url">Start time *</mat-label>
                    <input matInput
                        placeholder="Enter the start time"
                        formControlName="startTime">
                </mat-form-field>
            </p>
        </ng-container>
    </div>
    </mat-list-item>
</mat-list>

Upvotes: 0

Related Questions