Странная ошибка при попытке использовать 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 снова начало происходить два раза.
В конце я хочу упомянуть, что у меня есть сильные сомнения относительно моего подхода и того, как я решил проблему, и общий компонент списка данных (содержит форму и таблицу данных), который вызывался во всех других компонентах (регионах, штаты, районы..), эти родительские компоненты будут отправлять поля формы, столбцы таблицы и данные в список данных дочернего компонента, и все будет обрабатываться там (в дочернем элементе).
Но я все равно решил поделиться тем, что я сделал и узнал, может быть, кто-то столкнется с этим и даст мне конструктивный отзыв.