Создать и заполнить реактивный формуляр Angular 10

#angular #angular-reactive-forms

#angular #угловые-реактивные-формы

Вопрос:

Обновление: Я думаю, что я приближаюсь. Это то, что у меня есть сейчас:

   songEditForm = this.fb.group({
    title: [null, [Validators.required, Validators.maxLength(128)]],
    projectId: [null, [Validators.required]],
    artist: [null, [Validators.required, Validators.maxLength(128)]],
    album: [null, [Validators.maxLength(128)]],
    minutes: [null, [Validators.min(0), Validators.max(99)]],
    seconds: [null, [, Validators.min(0), Validators.max(59)]],
    songParts: [null, [Validators.maxLength(4000)]],
    timeSignature: [null, [Validators.maxLength(10)]],
    songKey: [null, [Validators.maxLength(10)]],
    bpm: [null, [, Validators.min(0), Validators.max(320)]],
    rating: [null, [, Validators.min(0), Validators.max(5)]],
    comfortLevel: [null, [, Validators.min(0), Validators.max(5)]],
    energyLevel: [null, [, Validators.min(0), Validators.max(11)]],
    notes: [null, [Validators.maxLength(512)]],
    genre: [null],
    isPublic: [null],
    isFavorite: [null],
    customSongProperties: this.fb.array([])
  });

  get customSongProperties() {
    return this.songEditForm.get('customSongProperties') as FormArray;
  }


      <mat-card formArrayName="customSongProperties" *ngFor="let customSongProperty of customSongProperties.controls; let i=index">
        <mat-form-field>
          <mat-label>{{customSongProperty.value.label}}</mat-label>
          <input matInput type="text" [formControlName]="i" name="i">
        </mat-form-field>
      </mat-card>
  

Но, похоже, я не могу связать значения из моего массива с массивом form.

ts с отображаемым массивом данных

html-рендеринг


ОРИГИНАЛЬНОЕ СООБЩЕНИЕ ПОД ЭТОЙ СТРОКОЙ

Мне нужно выполнить цикл по объекту / массиву и создать ноль или более полей ввода с метками. Объект, к которому я хочу привязать массив Reactive form, имеет свойства label и value (среди прочих). Я чувствую, что я близок, но я получаю это сообщение об ошибке:

ОШИБКА Ошибка: Не удается найти элемент управления с путем: ‘customSongProperties -> 0 -> значение’

 <ng-container formArrayName="customSongProperties">
  <mat-card *ngFor="let _ of customSongProperties.controls; index as i">
    <ng-container [formGroupName]="i">
      <input matInput formControlName="value.value" name="index" placeholder="value.label" maxlength="50" />
    </ng-container>
  </mat-card>
</ng-container>
  

Вот как я пытаюсь заполнить массив форм:

 this.data.customSongProperties.forEach(customSongProperty => {
  this.customSongProperties.push(new FormControl(customSongProperty));
});
  

Это объект, к которому я привязываюсь и пытаюсь создать поля формы из:

 export class CustomSongProperty {
  id: number;
  userId: number;
  songPropertyDataTypeId: number;
  songPropertyDataTypeName: string | null;
  label: string | null;
  songId: number;
  value: string | null;
}
  

Мне это кажется правильным, но явно это не так.
Я следовал этому руководству:
Руководство по массиву реактивных форм
Но в конце мое понимание как бы развалилось.
Любая помощь приветствуется.

Спасибо

Ответ №1:

Джейсон, ты можешь создать FormArray из FromControls или FormArray из FormGroups (если элементы массива form обладают уникальным свойством или они являются объектами). например

 //e.g. you need a FormArray of FormControls if your json object is like
title:'my title'
customSongProperties:[ 'one','two','three']

//e.g. you need a FormArray of FormGroups if your json object is like
title:'my title'
customSongProperties:[ {value:'one'},{value:'two'},{value:'three'}]
  

С помощью FormArray FormControls, который вы используете

 <div formArraName="customSongProperties">
    <mat-card *ngFor="let customSongProperty of customSongProperties.controls; 
       let i=index" >
        <mat-form-field>
          <mat-label>{{customSongProperty.value.label}}</mat-label>
           <!--you use [formControlName]="i" for the 
             uniq FormControl in the formArray-->
          <input matInput type="text" [formControlName]="i" >
        </mat-form-field>
     </mat-card>
</div>
  

