PatchValue嵌套和动态表单数组角度

问题描述

实际上,我不知道此问题的标题是什么,我正在基于此代码https://stackblitz.com/edit/angular-dynamic-survey-creation-golkhg构建类似于google form的表单生成器,我创建了测验表单并将其成功添加数据库中,但是现在我想制作一个“编辑测验表格”页面

这是我的完整代码

import { Component,OnInit } from '@angular/core';
import { FormArray,FormGroup,FormControl,Validators } from '@angular/forms';
import { UserService } from '../../../../../auth/user.service';
import { NbToastrService,NbComponentStatus } from '@nebular/theme';
import { Router } from '@angular/router';
import { CdkDragDrop,moveItemInArray } from '@angular/cdk/drag-drop';

@Component({
  selector: 'quiz-edit',templateUrl: './quiz-edit.component.html',styleUrls: ['./quiz-edit.component.scss']
})
export class QuizEditComponent implements OnInit {
  editQuizForm: FormGroup;
  selectedPanduan = [];
  selectedOption = [];
  list: any;
  href: string;
  lessonSlug: string;
  topicslug: string;
  materiSlug: string;
  quizData: any;
  constructor(
    private userService : UserService,private toastrService: NbToastrService,private router : Router
  ) { 
    this.materiSlug = localStorage.getItem('materiSlug');
    this.topicslug = localStorage.getItem('topicslug');
    this.lessonSlug = localStorage.getItem('lessonSlug');
  }

