Странная ошибка при попытке использовать Angular и DevExtreme

Я начинаю новую работу и опыт, где мне нужно узнать об Angular и освоить его.

Я потратил около недели, пытаясь создать несколько небольших приложений, перемещаясь по документации и делая заметки о важных вещах в Angular, затем я начал работать над простым приложением, чтобы больше узнать о маршрутизации, формах, хуках жизненного цикла и многом другом, я также хотел попробовать библиотеку DevExtreme, которая представляет собой богатую и отзывчивую библиотеку пользовательского интерфейса с открытым исходным кодом, она также работает с другими фреймворками, такими как Vuejs, Reactjs, jQuery и многими другими…

Позвольте мне сначала объяснить, что делает мое приложение. Таким образом, просто добавляется возможность удалять, редактировать, добавлять и перечислять страны, регионы, районы и штаты.. На снимке экрана ниже показано, как работает это простое приложение.

Во время работы над этим приложением я столкнулся с очень странной ошибкой в ​​компоненте выбора devExteme «SelectBox», где событие onValueChange было запущено дважды, когда я впервые щелкнул и выбрал элемент из списка селектора, из-за чего селектор не отображал никакого значения до тех пор, пока Я щелкнул и снова выбрал элемент. На приведенном ниже снимке экрана показаны более подробные сведения об этой проблеме.

В приведенном ниже блоке кода подробно показаны два события «onValueChange» из 3-го шага.

// the first one //
{ 
component: inheritor {callBase: undefined, _buttonCollection: t, …},
element: div.dx-show-invalid-badge.dx-selectbox.
event: eventsEngine.Event {originalEvent: e…e.Event, type: 'dxclick', currentTarget: Window, isTrusted: true, timeStamp: 3393.9000000059605, …},
previousValue: null,
value: {code: 'DZD', description: 'azeaze', isDefault: true, name: 'Algeria', id: 0},
[[Prototype]]: Object
}
/////////////////////////
// the second one
{
component: inheritor {callBase: undefined, _buttonCollection: TextEditorButtonCollection, …},
element: div.dx-show-invalid-badge.dx-selectbox...,
event: undefined,
previousValue: {code: 'DZD', description: 'azeaze', isDefault: true, name: 'Algeria', id: 0},
value: {code: 'DZD', description: 'azeaze', isDefault: true, name: 'Algeria', id: 0},
[[Prototype]]: Object
}

Во втором случае события, как вы можете видеть, ключ события равен нулю, а previousValue и value совпадают.

Прежде чем давать более подробную информацию о проблеме, давайте сначала покажем, как выглядит код, вызвавший проблему, давайте сначала покажем, как выглядит шаблон региона.

<app-data-list
 [title]="title"
 [cols]="cols"
 [data]="regions"
 [formFields]="formFields"
 (add)="addRegion($event)"
 (delete)="deleteRegion($event)"
 (update)="updateRegion($event)"
>
</app-data-list>

Это просто компонент списка данных с двумя выходами и 4 входами, а ниже приведен компонент региона, написанный на машинописном языке.

