Какой синтаксис и шаблоны проектирования я должен использовать для работы с предварительно заполненной формой Angular? - PullRequest
0 голосов
/ 13 февраля 2020

Я создаю микро-приложение для анализа и сохранения SVG как JSON объекта, который я хочу иметь возможность просматривать и редактировать перед сохранением его в базе данных. Видя, что анализатор генерирует данные, которые, как я понял, было бы проще создать семейство функций, которые превращают эти данные в законченный объект формы angular, который я, в свою очередь, могу перебрать в своем приложении-редакторе для управления. Видя, что я не начинаю с пустой формы, как я go об этом, довольно сильно отличается от того, что я вижу во всех уроках. Например, когда они делают что-то на своих входах, например, value="someControl", мне нужно сделать что-то вроде [attr.value]="someFormGroup.controls.someControl.value[0]", чтобы получить доступ к значениям моих элементов управления.

Чем больше я изучаю, тем больше путаюсь, потому что так много различий в том, как они go рассказывают о вещах, и я понятия не имею, какие методы подходят для моего варианта использования. Один учебник будет использовать formControlName="", другой будет использовать [formControl]="", моя консоль дает пример, который использует [formControlName]="" всякий раз, когда я делаю что-то, что разрывает соединение с [formGroup]. Затем использование этого синтаксиса приводит к другим ошибкам, которые после расследования привели меня к появлению здесь сообщений о том, что мы должны удалить [] и просто использовать вместо него formControlName, так что я действительно в растерянности относительно того, как собрать все это вместе должным образом.

Для начала, вот как формируются мои данные после анализа SVG

  export interface OvaadSvgDataObject{
    title           : string;
    graphicId       : string;
    viewBox         : ViewBoxParameters;
    coreAttributes? : OvaadGraphicAttribute[];
    coreStyles?     : OvaadSvgStyleProperty[];
    elements        : OvaadGraphicObject[];
  }

  export interface ViewBoxParameters{
    x      : string;
    y      : string;
    width  : string;
    height : string;
  }

  export interface OvaadGraphicObject{
    element      : string;
    selfClosing  : boolean;
    attributes   : OvaadGraphicAttribute[];
    styles?      : OvaadSvgStyleProperty[];
    subElements? : OvaadGraphicObject[];
  }

  export interface OvaadSvgStyleProperty{
    property : string;
    setting  : string;
  }

  export interface OvaadGraphicAttribute{
    attribute : string;
    setting   : string;
  }

Мои функции для создания формы из OvaadSvgObject выглядят следующим образом

CreateObjectForm ()

export function CreateGraphicObjectForm(data?: OvaadSvgDataObject): FormGroup{

    let graphicObjectForm: FormGroup = new FormGroup({
      title          : new FormControl([(data ? data.title     : ''), Validators.required]),
      graphicId      : new FormControl([(data ? data.graphicId : ''), Validators.required]),
      viewBox        : CreateViewBoxForm((data ? data.viewBox : undefined)),
      coreAttributes : new FormArray([]),
      coreStyles     : new FormArray([]),
      elements       : new FormArray([])
    });


    if(data && data.coreAttributes.length > 0){
      data.coreAttributes.forEach((a: OvaadGraphicAttribute)=>{

        let coreAttrArray: FormArray = graphicObjectForm.get('coreAttributes') as FormArray;

        coreAttrArray.push(CreateAttributeForm(a));

      });
    }

    if(data && data.coreStyles.length > 0){
      data.coreStyles.forEach((a: OvaadSvgStyleProperty)=>{

        let coreStyleArray: FormArray = graphicObjectForm.get('coreStyles') as FormArray;

        coreStyleArray.push(CreateStylePropertyForm(a));

      });
    }

    if(data && data.elements.length > 0){
      data.elements.forEach((a: OvaadGraphicObject)=>{

        let elementArray: FormArray = graphicObjectForm.get('elements') as FormArray;

        elementArray.push(CreateGraphicElementForm(a));

      });
    }

    return graphicObjectForm as FormGroup;
  }

CreateViewBoxForm ()

export function CreateViewBoxForm(data?: ViewBoxParameters): FormGroup{

    return new FormGroup({
      x      :  new FormControl([(data ? data.x      : ''), Validators.required]),
      y      :  new FormControl([(data ? data.y      : ''), Validators.required]),
      width  :  new FormControl([(data ? data.width  : ''), Validators.required]),
      height :  new FormControl([(data ? data.height : ''), Validators.required])
    }) as FormGroup;

  }