  ngOnInit() {
    this.initForm();   
    this.getQuizDetail();
    this.href = localStorage.getItem('currentMateri'); 
  }
  private initForm() {
    let name = '';
    let description = '';
    let questions = new FormArray([]);
    this.editQuizForm = new FormGroup({
      'name': new FormControl(name,[Validators.required]),'description': new FormControl(description,'materialTopicslug': new FormControl(localStorage.getItem('topicslug'),'questions': questions,});
    this.onAddCard();
  }
  get questionFormArray() {
    return this.editQuizForm.get('questions') as FormArray;
  }
  getQuizDetail() {
    this.userService.getAdminQuizDetail(this.lessonSlug).subscribe(
      (data: any) => {
        console.log(data);
        this.quizData = data.data;
        this.editQuizForm.patchValue({
          name : this.quizData.name,description : this.quizData.description,questions : this.quizData.questions,});
      },(error: any) => {
        console.log(error);
      }
    )
  }
  //Ubah Order dengan Drag
  drop(event: CdkDragDrop<string[]>) {
    this.list = this.editQuizForm.get("questions")["controls"];
    console.log(this.list);
    moveItemInArray(this.list,event.prevIoUsIndex,event.currentIndex);
    this.questionFormArray.controls[event.currentIndex]['controls']['display_order']
      .setValue(event.currentIndex + 1);
    this.questionFormArray.controls.forEach((category,index) => {
      (category as FormGroup).controls['display_order'].setValue(index + 1);
    });
    moveItemInArray(this.editQuizForm.get('questions')['controls'],event.currentIndex);
    this.editQuizForm.get('questions').updateValueAndValidity({ onlySelf: false });
  }
  showCreateQuizToast(message,status: NbComponentStatus) {
    this.toastrService.show(status,`${message}`,{ status });
  }
  //Fungsi Tambah Pertanyaan
  onAddCard() {
    console.log(this.editQuizForm.controls);
    const quizQuestionItem = new FormGroup({
      // questionType: new FormControl("",Validators.required),display_order: new FormControl(this.questionFormArray.length + 1),questionTitle: new FormControl("",options: new FormArray([])
      // questionGroup: new FormGroup({
      // })
    });
    (<FormArray>this.editQuizForm.get('questions')).push(quizQuestionItem);
  }
  onRemoveCard(index) {
    this.editQuizForm.controls.questions['controls'][index].controls.questionGroup = new FormGroup({});
    this.editQuizForm.controls.questions['controls'][index].controls.questionType = new FormControl({});
    (<FormArray>this.editQuizForm.get('questions')).removeAt(index);
    this.selectedOption.splice(index,1)
    console.log(this.editQuizForm);
  }
  //Fungsi Tambah Opsi untuk Kotak Centang dan Radio
  addOptionControls(questionType,index) {
    let options = new FormArray([]);
    (this.editQuizForm.controls.questions['controls'][index]).addControl('options',options);
    this.clearFormArray((<FormArray>this.editQuizForm.controls.questions['controls'][index].controls.options));
    this.addOption(index);
    this.addOption(index);
  }
  private clearFormArray(formArray: FormArray) {
    while (formArray.length !== 0) {
      formArray.removeAt(0)
    }
  }
  checked = false;
  toggle(checked: boolean) {
    return this.checked = checked;
  }
  toggleAnswer(checked: boolean) {
    console.log(checked);
    
  }
  addOption(index) {
    const optionGroup = new FormGroup({
      'optionText': new FormControl('','isAnswer': new FormControl(this.toggle(this.checked),});
    (<FormArray>this.editQuizForm.controls.questions['controls'][index].controls.options).push(optionGroup);
  }

  removeOption(questionIndex,itemIndex) {
    (<FormArray>this.editQuizForm.controls.questions['controls'][questionIndex].controls.options).removeAt(itemIndex);
  }
  //Fungsi Simpan Form Dalam JSON
  postQuiz() {
    let formData = this.editQuizForm.value;
    console.log(formData);
    this.userService.addAdminMateriTopicQuiz(formData).subscribe(
      (data:any)=>{
        console.log(data);
        this.showCreateQuizToast(data.message[0].message,'success');
        this.router.navigateByUrl(this.href);
      },error=>console.log(error)
    )
    // let editQuizFormData: any;
    // editQuizFormData = {
    //   name: this.editQuizForm.get('name').value,//   description: this.editQuizForm.get('description').value,//   materialTopicslug : this.editQuizForm.get('description').value,//   questions : this.editQuizForm.get('questions').value,// }
    // console.log(editQuizFormData);

    // console.log();
    // let ID = 0;
    // let Desc = formData.description;
    // let Name = formData.name;
    // let quizID = formData.nomorquiz;
    // let jenisquiz = formData.selectedPanduan;
    // let IsDeleted = false; 
    // let Question: Question[] = [];
    // let Questions = [];
    // let questions = formData.questions;
    // let optionArray = formData.questions[0].questionGroup.options[0].optionText
    // let survey = new Survey(ID,Desc,Name,IsDeleted,quizID,jenisquiz,Questions);
    // questions.forEach((question,index,array) => {
    //   let questionItem = {
    //     'ID': 0,//     "Type": question.questionType,//     "Text": question.questionTitle,//     "options": [],//     "required": false,//   }
    //   if (question.questionGroup.hasOwnProperty('options')) {
    //     question.questionGroup.options.forEach(option => {
    //       let optionItem: Option = {
    //         "ID": 0,//         "OptionText": option.optionText,//         "OptionColor": ""
    //       }
    //       questionItem.options.push(optionItem)
    //     });
    //   }

    //   survey.Question.push(questionItem)

    // });
    // console.log(survey);
    console.log('posting survey');
  }
  onSubmit() {
    this.postQuiz();
  }
}
@import "../../../../../@theme/styles/themes";

@include nb-install-component() {
    .buttons-row {
        margin: -0.5rem;
    }

    button[nbButton] {
        margin: 0.5rem;
    }

    .action-icon {
        @include nb-ltr(margin-right,0.5rem);
        @include nb-rtl(margin-left,0.5rem);
    }

    .actions-card {
        height: 8rem;
    }
}

.nb-theme-default [nbButton].appearance-filled.size-medium {
    margin: 0px;
}

.myIframe {
    position: relative;
    padding-bottom: 50.25%;
    padding-top: 30px;
    height: 0;
    overflow: auto;
    -webkit-overflow-scrolling: touch; //<<--- THIS IS THE KEY
}
.myIframe iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

.stepContainer {
    width: 800px;
    margin: auto;
}
.stepEmph {
    font-size: x-large;
    line-height: 2rem;
}
.divider {
    height: 5px;
    width: 60px;
    background-color: #3366ff;
    border-radius: 5px;
    margin: auto;
}
@media screen and (max-width: 900px) {
    .stepContainer {
        width: 100%;
        margin: auto;
    }
    .stepEmph {
        font-size: larger;
        line-height: 1.7rem;
    }
}
@media screen and (max-width: 425px) {
    .stepEmph {
        font-size: medium;
        line-height: 1.5rem;
    }
}

.form-group {
    width: 100%;
    margin: 10px auto;
    padding: 0px 15px;
}

.myIframe {
    position: relative;
    padding-bottom: 50.25%;
    padding-top: 30px;
    height: 0;
    overflow: auto;
    -webkit-overflow-scrolling: touch; //<<--- THIS IS THE KEY
}
.myIframe iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

.floatingButtonContainer {
    position: fixed;
    bottom: 1rem;
    right: 1.5rem;
    width: 4.4em;
    z-index: 10;
}
.floatingButton {
    border-radius: 50% !important;
    padding: 1.3rem !important;
    z-index: 1000;
    transition: all 0.5s ease;
    transform: translateY(0px);
    opacity: 1;
}
.floatingButtonMenu {
    border-radius: 50% !important;
    padding: 0.9rem !important;
    z-index: 1000;
    transition: all 0.5s cubic-bezier(0.01,0.62,0.32,0.97);
    transform: translateY(0px);
    margin: 5px !important;
}
.fabMenuShow {
    display: block;
    list-style: none;
    opacity: 1;
    transform: translateY(0px);
    transition: all 0.5s cubic-bezier(0.01,0.97);
}
.fabMenuHide {
    display: block;
    list-style: none;
    opacity: 0;
    transform: translateY(10px);
    transition: all 0.5s cubic-bezier(0.01,0.97);
}
.fabMenuShow li,.fabMenuHide li {
    float: right;
    margin-bottom: 5px;
}

.cdk-drag-placeholder {
    opacity: 0;
}

.cdk-drag-animating {
    transition: transform 250ms cubic-bezier(0,0.2,1);
}

.formlist:last-child {
    border: none;
}

.formlistsContainer.cdk-drop-list-dragging .formlist:not(.cdk-drag-placeholder) {
    transition: transform 250ms cubic-bezier(0,1);
}
.cdk-drag-preview {
    // Box-shadow: 0 5px 5px -3px rgba(0,0.2),0 8px 10px 1px rgba(0,0.14),0 3px 14px 2px rgba(0,0.12);
    Box-shadow: 0 0.5rem 1rem 0 rgba(44,51,73,0.2);
    list-style: none;
}
<div class="floatingButtonContainer">
    <button nbButton (click)="onAddCard()" class="floatingButton" nbTooltip="Tambah Form" nbTooltipStatus="primary"
        nbTooltipPlacement="left">
        <nb-icon icon="plus-outline"></nb-icon>
    </button>
</div>
<form [formGroup]="editQuizForm" (ngSubmit)="onSubmit()" autocomplete="off" class="row">
    <nb-card class="col-lg-5" size="small" style="height: 250px;">
        <div class="form-group">
            <label for="exampleInputEmail1" class="label">Nama Quiz</label>
            <input type="text" formControlName="name" nbInput fullWidth fieldSize="medium" placeholder="Nama Quiz">
        </div>
        <div class="form-group">
            <label for="exampleInputEmail1" class="label">Nama Quiz</label>
            <textarea formControlName="description" nbInput fullWidth placeholder="Deskripsi Quiz"></textarea>
        </div>
    </nb-card>
    <div formArrayName="questions" class="poll-options" class="col-lg-7">
        <ol style="list-style: none;padding: 0;" cdkDropList (cdkDropListDropped)="drop($event)"
            class="formlistsContainer">
            <li *ngFor="let questionCtrl of editQuizForm.get('questions')['controls']; let i = index" cdkDrag
                cdkDragLockAxis="y" class="formlist">
                <nb-card style="position: relative;" [formGroupName]="i">
                    <nb-card-header cdkDragHandle
                        style="text-align: center;padding: 0px;cursor: pointer;border-bottom: 0;">
                        <nb-icon icon="more-horizontal-outline"></nb-icon>
                    </nb-card-header>
                    <button *ngIf="i>=0" (click)="onRemoveCard(i)" status="danger"
                        style="padding:0px;border-radius:50%;position: absolute;top: -20px;right: -20px;width: 40px;height: 40px;"
                        nbButton>
                        <nb-icon icon="close-outline"></nb-icon>
                    </button>
                    <nb-card-body>
                        <div class="form-group row" style="padding: 0px;">
                            <div class="col-12" style="padding: 0px;">
                                <div>
                                    <div *ngIf="questionCtrl.controls.options">
                                        <div class="col-12" style="padding:0px;margin:5px auto;">
                                            <textarea placeholder="Pertanyaan" formControlName="questionTitle" nbInput
                                                fullWidth></textarea>
                                        </div>
                                        <ul style="padding: 0;" formArrayName="options">
                                            <li style="list-style: none;"
                                                *ngFor="let optionCtrl of questionCtrl.controls.options.controls let j = index">
                                                <div [formGroupName]="j">
                                                    <button style="margin: 0px 5px;" nbButton *ngIf="j>=0"
                                                        (click)="removeOption(i,j)" status="danger">X
                                                    </button>
                                                    <input style="margin: 5px auto;max-width: 60%;width: 50%;" nbInput
                                                        formControlName="optionText" placeholder="option text"
                                                        maxlength="100" [required]="true">
                                                    <nb-checkBox formControlName="isAnswer">Jawaban</nb-checkBox>
                                                </div>
                                            </li>
                                        </ul>
                                        <button nbButton status="primary" type="button" (click)="addOption(i)"
                                            style="margin-top:5px;" color="accent">
                                            <nb-icon icon="plus-outline"></nb-icon> Add option
                                        </button>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </nb-card-body>
                </nb-card>
            </li>
        </ol>
    </div>
</form>
<button nbButton fulWidth status="success" (click)="postQuiz()">Simpan Form</button>

quizData响应

   {
  "name": "quiz 2","description": "desk quiz 2","questions": [
    {
      "code": "Yb58vyqyoocgu7k","name": "tanya 1","order": null,"options": [
        {
          "code": "xqANiy2YY5hBp0f","name": "test","order": 1
        }
      ]
    },{
      "code": "XEj18dHM2UEd7um","name": "tanya 2","options": [
        {
          "code": "XDcGJ3ujHZYyrf2","name": "test 2","order": 1
        }
      ]
    }
  ]
}

enter image description here

哦,右边应该是表单的数组,响应中的questionsoptions应该在表单上,​​因为我在patchValue添加editQuizForm。但这不起作用,有人知道吗?

这是它的外观

enter image description here

解决方法

因此,我没有使用让我头疼的 patchValue,而是像这样将 API 中的值添加到每个表单控件中

let itemControl = this.fb.group({
  name: [
    sectionItems ? sectionItems.name : "",Validators.compose([Validators.required]),],code: [
    sectionItems ? sectionItems.code : "",questionType: [
    sectionItems ? sectionItems.type : "",url: [
    sectionItems ? sectionItems.url : "",filePath: [
    sectionItems ? sectionItems.filePath : "",materialSlug: [
    sectionItems && sectionItems.material != null
      ? sectionItems.material.slug
      : "",description: [
    sectionItems ? sectionItems.description : "",options: this.fb.array([]),display_order: new FormControl(max + 1),});

如果没有将其设置为空,则检查控件是否具有现有值""

为了将所有数据显示为表单数组,我做了这样的事情

(<FormArray>this.editSectionForm.get("formItems")).push(itemControl);

通过这种方式,我能够根据 JSON 数据以表单类型在卡片上显示我的所有数据,并且用户可以更改、添加和删除表单