Создание многоразового Angular ArrayInputComponent

Я работаю над созданием повторно используемых компонентов ввода форм, используя реактивные формы Angular.

Что касается моего компонента ввода FormArray, я столкнулся с проблемами.

Я обнаружил, что мне нужно использовать приведение двойного типа:

get arrayGroup(): FormGroup {
  return this.formArray as AbstractControl as FormGroup;
}

Потому что, если я не обернул входной HTML примерно так:

<div [formGroup] = "arrayGroup"></div>

Я получил эту ошибку:

NG01053: formGroupName необходимо использовать с родительской директивой formGroup. Вам понадобится добавить директиву formGroup и передать ее существующему экземпляру FormGroup (вы можете создать его в своем классе).

Мое решение работает и выполняет свою работу, но должен быть способ получше.

Я поделился двумя компонентами: формой и вводом.

** Компоненты **

// FILENAME: forms.component.ts

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
  FormArray,
  FormBuilder,
  FormGroup,
  ReactiveFormsModule,
} from '@angular/forms';

import { ArrayInputComponent } from './array-input/array-input.component';

@Component({
  selector: 'app-forms',
  standalone: true,
  imports: [ArrayInputComponent, CommonModule, ReactiveFormsModule],
  template: `
    <div>
      <h1>Forms</h1>
      <form [formGroup] = "form" (ngSubmit) = "onSubmit()">
        <app-array-input [formArray] = "formArray"></app-array-input>
        <button>Submit</button>
      </form>
    </div>
  `,
})
export class FormsComponent implements OnInit {
  form: FormGroup = this.fb.group({});

  constructor(private fb: FormBuilder) {}

  ngOnInit(): void {
    this.form.addControl('formArray', this.fb.array([]));
  }

  get formArray() {
    return this.form.get('formArray') as FormArray;
  }

  onSubmit() {
    console.info('Form valid: ', this.form.valid);
    console.info('Form values: ', this.form.value);
  }
}
// FILENAME: ./array-input/array-input.component

import { CommonModule } from '@angular/common';
import { Component, Input } from '@angular/core';
import {
  AbstractControl,
  FormArray,
  FormBuilder,
  FormGroup,
  ReactiveFormsModule,
  Validators,
} from '@angular/forms';

@Component({
  selector: 'app-array-input',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule],
  template: `
    <div [formGroup] = "arrayGroup">
      <h1>Array Input</h1>
      <button type = "button" (click) = "add()">Add</button>
      <ng-container *ngFor = "let item of this.formArray.controls; let i = index">
        <div [formGroupName] = "i">
          <input full placeholder = "name" formControlName = "name" />
          <input placeholder = "relation" formControlName = "relation" />
        </div>
      </ng-container>
    </div>
  `,
})
export class ArrayInputComponent {
  @Input() formArray!: FormArray;

  constructor(private fb: FormBuilder) {}

  add() {
    const item = this.fb.group({
      name: ['', [Validators.required]],
      relation: [],
    });

    this.formArray.push(item);
  }

  get arrayGroup(): FormGroup {
    return this.formArray as AbstractControl as FormGroup;
  }
}

Угловая версия 17

Тестирование функциональных 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
2
0
101
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

ControlContainer DI можно использовать для получения родительского FormGroup, используя

this.formGroup = this.controlContainer!.control as FormGroup;

Тогда мы можем использовать это formGroup, чтобы получить formArray.

  get formArray(): FormArray {
    return this.formGroup.get('formArray') as FormArray;
  }

После этого мы настраиваем formArrayName и formGroup, у которых есть отдельный DIV.

 <div [formGroup] = "formGroup">
    <div formArrayName = "formArray">
      <h1>Array Input</h1>
      <button type = "button" (click) = "add()">Add</button>
      <ng-container *ngFor = "let item of this.formArray.controls; let i = index">
        <div [formGroupName] = "i">
          <input full placeholder = "name" formControlName = "name" />
          <input placeholder = "relation" formControlName = "relation" />
        </div>
      </ng-container>
    </div>
  </div>

После этого ваш компонент должен работать нормально.

Полный код:

import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';

import { CommonModule } from '@angular/common';
import { Component, Input } from '@angular/core';
import {
  AbstractControl,
  ControlContainer,
  FormArray,
  FormBuilder,
  FormGroup,
  FormGroupName,
  NgForm,
  ReactiveFormsModule,
  Validators,
} from '@angular/forms';

