DataCollection.add always returns the client id

Hello,

When we bind a DataCollection to a server-side url, the method “add” returns a random client id instead of the new record id sent in the ajax response.

For example:
Ajax response => {“newid”:“5a2f141ae906fb0bd40be1a3”}
But ‘add’ method returns: “1234548848484”

I know that each back-end has its own conventions (newid, id, _id, etc…), but is there a way to catch the ajax response using the DataCollection.add method?

Or do we have to use a standard ajax request?

My goal was the following:

  • Sync multiple datatables with a DataCollection called “MyData”
  • When I add a record with a form, I’m using MyData.add
  • When I’m updating the same record with the form, I’m using MyData.updateItem

This way, all the datatables synced with MyData are updating automatically.
It works FINE for the add method, but as I can’t get the right id for the new record, I can’t update it properly.

add is a client-side operation, which result (a new item) will be sent to the server.
On this step, item gets a client-side ID based on the timestamp.
After the proper server response received (check the docs below for the required format), the item will get the id (newid) returned from the server.

https://docs.webix.com/desktop__custom_serverside.html#response

onAfterAdd event is not related to the server response, but you can use onAfterinsert event of DataProcessor (available if you have save property in collection config):

webix.dp("collectionId").attachEvent("onAfterInsert", function(response, old_id, details){ })

Mmmh no, I didn’t use the “save” property in my datatable: I’ve used the view.sync(data) in the init() method of the Jetview (data is my DataCollection).

If I use the “save” property of the datatable, then it doesn’t use the DataCollection and directly requests the server through the provided url.

(=> and of course, if the DataCollection is not modified, then all my datatables are not synced!!!)

Anyway: I will give a try to your method tonight, because I feel there is some kind of possible workaround here :slight_smile: Thanks!

Note: in my case, the DataCollection can be modified:

  • by all the datatables synced with it
  • by a form (used to edit one record at a time)

I would like all the datatables to use the same datasource to keep synced. The main problem is that I’m using a FORM to update the DataCollection…

My mistake, sorry. There should be a dataCollection instead of the datatable, i.e. a component that actually handles all your data.

Okayyyyyyyyyyyyyyyyyy! :blush:

Well, hello again…

I have spent some time in the documentation, forums, and samples, and I have to say there is REALLY something missing in your docs, which is a very common use case:

Having 1 form to feed/update a DataCollection
+
Having multiple datatables/dataviews synced with this DataCollection

In your docs, you insist on the scenario where you BIND a form with a datatable/dataview, but there are many cases where you can’t (and don’t need to) do that.

Below are very common scenarios that seems to be uncovered in the docs (and would probably save you some support time).

And here is why binding is not always what we need to connect data to our forms:

Example 1:

You have a form with 20 fields, but a datatable with only 5 of those fields in the columns config: you certainly *don't* want to load 20 columns in your datatable just to be able to "feed" your form when you bind it.
Example 2:

You have N datatables with N columns synced with the same DataCollection, but with different columns config.

When you dblclick a row of any datatable (to edit the record), you often want to display a FORM (that often contains more fields than the datatable do).

How will you reflect the changes made in the FORM to the DataCollection?
                     |                 | <--- sync ---> DataTable 1
FORM  <--- crud ---> | DataCollection* | <--- sync ---> DataTable 2
                     |                 | <--- sync ---> DataTable 3

* or any object that can handle the data locally and request the server according to crud operations

Well, that’s my 2 cents. I feel that if you could add a straightforward example of how we can achieve this common use case, we would just spent less time in the documentation to understand how to connect a DataTable to connect to a DataCollection to connect to a DataProcessor to connect to a Form (or something like that in your prefered order) :wink:

By the way, when I’m doing this, I’m getting an error:

(...)

// This DataCollection works fine with my datatables
let newCollection = new webix.DataCollection({
    url: config.db_path + "/something",
    save: "rest->" + config.db_path + "/something"
})

// But if I add this block below, following the documentation, then I get an error:
let dp = new webix.DataProcessor({
    master: newCollection,
    url: config.db_path + "/something"
})

The error is:

Uncaught TypeError: Cannot set property 'id' of undefined
    at webix.DataStore.changeId (webix_debug.js:11655)
    at h.Jr (webix_debug.js:34546)
    at h.processResult (webix_debug.js:34572)
    at h.xr (webix_debug.js:34592)
    at webix.ajax.success (webix_debug.js:34484)
    at Function.webix.ajax.$callback (webix_debug.js:3097)
    at XMLHttpRequest.G.a.onreadystatechange (webix_debug.js:2977)

It’s thrown by the importData:function(target, silent){…} of the webix.DataStore.prototype

It happens at the moment I’m calling:
newCollection.add(formValues)

About data binding/syncing, please check the related docs chapter and the following snippet: https://webix.com/snippet/241ad885

In the above example, the datatable is synced to a collection, and the form is bound to it. DataCollection has get/setCursor methods for data binding (it can be considered as ‘virtual selection’), so click on the “edit” icon will apply this method to a collection in order to trigger data binding in form.

As for the DataProcessor, please note that save property will create an instance of it automatically. The initialized DataProсessor is available as

webix.dp("masterId")

But only one instance of this module can be initialized at a time for one data component. Therefore, your code can be corrected as

let newCollection = new webix.DataCollection({
  url: "/url_to_load_data"
});

let dp = new webix.DataProcessor({
  master: newCollection,
  url: "/url_to_save_data"
});

Also, the needed configuration can be defined directly in save property, as in the above snippet.

Thank you, the snippet is very useful because if features a complete and simple example. Great!

Hi, I don’t understand the difference between a DataCollection and a DataProcessor. According to this: https://docs.webix.com/desktop__nonui_objects.html#serversideintegrationwithnonuicomponents

If I define a DataCollection, I don’t need a DataProcessor, right?

But I’m not getting the server side save operation to fire from the DataCollection when the .add function is called.
I’m using this example: https://docs.webix.com/samples/40_serverside/01_php/08_datastore.html

My code is as follows:

var store = new webix.DataCollection({
     url:"server/datatable.php",
     save:"server/datatable_save.php"
});

var grida = webix.ui({
    container:"grida",
    id: "grid1",
    view:"datatable",
    columns:[
        { id:"rank", header:"", css:"rank", width:50},
        { id:"title", header:"Film title", width:200},
        { id:"year", header:"Released", width:80},
        { id:"votes", header:"Votes", width:100}
    ],
    autoheight:true,
    autowidth:true,
    select:"row"
});
grida.sync(store);

var forma = webix.ui({
    container:"forma",
    view:"form",
    id:"form1",
    width:350,
    scroll:false,
    elements:[
        { view:"text", name:"title", label:"Title" },
        { view:"textarea", name:"year", label:"Year" },
        { view:"textarea", name:"votes", label:"Votes" },

        { view:"button", value:"Cancel",
            click:function() {
                var grid = $$("grid1");
                var id = grid.getSelectedId();
                if (id) {
                    var values = grid.getItem(id);
                    var form = $$("form1");
                    form.setValues(values);

                }
            }
        },
        { view:"button", value:"Save",
            click:function() {
                var form = $$("form1");
                var data = form.getValues();
                var grid = $$("grid1");
                console.log("data: " + JSON.stringify(data));
                if (data.id) {
                    console.log("update called.");
                    grid.updateItem(data.id, data);
                } else {
                    console.log("add called.");
                    grid.add(data);
                }
                form.clear();                        
            }
        }
    ]
});
forma.bind(grida);

If you watch the network traffic, when you add an item, it never calls the save function. It does for the update, but not for add. Any idea why?
Thanks, Dan