В настоящее время я экспериментирую с дизайном компонента приложения в Angular 7.*. Не много поработав с ним, мне трудно разработать чистую архитектуру компонентов.
Этот компонент, назовем его MainComponent, получает JSON ввод, такой как
{
"name": "name",
"description": "description",
"attributes": [ // Rectangle in the image
{
"attr1": "value", // Editable
"attr2": "value", // Editable
"attr3": "value" // Editable
},
{
"attr1": "value", // Editable
"attr2": "value", // Editable
"attr3": "value" // Editable
},
...
]
}
Важным полем является attributes. Каждый его элемент будет отображаться как отдельная область, которую я хотел бы закодировать как новый компонент.
Каждый элемент attributes становится прямоугольником внутри основного.
И каждое поле attribute редактируется, поэтому FormGroup необходимо создать.
Однако сохранение является глобальным, а не для подкомпонента.
И мне нужно позаботиться о том, чтобы «прослушать», какие значения были изменены внутри каждого компонента.
Я думал о двух решениях.
Имейте FormGroup на основном уровне с несколькими под-FormGroup-ами, по одному на каждый подкомпонент. Каждый суб-FormGroup вводится внутри суб-Component. При сохранении я просто смотрю на основную группу форм, она уже содержит все измененные значения.
Каждому подкомпоненту принадлежит отдельный FormGroup. При сохранении основной компонент спрашивает (то есть вызов метода) каждого подкомпонента для извлечения измененных значений.
Какое решение больше всего подходит для Angular? Что бы ты сделал?
Редактировать: я спрашиваю, как лучше распределить ввод JSON по нескольким подкомпонентам, позаботившись о получении отредактированных значений при сохранении, которое находится на верхнем/основном уровне.
@MsuArven, что вы имеете в виду под компонентом общий? Да "проблема" именно в этом!
Я думаю, что лучший подход — использовать formArray Проверьте эту ссылку: angular.io/guide/…
@BunyaminCoskuner в основном внутри основного компонента, мне пришлось бы создать FormGroup, который содержит FormArray из FormGroups? Это то, что вы говорите? Таким образом, ответ будет Опция 1 (управлять состоянием и FormGroup только на основном уровне)
Кроме того, вам нужно сделать так, чтобы ваш компонент Input реализовывал ControlValueAccessor, чтобы вы могли использовать formControlName в этом компоненте.
Да, таким образом вашему компоненту main не нужно знать, сколько у него подкомпонентов. Каждый подкомпонент будет обновлять formArray значения formGroup, если вы правильно настроили его.
@BunyaminCoskuner каждый подкомпонент будет принимать один FormGroup (который происходит от FormArray), верно?
Нет, они не принимают никаких входных данных, им просто нужно реализовать ControlValueAccessor, чтобы вы могли использовать formControlName на них. Им не нужно знать о formGroup
@BunyaminCoskuner, поэтому я могу просто использовать (например) formControlName для элемента ввода внутри шаблона подкомпонента? Действительно? мне нужно понять как это работает
Я создал стекблиц, скоро выложу
@BunyaminCoskuner спасибо!
@BunyaminCoskuner, быстрое предупреждение, помните, что каждый квадрат «Входные данные» выше содержит несколько редактируемых значений (attr1, attr2, ecc). Можно ли использовать ControlValueAccessor?
Вы можете поместить их в один объект, не так ли?
@BunyaminCoskuner на данный момент они содержатся в одном объекте { "attr1": "value", "attr2": "value", ... }





