Combo with suggest and server side data loading and filter

Hi, we need a combo-suggest, with items loaded and filtered server side (.NET Core API REST).

Also we would change keyPressTimeout (on suggest) and set the combo value to first server retrieved item.

If possible, since this combo read items from a table with thousands of records, we would load only few items (like datafetch in datatable) at bootstrap and paginate other loads.

I found a lot of samples, and them all work individually, but I cannot put them together.

This basic way, all items are loaded at startup, server side filter works but generate (and stop) a lot of request due to low keyPressTimeout.

webix.ui({
            id: "country", container: "combo", view: "combo", 
            suggest: "/Tables/GetCountriesCombo",
            label: "Countries"
        });

If we use “options”, server side filter stop working

webix.ui({
            id: "country", container: "combo", view: "combo", 
            options: {
                keyPressTimeout: 500,
                body: {
                    url: "/Tables/GetCountriesCombo"
                }
            },
            label: "Countries"
        });

Then we tried some other combinations, but:

  • first item selection never work;
  • when work keyPressTimeout doesn’t work load at startup and server filtering;

This never work

ready: function () { this.select(this.data.getFirstId()); }

How can we generate the correct control ?

Hello,

It looks like you need a dataFeed for the suggest list of your combo box:

options: {
    keyPressTimeout: 1000,
    body: {
        dataFeed:url, 
        ready: function () { 
            this.select(this.data.getFirstId()); 
        }
     }
}

Then, server filtering will be enabled with afilter[value]={value} query in the url.

Check the following snippet, please: https://snippet.webix.com/drcg62qa

Hi,
I already tried this way, but with these setting :

  • when loaded first time combo is empty (and so no item is selected);
  • if I type a space (empty char) the combo loads all table records.

The server side filter is ok.

What can I add, to load “paged”? (something like [datafetch: 25] )

To load some data initially, you need to provide the url alongside with the dataFeed.

Also, you can provide dataFeed function (or proxy) that will check whether to send requests with values containing only spaces, but basically you can tune your backend to perform the same check. Please note that dataFeed will clear all the data before trying to load the filtered set.

Check, please the snippet: https://snippet.webix.com/27d5w3p0

What can I add, to load “paged”? (something like [datafetch: 25] )

Yep, since the 5.2 dynamic loading became possible for lists, just ensure that you provide the dynamic setting for the combo list and your backend is able to return the data in the required format.

{ view:"combo", options:{ body:{
  dynamic:true, url:url
}}}

If you’d like to combine dynamic loading and server filtering, you can customize the control in the following manner:

https://snippet.webix.com/ei4as4y3

Please note that the backend there can handle dynamic loading only, but you can see that filter values are correctly appended to the url.

Dynamic load (second snippet) is the right way, but there are some “bugs” :

  1. following function works only in your snippet without server-side filtering, in my sample, setting current value in the combo, change the filter parameter to selected item text and call again my REST function entering an infinite loop

ready:function(){  
            $$("combo").setValue(this.data.getFirstId())
          }

  1. now I’ve this code

webix.ui({
            id: "country", container: "combo", view: "combo", 
            label: "@Localizer.GetString("Tables.Countries", "_outerLabel")",
            width: 300,
            options: {
                keyPressTimeout: 500,
                body: {
                    dataFeed: function (text) {
                        return this.loadNext(0, 0, {}, this.config.url);
                    },
                    dynamic: true,
                    datafetch: 25,
                    url: {
                        $proxy: true,
                        load: function (view, callback, params) {
                            if (params)
                                params.filter = $$("country").getText();
                            return webix.ajax().bind(view).get('/Tables/GetCountriesComboPaged', params, callback);
                        }
                    }
                }
            }
        });

that works, but during combo list scrolling, it call twice each time my REST method as you can see here twice — ImgBB
Why ?

  1. if I scroll combo list very very quickly, it doesn’t call twice, but “jump” some calls (for example as you can see here fast — ImgBB , jump from start=59 to start=142, but requesting only 25 items, so, combo list is filled with empty items empty — ImgBB , and scrolling back to top it call againg a lot of times my REST function to load missing item)

  2. which is the best practice to “bind” this combo to an ASP.NET Core model property in my view?

Hello,

(1) I couldn’t reproduce the issue, could you please share your snippet?

(2) Indeed, while using a proxy, you need to take care of the start parameter to avoid duplicate requests with the same parameters.

Please, check the next snippet: https://snippet.webix.com/ztxd93sr.

(3) If you scroll very fast, especially when dragging a scrollbar with a mouse, the control can jump the unneeded calls - it’s an expected behaviour. Then, when you scroll up, a control requests missing data, but it needs time to perform ajax calls and fill self with records.

(4) Webix does not have any specific requirements to backend provided that it can fetch the data in the needed format and process the parameters.

Ok, thanks.
Your snippet solve the twice call problem.

This is actually my combo

webix.ui({
            id: "country", container: "cmbCountry", view: "combo", 
            options: {
                keyPressTimeout: 500,
                body: {
                    dataFeed: function (text) {
                        return this.loadNext(0, 0, {}, this.config.url);
                    },
                    dynamic: true,
                    datafetch: 25,
                    url: {
                        $proxy: true,
                        load: function (view, callback, params) {
                            params = params || {};
                            params.filter = $$("country").getText();
                            if (!this.last_start || 
                                 params.start !== this.last_start) {
                                this.last_start = params.start;
                                return webix.ajax().bind(view).get('/Tables/GetCountriesComboPaged', params, callback);
                            } else {
                                webix.ajax.$callback(view, callback, "", {}, -1);
                            }
                        }
                    }
                }
            }
        });

```


Now I added another combo with cities (identical to countries one), that is cascading and filtered by country combo selected value, so I added this code

webix.ui({
            id: "city", container: "cmbCity", view: "combo", 
            options: {
                keyPressTimeout: 500,
                body: {
                    dataFeed: function (text) {
                        console.log('feed');
                        return this.loadNext(0, 0, {}, this.config.url);
                    },
                    dynamic: true,
                    datafetch: 25,
                    url: {
                        $proxy: true,
                        load: function (view, callback, params) {
                            params = params || {};
                            params.filter = $$("city").getText();
                            params.cid = $$("country").getValue();
                            if (!this.last_start || params.start !== this.last_start) {
                                this.last_start = params.start;
                                return webix.ajax().bind(view).get('/Tables/GetCitiesComboPaged', params, callback);
                            } else {
                                webix.ajax.$callback(view, callback, "", {}, -1);
                            }
                        }
                    }
                }
            }
        });

```


I'm trying, without success, to reload cities combo after country changes

$$("country").attachEvent("onChange", function (newv, oldv) {
    document.getElementById('hidCountry').value = newv;
    $$("city").getList().clearAll();
    $$("city").getList().load(???);
});

```


I tried a lot of different parms for load but it never works: how can I call it ?

Hi,

You can reload the data just the same way you do it in the dataFeed:

$("city").loadNext(0, 0, {}, $$("city").config.url);

But provided that you use the same proxy in two components already, it makes sence to initialize it separately to avoid code repetition and hence, call the load() method normally.

webix.proxy.combo = { };
...
//url
{ view:"list", dynamic:true, url:"combo->"+url };
//load
list.load( url:"combo->"+url);

Here’s the snippet: https://snippet.webix.com/n016ezn5

Great, thanks a lot.