@Component({
selector: 'app-regions',
templateUrl: './regions.component.html',
styleUrls: ['./regions.component.scss'],
providers: [RegionsService, CountriesService]
})
export class RegionsComponent implements OnInit {
 regions: DbRegion[];
 countries: DbCountry[];
 title: Title = {single: 'region', plural: "Regions"}
 cols: Field[] = [
  {caption: "Code", field: "code", type: "string"},
  {caption: "Name", field: "name", type: "string"},
  {caption: "Description", field: "description", type: "string"},
 ]
formFields: formField[] = [
  {dataField: "code", isRequired: true, editorType: "dxTextBox"},
  {dataField: "name", isRequired: true, editorType: "dxTextBox"]}, 
 {dataField: "description", isRequired: true, editorType: "dxTextBox"},
]
config: Config
constructor(private service: RegionsService,
private countriesService: CountriesService,
private configService: ConfigService,
private dataService: DataService) {}
ngOnInit(): void {
 this.configService.getConfig().subscribe(val =>this.config = val)   
 this.service.getRegions(val).subscribe( regions => this.regions = regions)
 this.countriesService.getCountries(val).subscribe( countries => {
  // after fetching the required data
 // add the selectors field with items 
 })
})}
 addRegion(event: any) { // http req to add a region }
 updateRegion(event: any) { // http req to update a region }
 deleteRegion(event: any) { // http req to delete region }
}

в следующем блоке кода я покажу, как я добавил недостающие селекторы, так как я не могу добавить их без данных, мне нужно вызвать this.countriesService.getCountries, чтобы получить страны, а затем добавить эти страны как элементы в списке выбора.

this.countriesService.getCountries(val).subscribe( countries => {
  // change the position of button and countries selector, keep button the last item in the form
// first save the button in temoFormField  
let tempFormfield = this.formFields[this.formFields.length-1]
// remove the button from the form
 this.formFields = this.formFields.filter((field) => field.dataField !== undefined)
// add the selector
  this.formFields.push({
    dataField: "country", 
    isRequired: true,   
    editorType: "dxSelectBox", 
    editorOptions: {
     items: countries,
     displayExpr: 'name',
     onValueChanged: (event:any) => console.log(event)
   }
  })
// add the button back 
this.formFields.push(tempFormfield)
})

кто-то может спросить, почему я делаю это так, потому что в списке приложений у меня есть другая функция ngOnInit, и я добавляю кнопку отправки в массив полей формы в ngOnInit последнего упомянутого компонента, поэтому ngOnInit дочернего компонента (списка данных) вызывается перед ngOnInit родительского компонента, поэтому форма может выглядеть как на картинке ниже:

и вот как выглядит ngOnInit компонента dataList:

ngOnInit(): void {
 this.formFields.push({ editorType: “dxButton”, itemType: “button”,   buttonOptions: this.addDataButtonOptions})
}

И, потратив некоторое время на тестирование, отладку и чтение документации DevExtreme, я понял, что проблема возникла из-за того, что я отправлял массив стран в ключ items в объекте editorOptions:

// add the selector
  this.formFields.push({
    dataField: "country", 
    isRequired: true,   
    editorType: "dxSelectBox", 
    editorOptions: {
     items: countries, /// here 
     displayExpr: 'name',
     onValueChanged: (event:any) => console.log(event)
   }
  })

Согласно демонстрациям в официальной документации devExtreme, атрибут items принимает только массив строк, и именно поэтому событие onValueChange срабатывало два раза, что, как мне кажется, неясно в официальной документации.

И я нашел другой способ добавить данные в селектор с помощью атрибута dataSource, который принимает массив объектов.

dataSource: new ArrayStore({
 data: countries,
 key: 'name'
}),

я заменил элементы на dataSource и добавил еще один атрибут valueExpr

// add the selector
  this.formFields.push({
    dataField: "country", 
    isRequired: true,   
    editorType: "dxSelectBox", 
    editorOptions: {
     dataSource: new ArrayStore({
        data: countries,
        key: 'name'
     }),
     displayExpr: 'name',
     valueExpr: 'name',
     onValueChanged: (event:any) => console.log(event)
   }
  })

это решило проблему, и элемент селектора выбирается, как и ожидалось, а событие valueOnChange больше не вызывается два раза.

проблема успешно решена, но я до сих пор не понимаю, почему событие запустилось два раза ?????????

Примечание. Я протестировал это решение, удалив атрибут ключа в ArrayStore или удалив значениеExpr, и событие valueOnChange снова начало происходить два раза.

В конце я хочу упомянуть, что у меня есть сильные сомнения относительно моего подхода и того, как я решил проблему, и общий компонент списка данных (содержит форму и таблицу данных), который вызывался во всех других компонентах (регионах, штаты, районы..), эти родительские компоненты будут отправлять поля формы, столбцы таблицы и данные в список данных дочернего компонента, и все будет обрабатываться там (в дочернем элементе).

Но я все равно решил поделиться тем, что я сделал и узнал, может быть, кто-то столкнется с этим и даст мне конструктивный отзыв.