CreateAttributeForm ()

export function CreateAttributeForm(data?: OvaadGraphicAttribute): FormGroup{

    return new FormGroup({
      attribute : new FormControl([(data ? data.attribute : ''), Validators.required]),
      setting   : new FormControl([(data ? data.setting   : ''), Validators.required])
    }) as FormGroup;

  }

CreateStylePropertyForm ()

export function CreateStylePropertyForm(data?: OvaadSvgStyleProperty): FormGroup{

    return new FormGroup({
      property : new FormControl([(data ? data.property : ''), Validators.required]),
      setting  : new FormControl([(data ? data.setting  : ''), Validators.required])
    }) as FormGroup;

  }

CreateGraphicElementForm ()

export function CreateGraphicElementForm(data?: OvaadGraphicObject): FormGroup{
    let elementForm: FormGroup = new FormGroup({
      element     : new FormControl([(data ? data.element    : ''), Validators.required]),
      selfClosing : new FormControl([(data ? data.selfClosing : false), Validators.required]),
      attributes  : new FormArray([]),
      styles      : new FormArray([]),
      subElements : new FormArray([])
    });

    if(data && data.attributes.length > 0){
      data.attributes.forEach((a: OvaadGraphicAttribute)=>{
        let attrArray: FormArray = elementForm.get('attributes') as FormArray;
        attrArray.push(CreateAttributeForm(a));
      });
    }

    if(data && data.styles.length > 0){
      data.styles.forEach((a: OvaadSvgStyleProperty)=>{
        let styleArray: FormArray = elementForm.get('styles') as FormArray;
        styleArray.push(CreateStylePropertyForm(a));
      });
    }

    if(data && data.subElements){
      data.subElements.forEach((a:OvaadGraphicObject)=>{
        let subElementArray: FormArray = elementForm.get('subElements') as FormArray;
        subElementArray.push(CreateGraphicElementForm(a));
      });

    }

    return elementForm as FormGroup;
  }

Я создал семейство компонентов для итерации возвращаемой группы FormGroup, которую я вызываю:

  • SvgObjectFormComponent
  • ViewBoxFormComponent
  • AttributeListComponent
  • AttributeFormComponent
  • StyleListComponen t
  • PropertyFormComponent
  • ElementListComponent
  • ElementFormComponent

Мне удалось передать FormGroups и FormArrays через эту структуру, используя @Input() Однако при реализации ControlValueAccessor я снова был озадачен тем, как осуществляется доступ к значениям. Из этого учебного пособия я вижу, что они добавляют formControlName="" к своему пользовательскому компоненту, но я не могу понять, где он подключен к FormGroup внутри их компонентов. Затем, думая о том факте, что они начинают с пустой формы для передачи значения по цепочке в родительскую форму, я не уверен, что этот метод позволяет передавать значения в компонент, и не может найти что-либо говорящее о если это можно сделать, или если мне нужно будет использовать @Input() и подключить два других типа пути внутри компонента.

Мне также было трудно найти информацию о том, как обрабатывать FormArrays с ControlValueAccessor, что оставляет меня в неведении относительно того, что нужно сделать с моими AttributeListComponent, StyleListComponent и ElementListComponent, потому что я передаю FormArrays в них и перебираю каждый объект в их соответствующие подкомпоненты , Итак, в случае итерации моего AttributeFormComponent внутри моего AttributeListCOmponent, что я передам в formControlName моего AttributeFormComponent из *ngFor l oop? Будет ли это просто индекс элемента в массиве? Будет ли это автоматически меняться и обновляться, как все связано, если я удаляю элемент из FormArray? Это лишь некоторые из вопросов, которые крутятся вокруг, и я не могу найти однозначных ответов.

Вот как я настроил AttributeListComponent.

AttrubuteListComponent.ts

@Component({
  selector: 'attribute-list-component',
  templateUrl: './attribute-list.component.html',
  styleUrls: ['./attribute-list.component.css']
})

export class AttributeListComponent implements OnInit {

  @Input() AttributeListData: FormArray;

  constructor() {}
  ngOnInit() {}