Но в вашем случае у вас есть FormArray FormGroups, поэтому .html должен быть

 <div formArraName="customSongProperties">
     <!--see that you indicate [formGroupName]="i"-->
    <mat-card *ngFor="let customSongProperty of customSongProperties.controls; 
       let i=index" [formGroupName]="i">
        <mat-form-field>
          <mat-label>{{customSongProperty.value.label}}</mat-label>
           <!--you use formControlName="nameOfProperty"
               remember that you can has severals FormsControls in the
               FormGroup
             -->
          <input matInput type="text" formControlName="value" >
        </mat-form-field>
     </mat-card>
</div>
  

О том, как создать FormGroup, всегда интересно использовать функцию, которая возвращает FormGroup и получает в качестве данных объект или null. Поскольку наш FormArray является FormArray FormGroup, мы можем сделать

 getCustomSongPropertiesFormGroup(data:any=null)
{
   //if data is null we create an object by defect
   data=data || {id:0,userId:0...}
   return this.fb.group({
     id: [data.id],
     userId: [data.userId],
     ...
   })
 }
  

И для создания FormGroup songEditForm

 getSongFormGroup(data:any=null)
{
   //if data is null we create an object by defect
   data=data || {title:null,projectId:null...,customSongProperties:null}
   return this.fb.group({
     title: [data.title, [Validators.required, Validators.maxLength(128)]],
     projectId: [data.projectId, [Validators.required]],
     ...
     customSongProperties:data.customSongProperties?
                          fb.array(data.customSongProperties
                            .map(x=>this.getCustomSongPropertiesFormGroup(x)):
                          []
   })
}
  

Попробуйте немного объяснить, что такое «карта», если у вас есть в data.customSongProperties массив объектов, вы преобразуете этот массив объектов в массив FormGroup, используя map map(x=>this.getCustomSongPropertiesFormGroup(x) это массив, с помощью которого мы создаем FormArray.

Теперь вы можете использовать, например

    //to create the form songEditForm
   this.songEditForm=this.getSongFormGroup()

   //to add a new element of the formArray
   this.customSongProperties.push(this.getCustomSongPropertiesFormGroup())   
  

Ответ №2:

Я думаю, в этом случае лучше использовать FormGroup, вы можете использовать имена полей для генерации элементов управления и создания FormGroup, а в шаблоне вы можете просто пройти по массиву и показать входные данные:

Компонент:

 export class AppComponent implements OnInit  {
  fieldNames: string[] = [];
  form: FormGroup;
  
  constructor(
  ) {}

  ngOnInit(): void {
    const controls: Record<string, FormControl> =  Object
      .keys(customSongProperty)
      .reduce( (res, fieldName) => {
        this.fieldNames.push(fieldName);
        res[fieldName] = new FormControl(customSongProperty[fieldName]);
        return res;
      }, {});

      this.form = new FormGroup(controls); 
   }
}
  

шаблон:

 <form [formGroup]="form">
  <ng-container *ngFor="let fieldName of fieldNames">
    <label>
      {{ fieldName}} : 
      <input [formControlName]="fieldName" maxlength="50" />
    </label>
  </ng-container>


  <div class="result">
    {{ form.value | json  }}
  </div>
</form>
  

код примера

Комментарии:

1. Спасибо, я думаю, может быть, я не совсем ясно выразился. Будет создан массив пользовательских свойств, и единственное поле, которое необходимо отобразить для пользовательского ввода, — это свойство ‘value’ каждого пользовательского свойства.

Ответ №3:

в определении класса ts:

   songEditForm = this.fb.group({
    title: [null, [Validators.required, Validators.maxLength(128)]],
    customSongProperties: this.fb.array([
      this.fb.group({
        id: [null],
        songId: [null],
        label: [null],
        value: [null]
      })
    ])
  });
  
  get customSongProperties() {
    return this.songEditForm.get('customSongProperties') as FormArray;
  }

 setExistingCustomSongProperties(customSongProperties: CustomSongProperty[]): FormArray
  {
    const formArray = new FormArray([]);
    customSongProperties.forEach(customSongProperty => {
      formArray.push(
        this.fb.group({
          id: customSongProperty.id,
          songId: customSongProperty.songId,
          label: customSongProperty.label,
          value: customSongProperty.value
        }));
    });
    return formArray;
  }
  

в ngOnInit:

  this.songEditForm.setControl('customSongProperties', this.setExistingCustomSongProperties(this.data.customSongProperties));
  

в компонентном html:

   <div formArrayName="customSongProperties" class="available-properties">
    <mat-card *ngFor="let customSongProperty of customSongProperties.controls; let i=index" [formGroupName]="i">
      <mat-form-field>
        <mat-label>{{customSongProperty.value.label}}</mat-label>
        <input matInput type="text" formControlName="value" [name]="i">
      </mat-form-field>
    </mat-card>
  </div>  
  

в onSubmit:

 this.data.customSongProperties = this.songEditForm.value.customSongProperties;