I am working on a task management system where tasks have associated contacts and subtasks. When I add or edit a task, the system currently only returns the ID of the contacts and the string values of the subtasks. However, I need to retrieve the full objects for both the contacts and the subtasks.
How can I modify my code to retrieve the full objects for contacts and subtasks when adding or editing a task? Any guidance or examples would be greatly appreciated. Thank you!
main.ts
@Component({
selector: 'app-root',
standalone: true,
imports: [TaskFormComponent],
template: `
<button (click)="addTask()">Add Task</button>
<button (click)="editTask()">Edit Task</button>
`,
})
export class App {
name = 'Angular';
constructor(private dialog: MatDialog) {}
public addTask() {
this.dialog
.open(TaskFormComponent, {})
.afterClosed()
.pipe(filter((task) => task))
.subscribe((task) => {
console.log('Add Task:');
console.log(task);
});
}
public editTask() {
let task: Task = {
id: 1,
title: 'Task 1',
subtasks: [
{
id: 1,
taskId: 1,
description: 'Subtask 1',
isDone: true,
},
{
id: 2,
taskId: 1,
description: 'Subtask 2',
isDone: false,
},
],
contacts: [
{
id: 1,
email: '[email protected]',
name: 'John Doe',
},
{
id: 2,
email: '[email protected]',
name: 'Jane Doe',
},
],
status: TASK_STATUSES['IN_PROGRESS'],
};
this.dialog
.open(TaskFormComponent, {
data: { fromPopup: true, task: task },
})
.afterClosed()
.pipe(filter((task) => task))
.subscribe((task) => {
console.log('Edit Task:');
console.log(task);
});
}
}
task-form.component.ts
export class TaskFormComponent {
protected readonly Object = Object;
taskForm!: FormGroup;
fromPopup = false;
keywords!: string[];
contacts: Contact[] = [
{
id: 1,
email: '[email protected]',
name: 'John Doe',
},
{
id: 2,
email: '[email protected]',
name: 'Jane Doe',
},
{
id: 3,
email: '[email protected]',
name: 'Max Max',
},
{
id: 4,
email: '[email protected]',
name: 'Anna Anna',
},
];
@ViewChild(ChipFieldComponent) chipFieldComponent!: ChipFieldComponent;
constructor(
private fb: FormBuilder,
@Optional() private dialogRef: MatDialogRef<TaskFormComponent>,
@Optional()
@Inject(MAT_DIALOG_DATA)
public data: { fromPopup: boolean; task: Task }
) {}
ngOnInit() {
this.keywords = [];
this.fromPopup = !!this.data?.fromPopup;
this.taskForm = this.fb.group({
id: this.data?.task?.id,
title: new FormControl(''),
subTasks: new FormControl(''),
contacts: new FormControl(''),
status: TASK_STATUSES['TO_DO'],
});
if (this.data?.task) {
this.taskForm.patchValue({
id: this.data.task?.id,
title: this.data.task.title,
subTasks: this.data.task.subtasks.map((x) => x.description),
contacts: this.data.task.contacts.map((x) => x.id),
status: this.data?.task.status,
});
}
}
public get subTasksFormControl() {
return this.taskForm.get('subTasks') as FormControl;
}
public onSubmit() {
this.dialogRef.close(this.taskForm.getRawValue());
this.onReset();
}
public onReset() {
this.taskForm.reset();
this.chipFieldComponent.keywords = [];
}
}
task-form.component.html
<div mat-dialog-title>{{ data?.task ? 'Update Task' : 'Create Task' }}</div>
<mat-dialog-content>
<form [formGroup]="taskForm" (ngSubmit)="onSubmit()">
<mat-form-field>
<mat-label>Title</mat-label>
<input
matInput
formControlName="title"
type="text"
placeholder="Enter a title"
/>
</mat-form-field>
<app-chip-field
[control]="subTasksFormControl"
[controlTitle]="'subtask'"
></app-chip-field>
<mat-form-field>
<mat-label>Contacts</mat-label>
<mat-select formControlName="contacts" multiple>
@for (contact of 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 (click)="onSubmit()" 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>
Issue
Before starting the solution, the first issue is that you need to set an empty array instead of an empty string to your subTasks
control to resolve the Mat Chip component not able to add multiple values.
task-form.component.ts
this.taskForm = this.fb.group({
...,
subTasks: new FormControl([]),
...
});
Changes
subTasks
and contact
controls.task-form.component.ts
if (this.data?.task) {
this.taskForm.patchValue({
id: this.data.task?.id,
title: this.data.task.title,
subTasks: this.data.task.subtasks,
contacts: this.data.task.contacts,
status: this.data?.task.status,
});
}
<mat-option>
value as a contact
object for the contacts
control.task-form.component.html
<mat-form-field>
<mat-label>Contacts</mat-label>
<mat-select formControlName="contacts" multiple [compareWith]="contactCompareWithFn">
@for (contact of contacts; track contact.id) {
<mat-option [value]="contact">{{ contact.name }}</mat-option>
}
</mat-select>
</mat-form-field>
task-form.component.ts
contactCompareWithFn = (contact: Contact, value: Contact) => contact.id == value.id;
data?.task?.id
to the taskId
Input property.task-form.component.html
<app-chip-field
[control]="subTasksFormControl"
[controlTitle]="'subtask'"
[taskId]="data?.task?.id"
></app-chip-field>
4.1. Add the taskId
@Input
property to accept the passed-in value.
4.2. Modify the keywords
, and the methods for the Subtask
type.
chip-field.component.ts
export class ChipFieldComponent {
@Input() taskId!: number | undefined;
keywords: Subtask[] = [];
removeKeyword(keyword: Subtask) {
const index = this.keywords.indexOf(keyword);
if (index >= 0) {
this.keywords.splice(index, 1);
this.announcer.announce(`removed ${keyword}`);
}
}
add(event: MatChipInputEvent): void {
const value = (event.value || '').trim();
if (value) {
this.keywords.push({
id: 0,
taskId: this.taskId ?? 0,
isDone: false,
description: value,
} as Subtask);
}
event.chipInput!.clear();
}
}
<mat-chip-row>
to supply value
with keyword
object and display the description
.chip-form.component.html
<mat-chip-row (removed)="removeKeyword(keyword)" [value]="keyword">
{{ keyword.description }}
<button matChipRemove aria-label="'remove ' + keyword.description">
<mat-icon>cancel</mat-icon>
</button>
</mat-chip-row>