Я создаю микро-приложение для редактирования JSON объектов перед сохранением их в моей базе данных. Объект JSON взят из другого микроприложения, которое я создал для анализа кода SVG и создания объекта данных в форме интерфейсов, приведенных ниже.
export interface OvaadSvgStyleProperty{
property: string;
setting: string;
}
export interface OvaadGraphicAttribute{
attribute: string;
setting: string;
bind? : string;
}
export interface OvaadGraphicObject{
element: string;
selfClosing: boolean;
attributes: OvaadGraphicAttribute[];
styles?: OvaadSvgStyleProperty[];
subElements?: OvaadGraphicObject[];
}
export interface ViewBoxParameters{
x: string;
y: string;
width: string;
height: string;
}
export interface OvaadSvgDataObject extends TitleCore{
graphicId: string;
viewBox: ViewBoxParameters;
coreAttributes: OvaadGraphicAttribute[];
coreStyles: OvaadSvgStyleProperty[];
elements: OvaadGraphicObject[];
}
Если вы обратите внимание на интерфейс OvaadGraphicObject
, вы увидите свойство subElements
имеет тип OvaadGraphicObject[]
, который предназначен для обработки экземпляров тегов <g>
и других элементов svg, которые потенциально могут вложить вещи в тысячи слоев глубиной, если бы это было необходимо по какой-то причине.
I ' До сих пор я училась через мою борьбу с Angular Forms, и ей нравится, когда форма является единственной монолитной c вещью, поэтому я решила создать семейство функций для генерации формы и передачи значений из JSON объект и передача заполненного FormGroup
в мою форму с [formGroup]="myFormVar"
.
. Это обеспечивает FormGroup
, сформированный в соответствии со всеми значениями, переданными, как ожидалось. Если я обращаюсь с данными как с любыми другими данными и передаю их в @Input()
s, все ломается и идет туда, куда нужно go, но, к сожалению, оно разрывает связь внутри формы. Вы можете просмотреть демонстрацию stackblitz здесь, чтобы увидеть весь механизм до сих пор, но ради этого поста я сосредоточусь на аспекте elements
формы, который в значительной степени раскроет загадку того, как для обработки всего остального.
Для начала функция, которую я использую для создания элемента FormGroup
, выглядит следующим образом
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
есть 2 FormControl
s и 3 FormArray
s, которые необходимо передать в соответствующие компоненты. Я обращаюсь с этой частью с помощью того, что я называю, и Element List Component
, который принимает массив этих FormGroup
и отвечает за их добавление или удаление. Тогда у меня есть ElementFormComponent
, который получает FormGroup
от ElementListComponent
. Затем, конечно, ElementListComponent
получает данные от родительского компонента, где создается форма, которую я называю SvgObjectFormComponent
. Вот как выглядят ElementListComponent
и ElementFormComponent
.
ElementList.component.ts
export class ElementListComponent implements OnInit {
@Input() ElementListData: FormArray;
constructor() { }
ngOnInit() {
}
addElement(): void{
const newElement: FormGroup = CreateGraphicElementForm();
let elementList: FormArray = <FormArray>this.ElementListData as FormArray;
console.log(newElement);
elementList.push(newElement);
}
deleteElement(item: number): void{
let elementList:FormArray = this.ElementListData as FormArray;
console.log(item);
elementList.removeAt(item);
}
}
ElementList.component. html
<section [formGroup]="ElementListData">
<article *ngIf="!ElementListData">
<p>loading</p>
</article>
<article *ngIf="ElementListData">
<h5 *ngIf="ElementListData.controls.length === 0" class="articleText">no elements</h5>
<section *ngIf="ElementListData.controls.length > 0">
<article *ngFor="let item of ElementListData.controls; let i = index" class="list-grid">
<p class="index-area articleText">{{i}}</p>
<!-- I removed attempts at passing in data to avoid discussing things we already know are wrong-->
<element-form-component class="component-area"></element-form-component>
<article class="delete-area">
<button (click)="deleteElement(i)">delete</button>
</article>
</article>
</section>
</article>
<article>
<button (click)="addElement()">add</button>
</article>
</section>
ElementForm.component.ts
export class ElementFormComponent implements OnInit, ControlValueAccessor {
@Input() ElementFormData: FormGroup;
constructor() { }
ngOnInit() {}
}
ElementForm.component. html
<section [formGroup]="ElementFormData">
<article class="text-control-section">
<label class="control-label">
element:
<input type="text" class="text-control" formControlName="element" />
</label>
<label class="control-label">
self closing:
<input type="text" class="text-control" formControlName="selfClosing" />
</label>
</article>
<!-- again I eliminated failed attempts at passing objects into components to reduce confusion -->
<article>
<h3>Attributes</h3>
<attribute-list-component></attribute-list-component>
</article>
<article>
<h3>Styles</h3>
<style-list-component></style-list-component>
</article>
<section>
<h3>Sub Elements</h3>
<p *ngIf="ElementFormData.controls.subElements.length === 0">no sub elements</p>
<article *ngFor="let item of ElementFormData.controls.subElements; let i = index" class="list-grid">
<p class="index-area">{{i}}</p>
<!-- this is where the recursive behavior begins if the element has nested elements -->
<element-form-component class="component-area"></element-form-component>
<article class="delete-area">
<button>delete element</button>
</article>
</article>
</section>
</section>
Я пробовал все, используя [(ngModel)]
и [FormControl]
против formControlName
против [formControlName]
и возиться с ControlValueAccessor
, просто чтобы обнаружить, что он предназначен только для одиночных FormControl
с. На другой вопрос, который я задал, и который был основан на ControlValueAccessor
, кто-то предложил мне добавить это к своим поставщикам компонентов
viewProviders: [
{ provide: ControlContainer, useExisting: FormGroupDirective }
, что устраняет необходимость в ControlValueAccesssor
, позволяющем всем элементам управления быть доступ, но это предполагает, что я использую свой компонент только в одном месте в форме синхронизации только с одним FormGroup
, когда, как вы видите из этого примера, мне нужно выяснить, как обеспечить рекурсивное соединение с вершиной дерева компонентов , Как можно достичь такого типа поведения с помощью Angular Forms?
Мой стековый блик содержит все компоненты, функции и объект данных для демонстрации, а также мои самые последние попытки заставить это поведение работать, которое было через ControlValueAcessor
. Кто-то, пожалуйста, помогите мне разобраться, как выполнить sh, потому что Angular Формы - это единственное, что я не смог получить gr asp за все время использования платформы, потому что не знал что бы то ни было, я не могу найти дорогу перед собой.