Я создаю микро-приложение для анализа и сохранения 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, поэтому я ценю терпение каждого.