  addAttribute(): void{

    const newAttribute: FormGroup = CreateAttributeForm();
    let attrList: FormArray = <FormArray>this.AttributeListData as FormArray;

    attrList.push(newAttribute);
  }

  deleteAttribute(item: number): void{

    let attrList:FormArray = this.AttributeListData as FormArray;

    attrList.removeAt(item);
  }

}

AttributeListComponent. html

<section>

    <article *ngIf="!AttributeListData">
        <p>loading</p>
    </article>

    <article *ngIf="AttributeListData">

        <h5 *ngIf="AttributeListData.controls.length === 0" class="articleText">no attributes</h5>

        <section formArrayName="AttributeListData" *ngIf="AttributeListData.controls.length > 0">

            <article *ngFor="let item of AttributeListData.controls; let i = index" class="list-grid">

                <p class="index-area articleText">{{i}}</p>



                <attribute-form-component  [AttributeFormData]="item" class="component-area"></attribute-form-component>



                <article class="delete-area">
                    <button (click)="deleteAttribute(i)">delete</button>
                </article>

            </article>

        </section>

    </article>

    <article>
        <button (click)="addAttribute()">add</button>
    </article>

</section>

AttributeFormComponent.ts

@Component({
  selector: 'attribute-form-component',
  templateUrl: './attribute-form.component.html',
  styleUrls: ['./attribute-form.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(()=>AttributeFormComponent),
      multi: true
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(()=>AttributeFormComponent)
    }
  ]
})

export class AttributeFormComponent implements OnInit, ControlValueAccessor {

  @Input() AttributeFormData: FormGroup;

  constructor() { }

  ngOnInit() {
  }

  public onTouched : ()=> void =()=>{};

  writeValue(val: any):void{ val && this.AttributeFormData.setValue(val, {emitEvent: false}); console.log(val); }

  registerOnChange(fn: any): void{ this.AttributeFormData.valueChanges.subscribe(fn); console.log(fn); }

  registerOnTouched(fn: any): void{ this.onTouched = fn; }

  setDisabledState?(isDisabled: boolean): void{ isDisabled ? this.AttributeFormData.disable() : this.AttributeFormData.enable(); }

  validate(c: AbstractControl): ValidationErrors | null{
    return this.AttributeFormData.valid ? null : {invalidForm:{valid: false, message: 'field invalid'}};
  }

}

AttrubuteFormComponent. html

<article [formGroup]="AttributeFormData" class="duo-text-control-section">


    <label class="duo-control-label">

        <p class="articleText">attribute:</p>
        <input
            type="text"
            class="duo-text-control"
            [formControl]="AttributeFormData.controls.attribute"
        />

    </label>

    <label class="duo-control-label">

        <p class="smallText">setting:</p>
        <input
            type="text"
            class="duo-text-control"
            [formControl]="AttributeFormData.controls.setting"
        />

    </label>
</article>

До сих пор я только реализовал ControlValueAccessor в AttributeFormComponent, полагая, что я начну с нижней части дерева компонентов и продолжу свой путь вверх, и на данный момент я получаю ошибку от моего AttributeListComponent говоря мне, что для использования formArrayName="" мне нужно иметь родительское [formGroup] however I'm passing the entire FormArray into the component so that array is the top level item. I figured I could solve the problem by creating a new FormGroup ({}) instance in my component with a single FormArray ([]) `свойство, в которое я мог бы передать массив и перебрать его в шаблон оттуда. Из-за препятствий, с которыми я столкнулся при использовании форм angular, я чувствую, что это может разорвать цепь связи между компонентом и верхним уровнем формы.

Мои StyleListComponent и ElementListComponent являются настроить с одинаковым шаблоном с соответствующими полями, а SvgObjectFormComponent и ElementFormComponent являются просто расширенными версиями одного и того же шаблона и учитывая, как долго он уже существует, и моя проблема больше в правильном синтаксисе, я не думаю, что это действительно необходимо опубликовать все эти компоненты только для того, чтобы вы посмотрели на один и тот же точный процесс, однако, если потребуется больше информации, я был бы более чем рад обновить это тем, что вам нужно посмотреть. Я мог бы sh Я мог бы быть более кратким и прямым об этом, но для меня слишком много "неизвестных", чтобы передать это как-то яснее или отточить что-то более конкретное c, поэтому я ценю терпение каждого.

...