В моем коде я сталкиваюсь с ошибкой «TypeError: тип «любой» должен иметь метод Symbol.iterator, который возвращает итератор». Я пытаюсь создать функцию, позволяющую пользователям вводить подзадачи в поле ввода и подтверждать их, нажав Enter. Эти подзадачи затем должны отображаться на экране в виде маркированного списка.
Примечание. Я закомментировал и
Вот упрощенная версия моего кода:
задача-форма.компонент.html:
<div mat-dialog-title>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>
<mat-form-field>
<mat-label>Subtasks</mat-label>
<input
matInput
formControlName = "subtasks"
type = "text"
placeholder = "Enter a subtask"
/>
<button
mat-icon-button
matSuffix
(keydown.enter) = "addSubtask()"
(click) = "addSubtask()"
>
<mat-icon>add</mat-icon>
</button>
<div class = "vertical-divider" matSuffix></div>
<button mat-icon-button matSuffix (click) = "clearSubtask()">
<mat-icon>close</mat-icon>
</button>
<mat-hint>Press enter to add a subtask</mat-hint>
</mat-form-field>
</form>
<!-- SHOW HERE ALL SUBTASKS-->
<!--
<ul>
@for (subtask of subtasksFormControl.value; track subtask.id){
<li>
{{ subtask }}
<button mat-icon-button (click) = "editSubtask(subtask)">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button (click) = "deleteSubtask(subtask)">
<mat-icon>delete</mat-icon>
</button>
</li>
}
</ul>-->
</mat-dialog-content>
<mat-dialog-actions>
<button (click) = "onSubmit()" mat-raised-button color = "primary" type = "submit">
Create Task
</button>
</mat-dialog-actions>
задача-form.comComponent.ts
export class TaskFormComponent {
protected readonly Object = Object;
taskForm!: FormGroup;
fromPopup = false;
constructor(
private fb: FormBuilder,
@Optional() private dialogRef: MatDialogRef<TaskFormComponent>,
@Optional()
@Inject(MAT_DIALOG_DATA)
public data: { fromPopup: boolean; task: Task }
) {}
ngOnInit() {
this.fromPopup = !!this.data?.fromPopup;
this.taskForm = this.fb.group({
id: this.data?.task?.id,
title: new FormControl(''),
subtasks: new FormControl([]),
});
if (this.data?.task) {
this.taskForm.patchValue({
id: this.data.task?.id,
title: this.data.task.title,
subTasks: this.data.task.subtasks,
});
}
}
public get subtasksFormControl() {
return this.taskForm.get('subtasks') as FormControl;
}
public addSubtask() {
this.taskForm.value.subtasks;
console.info(this.taskForm.value.subtasks);
}
public clearSubtask() {
this.taskForm.patchValue({ subtasks: '' });
}
}
🤔 А знаете ли вы, что...
Для создания пользовательских интерфейсов в Angular используется декларативный язык шаблонов.
Вам нужно сохранить подзадачи в виде массива, а имя элемента управления subtask
должно содержать только описание, тогда сложность снижается, и вы строите всю свою логику поверх этого и оставляете элемент управления формы, чтобы редактировать только описание.
import { Component } from '@angular/core';
import { CommonModule, JsonPipe } 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 {
FormArray,
FormBuilder,
FormControl,
FormGroup,
ReactiveFormsModule,
} from '@angular/forms';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatInputModule } from '@angular/material/input';
import { MatOption } 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,
MatDialogModule,
MatDialogRef,
} from '@angular/material/dialog';
import { Task } from '../../app/task';
import { Subtask } from '../subtask';
@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,
JsonPipe,
],
templateUrl: './task-form.component.html',
styleUrl: './task-form.component.css',
})
export class TaskFormComponent {
protected readonly Object = Object;
taskForm!: FormGroup;
fromPopup = false;
constructor(
private fb: FormBuilder,
@Optional() private dialogRef: MatDialogRef<TaskFormComponent>,
@Optional()
@Inject(MAT_DIALOG_DATA)
public data: { fromPopup: boolean; task: Task }
) {
if (!this.data) {
this.data = {
fromPopup: true,
task: {
id: 1,
title: '',
subtasks: [],
},
};
}
}
ngOnInit() {
this.fromPopup = !!this.data?.fromPopup;
this.taskForm = this.fb.group({
id: this.data?.task?.id,
title: new FormControl(''),
subtask: new FormControl(''),
});
if (this.data?.task) {
this.taskForm.patchValue({
id: this.data.task?.id,
title: this.data.task.title,
});
if (this.data.task.subtasks.length) {
this.data.task.subtasks.forEach((subTask: Subtask) => {
this.subtasksFormArray.push(new FormControl(subTask.description));
});
}
}
}
public get subtasksFormArray() {
return this.taskForm.get('subtasks') as FormArray;
}
public get subtasksFormArrayControls() {
return (this.taskForm.get('subtasks') as FormArray)
.controls as FormControl[];
}
public addSubtask() {
const ctrl = this.taskForm.get('subtask');
this.data.task.subtasks.push({
id: Math.random(),
description: this.taskForm.value.subtask,
isDone: false,
taskId: this.taskForm.value.id,
});
}
public clearSubtask() {
this.taskForm.patchValue({ subtask: '' });
}
public editSubtask(subtask: Subtask) {}
public deleteSubtask(subtask: Subtask) {}
public onSubmit() {}
public onReset() {}
}