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>
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),
});
}
...
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');
}
}
<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>
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));