DEV Community

Cover image for A weird error while trying Angular and DevExtreme
Ez Pz Developement
Ez Pz Developement

Posted on

A weird error while trying Angular and DevExtreme

I'm starting a new job and experience where I need to learn about Angular and get good at it.

I spent about a week trying to create some small apps, navigating the documentation, and taking notes about important things in Angular, then i started working on a simple app to understand more about Routing, Forms, lifecycle hooks, and much more, I also wanted to try the DevExtreme Library, which is an open source rich and responsive UI library, it also works with other frameworks like Vuejs, Reactjs, jQuery and more and more…

Let me first explain what my app does. So simply adds the ability to delete edit, add, and list countries, regions, districts, and states .., a screenshot below shows how this simple app works.

Image description

While working on this app I faced a very weird error from the devExteme select component “SelectBox”, where the onValueChange event was fired twice when I first clicked and chose an item from the selector list, which caused the selector to not display any value until I clicked and selected the item again. The screenshot below shows more details about this problem.

Image description

The code block below shows the two “onValueChange “ events in detail from the 3rd step.

// 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
}
Enter fullscreen mode Exit fullscreen mode

In the second occurrence of the event and as you can see the event key is null, and the previousValue and value are the same.

Before giving more details of the problem, let’s first show what the code that caused the problem looks like, let's first show what the region's template looks like.

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

It is just a simple data-list component with two outputs and 4 inputs, and below is the region component written in typescript.

@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 }
}
Enter fullscreen mode Exit fullscreen mode

in the next code block, I will show how I added the missing selectors, as I can not add them with no data I need to call this.countriesService.getCountries in order to fetch countries and then add these countries as items to the select list.

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)
})
Enter fullscreen mode Exit fullscreen mode

someone may ask why I'm doing it like this , it is because in the app-data-list I have another ngOnInit function and I'm adding a submit button to the form fields array in the ngOnInit of the last mentioned component, so the ngOnInit of the child component (data-list) is called before the ngOnInit of the parent component, and because of that the form might look like the picture below:

Image description

and here is what the ngOnInit of the dataList component looks like:


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

Enter fullscreen mode Exit fullscreen mode

And after spending some time testing and debuging and reading the DevExtreme documentation i realized that the problem occurred because i was sending an array of countries to the items key in editorOptions object :

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

*According to the demos in devExtreme official documentation the items attribute only accepts strings array, and this is what was causing the onValueChange event to fire two times, which i think is not clear in the official documentation.
*

And i found another way to add data to the selector by using the dataSource attribute which accepts an array of objects.

dataSource: new ArrayStore({
 data: countries,
 key: 'name'
}),
Enter fullscreen mode Exit fullscreen mode

I replaced items with dataSource and added another attribute 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)
   }
  })
Enter fullscreen mode Exit fullscreen mode

this solved the problem and the selectors item is selected as expected, and the valueOnChange event is not called two times anymore.

problem is solved successfully but i still do not understand why the event was fired two times ?????????

Note: i tested this solution by removing the key attribute in ArrayStore or by removing the valueExpr and the valueOnChange Event started occurring two times again.

In the end, i want to mention that i have strong doubts about my appreaoch and how i solved the problem, and the generic data-list component (contains the form and the data table )which was called in all the other compoents (regions, states, districts..), these parent components will send form fields, table cols , and data to the child component data-list and everything will be handled there( in the child).

But i decided to share what i did and learned anyway, maybe someone will come across this and give me some constructive feedback.

Top comments (0)