0

When you click on "Edit Task" a form opens that already contains the values ​​of a task. This already works for simple strings, but not for values ​​that have an array of values.

Problem:

A task can be assigned to several people. However, the people who have already been assigned are not selected.

I've already researched, but couldn't implement the solution on my code:

patchValue on a FormArray object?

How to use FormArray in angular

A Stackblitz Demo:

https://stackblitz.com/edit/stackblitz-starters-1ksmca?file=src%2Fmain.ts

Most relevant Code:

ts-file:

export class TaskFormComponent {
  protected readonly Object = Object;
  taskForm!: FormGroup;
  fromPopup = false;

  constructor(
    @Optional()
    @Inject(MAT_DIALOG_DATA)
    public data: { fromPopup: boolean; task: Task }
  ) {}

  ngOnInit() {
    this.fromPopup = !!this.data?.fromPopup;
    this.taskForm = new FormGroup({
      assignedTo: new FormControl(''),
    });

    if (this.data?.task) {
      this.taskForm.setValue({
        assignedTo: this.data.task.contacts,
      });
    }
  }

  public onSubmit() {
    console.log('Submitted');
  }
}

html-file:

<div>
  <div>
    <div mat-dialog-title>Edit Task</div>
  </div>

  <mat-dialog-content>
    <form [formGroup]="taskForm" (ngSubmit)="onSubmit()">
      <mat-form-field>
        <mat-label>Assigned to</mat-label>
        <mat-icon color="primary" matSuffix>group_add</mat-icon>
        <mat-select formControlName="assignedTo" multiple>
          @for (contact of this.data.task.contacts; track contact.id) {
          <mat-option [value]="contact.id">{{ contact.name }}</mat-option>
          }
        </mat-select>
      </mat-form-field>
    </form>
  </mat-dialog-content>

  <mat-dialog-actions>
    <button mat-raised-button color="primary" type="submit">
      {{ data?.task ? 'Update Task' : 'Create Task' }}
    </button>
    <button mat-raised-button mat-dialog-close type="button" color="warn">
      Close
    </button>
  </mat-dialog-actions>
</div>
1
  • Right now your question is too broad. It reads like “implement this code for me” which is off topic for this site. If that’s not your intent and you have a specific question about a targeted part of your solution you should really edit your question to make it clear what that is. Commented May 16, 2024 at 1:13

2 Answers 2

1

Since you have set the value of the options of the select to be id field

...
<mat-option [value]="contact.id">{{ contact.name }}</mat-option>
...

We must also patchValue the ids as an array, for this we use the javascript map operator, to fetch only the ids from the array of objects!

...
if (this.data?.task) {
  this.taskForm.setValue({
    assignedTo: this.data.task.contacts.map((x) => x.id),
  });
}
...

FULL CODE:

TS

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Inject, Optional } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckbox } from '@angular/material/checkbox';
import {
  MatError,
  MatFormField,
  MatFormFieldModule,
  MatLabel,
} from '@angular/material/form-field';
import {
  FormControl,
  FormGroup,
  ReactiveFormsModule,
  Validators,
} from '@angular/forms';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatInputModule } from '@angular/material/input';
import { MatOption, provideNativeDateAdapter } from '@angular/material/core';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { TitleCasePipe } from '@angular/common';
import { MatSelect } from '@angular/material/select';
import { MatIconModule } from '@angular/material/icon';
import { MatRadioButton, MatRadioGroup } from '@angular/material/radio';
import {
  MAT_DIALOG_DATA,
  MatDialog,
  MatDialogModule,
  MatDialogRef,
} from '@angular/material/dialog';
import { Task } from '../../app/task';

