Angular — архитектура компонентов приложения

#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 должно быть создано.

введите описание изображения здесь

Однако сохранение является глобальным, а не для каждого подкомпонента.
И мне нужно позаботиться о «прослушивании» того, какие значения были изменены внутри каждого компонента.

Я думал о двух решениях.

  1. Иметь a FormGroup на главном уровне с несколькими подразделами FormGroup , по одному на каждый подкомпонент. Каждый вложенный элемент FormGroup вводится внутри вложенного элемента Component . При сохранении я просто смотрю на основную FormGroup, она уже содержит все измененные значения.

  2. Каждому подкомпоненту принадлежит отдельный 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: ['']
});