@Component({
  selector: 'app-array-input',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule],
  template: `
  <div [formGroup] = "formGroup">
    <div formArrayName = "formArray">
      <h1>Array Input</h1>
      <button type = "button" (click) = "add()">Add</button>
      <ng-container *ngFor = "let item of this.formArray.controls; let i = index">
        <div [formGroupName] = "i">
          <input full placeholder = "name" formControlName = "name" />
          <input placeholder = "relation" formControlName = "relation" />
        </div>
      </ng-container>
    </div>
  </div>
  `,
})
export class ArrayInputComponent {
  formGroup: FormGroup = new FormGroup({});

  constructor(
    private fb: FormBuilder,
    private controlContainer: ControlContainer
  ) {}

  ngOnInit() {
    console.info(this.controlContainer.control);
    this.formGroup = this.controlContainer!.control as FormGroup;
  }

  add() {
    const item = this.fb.group({
      name: ['', [Validators.required]],
      relation: [],
    });

    this.formArray.push(item);
  }

  get formArray(): FormArray {
    return this.formGroup.get('formArray') as FormArray;
  }
}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ArrayInputComponent, CommonModule, ReactiveFormsModule],
  template: `
    <div>
      <h1>Forms</h1>
      <form [formGroup] = "form" (ngSubmit) = "onSubmit()">
        <app-array-input></app-array-input>
        <button>Submit</button>
      </form>
    </div>
  `,
})
export class App {
  form: FormGroup = this.fb.group({});

  constructor(private fb: FormBuilder) {}

  ngOnInit(): void {
    this.form.addControl('formArray', this.fb.array([]));
  }

  get formArray() {
    return this.form.get('formArray') as FormArray;
  }

  onSubmit() {
    console.info('Form valid: ', this.form.valid);
    console.info('Form values: ', this.form.value);
  }
}

bootstrapApplication(App);

Демо-версия Stackblitz


Если вам нужно что-то еще более повторно используемое, вы можете указать имя массива в качестве рабочего примера ниже.

Демо-версия Stackblitz

В Angular 17 вы можете использовать FormArray<FormGroup> :) (Через четыре года ребята из Angular решили проблему с итерацией и использованием переменной цикла.)

Eliseo 30.05.2024 18:17

FormArray может быть FormArray из FormsControls или FormArray из FormsGroups.

В Angular 17 (почти) вы можете определить

formArrayOfControls!:FormArray<FormControl>
formArrayOfGroups!:FormArray<FormGroup>

Теперь вы можете повторить.

С новым @for вы можете использовать

@for (control of formArrayOfControls.controls;let i=$index;track i)
{
  //see that you use "formControl"
  <input [formControl] = "control"/>
}

//and 
@for (group of formArrayOfGroups.controls;track $index)
{
  <div [formGroup] = "group">
     <!--here you can use formControlName, e.g.-->
     <input formControlName = "control"/>
  </div>
 }

Использование старого *ngFor

<div *ngFor = "let control of formArray.controls">
  <input [formControl] = "control">
</div>

//and
<div *ngFor = "let group of formArray.controls" [formGroup] = "group">
  <input formControlName = "control">
</div>

Таким образом, вы можете свободно передавать сам FormArray в свой компонент, просто используя

  get formArray() {
    return this.form.get('formArray') as FormArray<FormGroup>;
  }

и пройти

<app-array-input [formArray] = "formArray"></app-array-input>

Нарен развил стекблиц

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

Невозможно включить телевизор Samsung Tizen с помощью предоставленных API
JavaScript Array Каждый метод передает аргумент, показывающий undefine для функции обратного вызова лямбда стрелки
Модуль Next.Js не найден: невозможно разрешить проблему после изменения пути
Инструмент «Пропустить тип» решает проблему необязательных атрибутов
API драматурга получает машинописный текст неопределенного значения
Панель навигации с размытием не показывает эффект, когда за ней находится элемент с каким-либо типом анимации?
Как имитировать только данные, за исключением другой информации (isLoading, isError и т. д.), возвращаемой из пользовательского перехватчика с помощью useQuery?
Использовать вывод типа, чтобы функция возвращала только определенное значение?
Фильтрация ненулевого значения из массива в typectipt
TypeScript больше не может правильно проверять существование свойств, как только проверки передаются функции?