#angular
#angular
Вопрос:
В настоящее время я экспериментирую с дизайном компонента приложения в 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
должно быть создано.
Однако сохранение является глобальным, а не для каждого подкомпонента.
И мне нужно позаботиться о «прослушивании» того, какие значения были изменены внутри каждого компонента.
Я думал о двух решениях.
-
Иметь a
FormGroup
на главном уровне с несколькими подразделамиFormGroup
, по одному на каждый подкомпонент. Каждый вложенный элементFormGroup
вводится внутри вложенного элементаComponent
. При сохранении я просто смотрю на основную FormGroup, она уже содержит все измененные значения. -
Каждому подкомпоненту принадлежит отдельный
FormGroup
. При сохранении главный компонент запрашивает (то есть вызывает метод) каждый подкомпонент для извлечения измененных значений.
Какое решение больше всего подходит для Angular
? Что бы вы сделали?
Редактировать: я спрашиваю, как лучше распределить JSON
входные данные по нескольким подкомпонентам, позаботившись о получении отредактированных значений при сохранении, которое находится на верхнем / основном уровне.
Комментарии:
1. Вы можете вызвать общий компонент и создать группу форм со значением входного атрибута? Редактировать: проблема в том, что кнопка сохранения находится на родительском компоненте.
2. @MsuArven что вы имеете в виду под общим компонентом? Да, «проблема» именно в этом!
3. Я думаю, что лучшим подходом является использование
formArray
Проверьте эту ссылку: angular.io/guide /…4. @BunyaminCoskuner по сути, внутри основного компонента мне пришлось бы создать,
FormGroup
который содержитFormArray
FormGroup
s? Это то, что ты хочешь сказать? Таким образом, ответом был бы вариант 1 (управление состоянием иFormGroup
только на главном уровне)5. Кроме того, вам необходимо создать реализацию вашего
Input
компонентаControlValueAccessor
, чтобы вы могли использоватьformControlName
этот компонент.
Ответ №1:
Вот рабочее решение.
Я использовал 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.log(this.myForm.value);
}
}
Комментарии:
1. Большое вам спасибо! Вопрос, внутри
MyInputComponent
естьvalue
поле, которое не типизировано, потому что интерфейс используетany
. Будет ли этоFormGroup
в этом случае? (как вы на это ссылались[formControlName]="i"
)2. Хорошо, это было бы
FormControl
. Просто прочитайтеarrayFormControl.push(this.fb.control(attr)));
3. Это не так
FormControl
. Это любое значение, которое вы привязываете. Самое приятное в этом то, чтоMyInputComponent
не знает, как вы это используете. Вы также можете использоватьngModel
вместо[formControlName]="i"
.4. Хорошо! Как насчет проверки одного атрибута внутри
MyInputComponent
? Разве это не было бы проблемой?5. Да, я так думаю
Ответ №2:
Я думаю, что лучший способ решить проблемы такого типа (совместное использование данных). мы всегда должны создавать общую службу. поместите туда один FormGroup
файл. и напишите методы в каждом компоненте для обновления значения этой группы форм. и, в конечном счете, метод для получения значения form group. На сегодняшний день это лучший метод для написания общего кода FormGroup в службах.
this.lotTwoFormGroup = this.formBuilder.group({
title: ['', Validators.compose([Validators.required])],
description: ['', Validators.compose([Validators.required])],
dutchTitle: [''],
dutchDescription: [''],
frenchTitle: [''],
frenchDescription: ['']
});