Я думаю, что лучший способ решить такие проблемы (обмен данными). мы всегда должны создавать общий сервис. поместите один FormGroup там. и напишите методы в каждом компоненте для обновления значения этой группы форм. и, в конечном итоге, метод получения значения группы форм. На сегодняшний день это лучший способ написать общий код FormGroup в службах.
this.lotTwoFormGroup = this.formBuilder.group({
title: ['', Validators.compose([Validators.required])],
description: ['', Validators.compose([Validators.required])],
dutchTitle: [''],
dutchDescription: [''],
frenchTitle: [''],
frenchDescription: ['']
});
Вот рабочий раствор.
Я использовал FormArray. Для получения дополнительной информации прочитайте здесь
Во-первых, давайте создадим компонент ввода и назовем его MyInputComponent
@Component({
selector: 'my-input',
template: `
<div>
Attr1: <input type = "text" [ngModel] = "value.attr1"
(ngModelChange) = "updateModel($event, 'attr1')" />
</div>
<div>
Attr2: <input type = "text" [ngModel] = "value.attr2"
(ngModelChange) = "updateModel($event, 'attr2')" />
</div>
`
})
export class MyInputComponent {
value;
updateModel(value, attrName) {
this.value[attrName] = value;
}
}
Это довольно простой компонент. Он содержит два входа (вы можете добавить больше) и привязывает модель к этому входу с помощью ngModel. На данный момент он ничего не раскрывает внешнему миру. При изменении любого из входов он соответственно обновляется value.
Теперь давайте использовать его в нашем main.component
Допустим, у вас есть следующие данные
attributes = [
{ 'attr1': 'value1', 'attr2': 'value12'},
{ 'attr1': 'value2', 'attr2': 'value22'},
{ 'attr1': 'value3', 'attr2': 'value32'},
];
И вы можете использовать этот компонент в своем шаблоне следующим образом.
<div *ngFor = "let attr of attributes; let i = index">
<my-input></my-input>
<hr />
</div>
Теперь давайте привяжем formArrayName и formControlName к этому входу.
Для этого мы импортируем ReactiveFormsModule в наш модуль и заставляем MyInput внедрять ControlValueAccessor
my-input.component
@Component({
selector: 'my-input',
template: `
<div>
Attr1: <input type = "text" [ngModel] = "value.attr1" (ngModelChange) = "updateModel($event, 'attr1')" />
</div>
<div>
Attr2: <input type = "text" [ngModel] = "value.attr2" (ngModelChange) = "updateModel($event, 'attr2')" />
</div>
`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MyInputComponent),
multi: true
}
]
})
export class MyInputComponent implements ControlValueAccessor {
value;
onChange;
onTouched;
disabled = false;
updateModel(value, attrName) {
this.value[attrName] = value;
this.onChange(this.value); // now I call onChange method to update the value within form
}
// comes from ControlValueAccessor
writeValue(newValue): void {
this.value = newValue;
}
// comes from ControlValueAccessor
registerOnChange(fn: (rating: number) => void): void {
this.onChange = fn;
}
// comes from ControlValueAccessor
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
// comes from ControlValueAccessor
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
}
И измените шаблон основного компонента на
<div [formGroup] = "myForm">
<div formArrayName = "array">
<div *ngFor = "let attr of attributes; let i = index">
<my-input [formControlName] = "i"></my-input>
<hr />
</div>
<button (click) = "save()">Save</button>
</div>
</div>
И вам нужно создать formGroup внутри основного компонента следующим образом:
export class AppComponent {
myForm = this.fb.group({
array: this.fb.array([])
})
attributes = [
{ 'attr1': 'value1', 'attr2': 'value12'},
{ 'attr1': 'value2', 'attr2': 'value22'},
{ 'attr1': 'value3', 'attr2': 'value32'},
];
constructor(private fb: FormBuilder) {}
ngOnInit() {
const arrayFormControl = this.myForm.get('array') as FormArray;
this.attributes.forEach(attr =>
arrayFormControl.push(this.fb.control(attr)));
}
save() {
console.info(this.myForm.value);
}
}
Большое спасибо! Вопрос, внутри MyInputComponent есть поле value, которое не типизировано, потому что в интерфейсе используется any. Будет ли это FormGroup в этом случае? (как вы связались с ним [formControlName] = "i")
Хорошо, было бы FormControl. Просто прочитайте arrayFormControl.push(this.fb.control(attr)));
Это не FormControl. Это любое значение, которое вы связываете. Самое приятное в этом то, что MyInputComponent не знает, как вы его используете. Вы также можете использовать ngModel вместо [formControlName] = "i".
Хорошо! Как насчет проверки одного атрибута внутри MyInputComponent? Не будет ли это проблемой?
С этой настройкой вам нужно создать пользовательскую проверку.
Или я мог бы использовать внутренний FormGroup (не через Input), похожий на stackoverflow.com/questions/52622135/…
Да, я так думаю
Вы можете вызвать общий компонент и создать группу форм со значением входного атрибута? Обновлено: проблема в том, что кнопка сохранения находится на родительском компоненте.