Как я могу получить полный объект при добавлении или редактировании задачи, а не только идентификатор?

Я работаю над системой управления задачами, в которой задачи связаны с контактами и подзадачами. Когда я добавляю или редактирую задачу, система в настоящее время возвращает только идентификаторы контактов и строковые значения подзадач. Однако мне нужно получить полные объекты как для контактов, так и для подзадач.

Как я могу изменить свой код, чтобы получить полные объекты для контактов и подзадач при добавлении или редактировании задачи? Любые рекомендации или примеры будут с благодарностью оценены. Спасибо!

Демо @ StackBlitz

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.info('Add Task:');
        console.info(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.info('Edit Task:');
        console.info(task);
      });
  }
}

задача-form.comComponent.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 = [];
  }
}

задача-форма.компонент.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>
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
0
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема

Прежде чем приступить к решению, первая проблема заключается в том, что вам нужно установить пустой массив вместо пустой строки в элементе управления subTasks, чтобы разрешить компонент Mat Chip, который не может добавлять несколько значений.

задача-form.comComponent.ts

this.taskForm = this.fb.group({
  ...,
  subTasks: new FormControl([]),
  ...
});

Изменения

  1. В том же компоненте вы должны передать массив объектов для элементов управления subTasks и contact.

задача-form.comComponent.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,
  });
}
  1. Установите значение <mat-option> в качестве объекта contact для элемента управления contacts.

задача-форма.компонент.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>

задача-form.comComponent.ts

contactCompareWithFn = (contact: Contact, value: Contact) => contact.id == value.id;
  1. Передайте data?.task?.id свойству taskId Input.

задача-форма.компонент.html

<app-chip-field
  [control] = "subTasksFormControl"
  [controlTitle] = "'subtask'"
  [taskId] = "data?.task?.id"
></app-chip-field>

4.1. Добавьте свойство taskId@Input, чтобы принять переданное значение.

4.2. Измените keywords и методы для типа Subtask.

чип-field.comComponent.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();
  }
}
  1. Измените <mat-chip-row>, чтобы снабдить value объектом keyword и отобразить description.

чип-форма.компонент.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>

Демо @ StackBlitz

Спасибо. Это мне очень помогло. Есть только одна проблема. когда я нажимаю «Редактировать задачу», контакты, назначенные этой задаче, не отображаются. Я определил контакты: [[]] как пустой массив, но это все равно не работает.

coder 24.05.2024 14:24

Хммм, а ты пробовал без вложенного массива, contacts: []? Или вы можете поделиться со мной демо-версией StackBlitz для расследования? Спасибо.

Yong Shun 24.05.2024 14:44

Вот демо-версия StackBltz. stackblitz.com/edit/…

coder 24.05.2024 14:55

Привет, ваш <mat-select> должен реализовать compareWith, чтобы он мог отображать предварительно установленное значение на основе id. Демо

Yong Shun 24.05.2024 15:15

Другие вопросы по теме