Exploring Different Techniques for Datatable Editing

Webix offers several ways for editing tabular data of various types. Most common practices to do it is via built-in Datatable editors or with the help of a standalone Webix Form. But the question remains – how to put it all together to deliver good UI and UX to users?

Techniques for Datatable Editing

Editing with inline editors

Let’s imagine we have a table with some data. To provide a better user experience we make it editable so that users can make changes to the data right in the table.

Try editing some data:

View code >>

Everything seems to work fine. However, this approach is not that efficient because every time a user saves a value in the editor the Datatable sends it to the server. To overcome this peculiarity, we can edit the entire row with the dedicated editRow method. We can define an icon in the last column that will trigger this method upon clicking on it:

{
  view:"datatable",
  editable: true,
  editaction: "custom",
  columns:[
   // … data columns
   // edit icon
   {template: `<span class='webix_icon wxi-dots'></span>`}
  ]
  onClick: {
    "wxi-dots": function(ev, id) {
         this.editRow(id);
     }
  }
}

Now you can edit the entire row:

View code >>

You probably noticed that after selecting an option from Combobox, all the editors got closed. It happens because Webix finishes all the current editings when a user selects an option from Combobox. The same goes for clicking outside a row being edited – the row gets closed. But what if we need to start and end editing strictly on clicking a particular control?

Editing with Webix Form

A simple and straightforward solution is to implement a modal window with a form inside. Right like in the demo below:

View code >>

Editing now works as expected and looks not so bad. However, we can take a step forward and deliver even better user experience by sticking this form to a row being edited.

Combining form and inline editing

We can combine form and inline editing. As far as the form is going to be inside Webix Window we can tune its appearance so that it won’t differ from the Datatable built-in editor.

That’s how it looks:

View code >>

Exactly what we need!

What to pay attention to

There are some details you should pay attention to while implementing this solution.

Grid

You should specify a template for the last grid column to switch its icon depending on the current editing status: “wxi-dots” if editing is not active and “wxi-check” if editing is active.

{
  view:"datatable",
  id:"grid",
  columns:[
  //  template for icon
   {width:55, css:"edit", template:function(obj){
     return `<span class='webix_icon wxi-${obj.edit?"check":"dots"}'></span>`;
   }}
  ],  
  // other properties
}

The edit property of a data item represents its status and gets switched by the dedicated editStart and editEnd functions described further.

Window and Form

When styling the window make sure to hide everything that makes it a window. You should disable its header and hide the borders. Make sure to put the window inside the grid container to provide its correct positioning during scroll.

{
  view:"window",
  id:"editor",
  height: 50,
  container:$$("grid").$view,
  head:false,
  borderless:true,
  // … other properties
}

The height of the form should coincide with the row height (in our case it’s 50px) and the width of the controls should be the same as the column width. Specify the form controls depending on the type of data they are used for. If some fields are not supposed for editing use Webix labels as it is shown below:

// inside window
body:{
  view:"form",
  padding:0, height:49, margin:0,
  cols:[
     //labels for non-editable fields
     {view:"label", css:"mylabel", width:40, name:"rank"},
     {view:"combo", width:100, name:"catId", options:cats},
     {view:"text", width:100, name:"votes"},
     {view:"datepicker", width:120, name:"start", editable:true}
}

Editing lifecycle

Start editing

In our case editing fires upon a button click (the “wxi-dots” icon). The starter function retrieves the values of the active row and sets them into the form. When editing is active the “wxi-dots” icon is replaced with the “wxi-check”.

function editStart(e, id){
 //…
  const vals = grid.getItem(itemId);
  form.setValues(vals);

// editing is active
  vals.edit = true;
  grid.refresh(itemId);
}

It is also important to place the window right above the row so you can make use of its show method. Note that the “x” and “y” properties are calculated with regard to the table offset to ensure correct positioning in any layout.

// table position
const offset = webix.html.offset($$("grid").$view);
const xOffset = offset.x;
const yOffset = offset.y;
// ...
function showForm(id){
  //row position
  const node = grid.getItemNode(id);
  if(node){
    const pos = webix.html.offset(node);
    editor.show({x:pos.x-xOffset, y:pos.y-yOffset-1});
  }
// other properties
}

The showForm function is also called after every scroll. It is done to stick the editor window to its row while scrolling.

{
   view:"datatable",
   on:{
     onAfterScroll:function(){
       if(itemId) showForm(itemId);
     }
   },
      // … other settings
 }

End editing

When you click on the “wxi-check” icon the form validates its values and saves them if they match the validation rules.

function editEnd(){
  if(form.validate()){
    const vals = form.getValues();
    // editing is finished
    vals.edit = false;

    if(form.isDirty())
      grid.updateItem(itemId, vals);
    else {
      grid.getItem(itemId).edit = false;
      grid.refresh(itemId);
    }
// ...
  }
}

Note that you can check whether the form has been changed with the help of the isDirty method. If it is the case you should call the updateItem method to render new values in the grid and send them to the server. If there were no changes – just change the icon back to the “wxi-dots” with the refresh method. That’s it.

Solution for horizontal scroll

If you want to implement a horizontal scroll make sure to wrap the table in scrollView with the scroll property set to “X” to position and render the editing window correctly.

{view:"scrollview", id:"scrolls", scroll:"x", body:{
  view:"datatable",
  id:"grid",
  rowHeight:50,
  autowidth:true,
// other properties
}};

You should also take into account how much the user has scrolled the table. You can retrieve the current scroll position with the help of the getScrollState method and then position the window based on that. In the example below the scroll position is available as state.x:

function showForm(id){
  const node = grid.getItemNode(id);

  const pos = webix.html.offset(node);
  const state = scrolls.getScrollState();
  editor.show({
    x:pos.x-xOffset+state.x,
    y:pos.y-yOffset
  });
}

Datatable with horizontal scroll:

View code >>

What’s next

How would you solve this task? Share your thoughts about this solution and feel free to propose your own ideas in the comments section below.