@Component({
  selector: 'app-task-form',
  standalone: true,
  imports: [
    CommonModule,
    MatButtonModule,
    MatCheckbox,
    MatError,
    MatFormField,
    MatLabel,
    ReactiveFormsModule,
    MatDatepickerModule,
    MatInputModule,
    MatFormFieldModule,
    MatButtonToggleModule,
    TitleCasePipe,
    MatSelect,
    MatOption,
    MatRadioGroup,
    MatRadioButton,
    MatDialogModule,
    MatIconModule,
  ],
  templateUrl: './task-form.component.html',
  styleUrl: './task-form.component.css',
})
export class TaskFormComponent {
  protected readonly Object = Object;
  taskForm!: FormGroup;
  fromPopup = false;

  constructor(
    @Optional()
    @Inject(MAT_DIALOG_DATA)
    public data: { fromPopup: boolean; task: Task }
  ) {}

  ngOnInit() {
    this.fromPopup = !!this.data?.fromPopup;
    this.taskForm = new FormGroup({
      assignedTo: new FormControl(''),
    });

    if (this.data?.task) {
      this.taskForm.setValue({
        assignedTo: this.data.task.contacts.map((x) => x.id),
      });
    }
  }

  public onSubmit() {
    console.log('Submitted');
  }
}

HTML

<div>
  <div>
    <div mat-dialog-title>Edit Task</div>
  </div>

  <mat-dialog-content>
    <form [formGroup]="taskForm" (ngSubmit)="onSubmit()">
      <mat-form-field>
        <mat-label>Assigned to</mat-label>
        <mat-icon color="primary" matSuffix>group_add</mat-icon>
        <mat-select formControlName="assignedTo" multiple>
          @for (contact of this.data.task.contacts; track contact.id) {
          <mat-option [value]="contact.id">{{ contact.name }}</mat-option>
          }
        </mat-select>
      </mat-form-field>
    </form>
  </mat-dialog-content>

  <mat-dialog-actions>
    <button mat-raised-button color="primary" type="submit">
      {{ data?.task ? 'Update Task' : 'Create Task' }}
    </button>
    <button mat-raised-button mat-dialog-close type="button" color="warn">
      Close
    </button>
  </mat-dialog-actions>
</div>

PARENT:

import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { TaskFormComponent } from './app/task-form/task-form.component';
import { filter } from 'rxjs';
import 'zone.js';
import { MatDialog } from '@angular/material/dialog';
import { Task } from './app/task';
import { provideAnimations } from '@angular/platform-browser/animations';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TaskFormComponent],
  template: `
    <button (click)="editTask()">Edit Task</button>
  `,
})
export class App {
  name = 'Angular';

  constructor(private dialog: MatDialog) {}

  public editTask() {
    let task: Task = {
      id: 1,
      contacts: [
        {
          id: 1,
          name: 'John Doe',
        },
        {
          id: 2,
          name: 'Jane Doe',
        },
      ],
    };
    this.dialog
      .open(TaskFormComponent, {
        data: { fromPopup: true, task: task },
      })
      .afterClosed()
      .pipe(filter((task) => task))
      .subscribe((task) => {
        console.log('Edit Task');
      });
  }
}

bootstrapApplication(App, {
  providers: [provideAnimations()],
}).catch((err) => console.error(err));

Stackblitz Demo

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

2 Comments

I thought you could do that with a FormArray. Would that approach have been correct?
@coder the form array we can pass an an input an array, but we need to make sure the select dropdown value is correct, here we are using contact.id as the key, so we need to pass an array of numbers, if we just use [value]="contact" then we will not need to convert the array to an array of numbers!
0

The question may seem broad, but I'll try to answer focusing on the initial question. One way to access the value of an array in HTML is by iterating over the variable with NgFor, for example:

<ul>
  <li *ngFor="let item of items">{{ item }}</li>
</ul>

2 Comments

If that’s the answer to OP’s question (and I’m not saying it’s not), then this question has to be a duplicate; there’s no way this question hasn’t already been asked and answered on this site.
Since the OP is asking about forms, perhaps the FormArray API example would be a better suggestion?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.