paint-brush
Webix Datatable. From a simple table to a complex solutionby@oldschooldeveloper
3,909 reads
3,909 reads

Webix Datatable. From a simple table to a complex solution

by Serhii PylypchukJune 19th, 2021
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

The DataTable widget is one of the most functional components of the Webix JavaScript UI library. It allows you to display the data in the form of a table and customize it to your needs. This powerful and easy-to-use tool with a stylish design supports various data formats (XML, CSV, JSArray, HTML tables) It is also quite fast in large datasets processing. The widget also has a wide arsenal of APIs for filtering, sorting, paging, copying, resizing and many other useful features.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - Webix Datatable. From a simple table to a complex solution
Serhii Pylypchuk HackerNoon profile picture

This article is intended for those who are used to solving complex problems with simple methods. At first glance, working with big data may seem like a daunting challenge. With the right tools organizing and displaying large datasets will seem like little more than fun entertainment to you. Today I want to talk about one of the most extraordinary tools for working with data. It is the DataTable widget – a simple and at the same time powerful solution of the Webix UI library. Let's figure out what its strength is.

Webix library and DataTable widget 

Webix JavaScript UI is a modern and powerful JavaScript library that allows you to create a user-friendly interface based on its own ui components. The range is varying from a simple button to a ready-to-use complex solution. In addition to the components, the library provides many extra tools for working with them. For example, event handling, methods of working with data, interaction with the server, styling themes and many more. You can find out more information about capabilities in the documentation.  

The DataTable widget is one of the most functional components of the Webix library. It allows you to display the data in the form of a table and customize it to your needs. This powerful and easy-to-use tool with a stylish design supports various data formats (XML, JSON, CSV, JSArray, HTML tables). It is also quite fast in large datasets processing. Among other things, the widget also has a wide arsenal of APIs for filtering, sorting, paging, copying, resizing, styling and many other useful features. All these advantages make DataTable an indispensable assistant when it comes to work with huge volumes of data.

I can make a long speech about the convenience of working with Webix tables and their extensive capabilities. But it would be better to figure everything out in practice. For clarity, I created a small application that displays all the benefits of working with this powerful tool.  

You can find the full source code and live demo here.

Main preparations

In order to use the capabilities of the Webix library in your application, you need to include it in the main index.html file. It is worth mentioning here that there are 2 versions of the library: the basic and the Pro version. The basic version is free and provides a limited set of features in comparison with the Pro version. In my demo application, I use the extended Pro license to get the most out of the DataTable widget. The required files are available via the CDN at the following links:

<script type="text/javascript" src="//cdn.webix.com/pro/edge/webix.js"></script>
<link rel="stylesheet" type="text/css" href="//cdn.webix.com/pro/edge/webix.css">

I just need to include them in my index.html file. Now it looks like this:

<!DOCTYPE html>
<html>
  <head>
    <title>Webix Booking</title>
    <meta charset="utf-8">
    <!--Webix sources -->
    <script type="text/javascript" src="//cdn.webix.com/pro/edge/webix.js"></script>
    <link rel="stylesheet" type="text/css" href="//cdn.webix.com/pro/edge/webix.css">	
  </head>
  <body>
    <script type="text/javascript">
      //...
    </script>
  </body>
</html>

Inside the code, I include the <script>...</script>  tags, where I will build the application.

Initialization

All Webix magic happens inside the webix.ui() constructor. I need to make sure that the code starts executing after the HTML page is fully loaded. To do this, I wrap it in webix.ready (function(){}). It looks like this:

webix.ready(function(){
  webix.ui({
    /*the application code*/
  });
});

I have created a basic index.html file and included the necessary tools. Now it is time to set up the DataTable component itself.

The power of simplicity

If you are a minimalist and not eager to understand all the intricacies of setting up a widget, then the laziest configuration is provided for you. This configuration allows you to display the loaded data. In the datatable.js file you need to create a widget constructor with the following three lines of code:

const datatable = {
 view:"datatable",
 autoConfig:true,
 url:"./data/data.json"
}

The DataTable component is declared using the view:"datatable" expression. Via the url property You can set the path at which the widget loads data. It is worth clarifying that by default the widget expects to get data in JSON format. If the data comes in a different format (xml, jsarray or csv), you need to specify it through the datatype property. In the case when the data is stored on the client-side in the form of an array, it can be passed to the component via the data property or the parse() method.

Particular attention should be paid to the autoConfig property. It is designed for the laziest programmers. With its help, the data is automatically distributed among the corresponding rows and columns of the table. It's more convenient just to do nothing at all.

Now you can render the component in the browser and see the result you get. For convenience, you can save the widget constructor into the datatable variable and use it for assembly in the index.html file.

Using the following code, you can include the file with the DataTable component into the index.html file:

<!--App sources -->
<script src="js/datatable.js" type="text/javascript" charset="utf-8"></script>

In the application constructor, you need to specify a variable that stores the widget settings:

<script type="text/javascript">
 webix.ready(function(){
  webix.ui( datatable );
 });
</script>

In the browser, you can see the following result:

So now you know how to display a table with the corresponding data using just 3 lines of code. In this situation, the structure of the table depends on the structure of the loaded data directly. In fact, autoconfiguration is little used in real projects. It is much more practical to customize the table columns manually. In the next section, I will show you how to do this.

Subtleties of Table Settings

If autoconfiguration seems too trivial to you, there is an ability to set individual settings for each column separately. This can be done in the array of the columns property. For each column, you need to specify an object with the corresponding settings. Bear in mind that the order in which the columns are displayed in the table depends on the order of the objects with settings in the array.

First of all, you need to specify the unique id for each column object. It must match the key under which the data is stored in the database. After this you can specify the “header” of the column using the header property. 

Now you can set the width of each column. For this, there are properties such as width, minWidth, maxWidth and fillspace. To make the DataTable fill the entire width, you should define the fillspace property to true for at least one of its columns. The column with this property fills the whole remaining space. Now the column settings look like this:

{
  view:"datatable",
  columns:[
    { id:"rank", header:"Rank", width:45 },
    //...
    { id:"vin_code", header:"VIN", minWidth:50, width:180, maxWidth:300 },
    //...
    { id:"address", header:"Address", minWidth:200, fillspace:true },
    //...
  ],
  url:"./data/data.json"
}


In the browser, you can see the following result:

As you can see, the columns are arranged in the desired order with the specified width. The column names correspond to those specified in the settings as the values for the header property.

Besides the individual width settings for each column, you can specify the global width for all columns. For this, there are the autowidth and columnWidth properties. The first one adjusts the width of Datatable to its content. The second one defines the fixed width for all columns. By default, the column width is 100px.

To complete the picture, let's give users the ability to resize the columns. To do this, I set the resizeColumn property to true. I also set the borders for rows, columns and their headers using the css property and corresponding classes: webix_data_border and webix_header_border.

Since the table has many columns, it would be a good idea to provide horizontal and vertical scrolling. This can be done using the scroll property. By default, it is set to false, but I need to set it to “xy”.

So now I can resize the columns by dragging their right border in the desired direction, as well as scroll the table horizontally and vertically.

Working with Templates

By default, table cells are filled with data, the keys of which are specified as id in the column objects. But the widget allows me to control their look using the template property. So I can set the required template according to which the data will be displayed in the cell. The value can be specified either as a string or as a function. To use incoming data in a string template, their keys must be specified as #data_key#. There are several columns for which I need to set templates.  

I want to start with the first column. It will include a star icon. You may have a reasonable question, why do I need such a column with stars? The answer is easy. I will make it so that the potential user can mark the options he likes by clicking on the star. Let's implement it using a template function. The code looks like this:

{
  view:"datatable",
  id:"car_rental_table",
  //...
  columns:[
    { id:"stared", header:"",
     template:function(obj){
       return `<span class='webix_icon star mdi mdi-"+(obj.star ? "star" : "star-outline") + "'></span>`;
     }, ...,
    }, 
    //...
  ]
}

I set the template-function that returns a span element with certain classes to the template property. This function will change the corresponding "star" and "star-outline" classes when the user clicks on the star icon. The code of the function looks like this:

function selectStar(id){
  const table = $$("car_rental_table");
  const item = table.getItem(id);
  const star = item.star?0:1;
  item.star = star;
}

The handler takes the id of the selected row as an argument. I get the widget instance by its id via the $$("car_rental_table") method. Using the table getItem() method, which takes the item id as a parameter, I get the data object of the selected row. Then I check for the existence of the star key and set it to 0 (if it exists) or 1 (if it does not ). 

To make the switching work, I need to call this function upon clicking on the star icon. To set a handler on any table element with a certain css class, the table has a special onClick property:

//...
url:"./data/data.json",
onClick:{
  "star":(e,id) => selectStar(id)
},
//...

Теперь мы можем отмечать нужные нам ряды при клике по звездочке в первом столбце таблицы. В браузере это выглядит следующим образом:

The table includes the "Available" column. Its cells store the true and false values, which indicate the availability of a car at the current time. I need to define a template that changes the cell values ​​to the corresponding “Yes” and “No” text.

To do this, I create a special function that changes the needed values. It looks like this:

function customCheckbox(obj, common, value){
  if(value){
    return "<span class='webix_table_checkbox checked'> YES </span>";
  }else{
    return "<span class='webix_table_checkbox notchecked'> NO </span>";
  }
}

Note, you don't need to call it. Just pass its name as a value to the template property of the “Available” column.

columns:[
  //...
  { id:"active", header:"Available", template:customCheckbox, ...,},
]

In the browser, you can see the following result:

It looks pretty good already. Since the first three columns contain the data that should always be in sight, let's fix it on the left side of the table. To do this, I need to add the leftSplit property to the widget constructor and specify the number of desired columns (in our case, 3). So now the user can see these columns on top of other elements when scrolling horizontally.

Now I want to configure the “Color” column, which displays the  HEX codes of the car color. For the average user, such information ​​ means nothing at all. So I need to add some visual representation of the color for each cell. Guess what I'm going to use? Right! The template property. The code looks like this:

columns:[
  //...
  { id:"color", header:"Color", template:`<span style="background-color:#color#; 
border-radius:4px; padding-right:10px;">&nbsp</span> #color#`},
  //...
]

In this code I use a string template in which I set the background of a non-breaking space (&nbsp) with a HEX color value. In the browser, you can see the following result:

Working with Collections 

In some cases, the data come in and are displayed as numbers that indicate the ids of certain elements in other data collections. But it doesn't quite suit me. In such cases, the widget can replace the numbers with the corresponding names from other collections.

For example, let's look at a column that should display car brands. The data for its cells are stored as numbers from 1 to 24.

//data.json
[
  { "id":1, "rank":1, "car_make":22, ..., "country":1, "company":1, ..., },
  { "id":2, "rank":2, "car_make":10, ..., "country":2, "company":3, ..., },
  { "id":3, "rank":3, "car_make":16, ..., "country":1, "company":2, ..., },
  //...
]

To replace the numbers with the corresponding car brands, I need to store an object with these brands:

//car_make.json
[
  { "id":22, "value":"Toyota" }, ...,
  { "id":10, "value":"GMC" }, ...,
  { "id":16, "value":"Mazda" }, ...,
  //...
]

Now I need to include the collection property into the column settings and set to it the path to the required object:

columns:[
  //...
  { id:"car_make", header:"Car make", collection:"./data/car_make.json", ...,},
  //...
]

Thus, the widget displays car brands instead of numeric values in the cells of the “Car make” column. In the same way, I replace the values ​​for the “Company”, “Country” and “Card” columns. 

In the browser, you can see the following result:

Working with Data Formats

When working with data, sometimes there is a need to change the format of their display. For this case, the table has a special format property. With its help I can set the desired display format for numbers and dates. There are special methods for doing this. I need to set the display format for columns with dates and order prices: 

columns:[
  //...
  { id:"date", header:"Date", format:webix.i18n.longDateFormatStr, ..., },
  { id:"price", header:"Price", format:webix.i18n.priceFormat, ..., },
  //...
]

The dates are loaded in the form of strings like “05/26/2021”. But I need to display them like “26 May 2021”. There is the special webix.i18n.longDateFormatStr method, which I have used in the “Date” column settings. It has to take the Date object and return a string in the required format. But now it only takes a string like “05/26/2021”, so the result may be unexpected. I need to change the incoming dates and convert them to the corresponding Date objects

For this, the table has the special scheme property. In the object of this property, I change the date string to the corresponding object using the webix.i18n.dateFormatDate method. The code looks like this: 

{
  view:"datatable",
  //...
  scheme:{
    $init:function(obj){
      obj.date = webix.i18n.dateFormatDate(obj.date)
    }
  },
  columns:[...]
}

I think the date formatting is clear for you. Now I want to show you how to change the price format. But here everything is even easier. The method webix.i18n.priceFormat takes a number (for example 199) and returns a string with a dollar sign at the beginning: “$199”. That's the whole trick. 

In the browser, you can see the following result:

In this article you can read more about the data formatting capabilities of the Webix library.

Sorting data

Sorting is one of the most frequently used operations when it comes to working with data. If the conditions were typical, I would need to create handlers, catch events and do a lot of tricky manipulations.  

The DataTable widget solves this problem with the sort property. All I need to do is just include it in the settings of the required column and set the correct mode. Since I am going to sort dates, numbers and strings, I need the following sorting types:

  • "int" - compares numeric values
  • "date" - compares dates
  • "string" - compares string values ​​as they are loaded
  • "text" - compares the text of the element that is displayed in the cell (including the template)
columns:[
  { id:"car_model", header:"Model", width:120, ..., sort:"string", }, ...,
  { id:"car_year", header:"Year", width:85, ..., sort:"int" }, ...,
  { id:"country", header:"Country", width:140, ..., sort:"text" }, ...,
  { id:"date", header:"Date", width:150, ..., sort:"date" }, ...,
]

Now the column data will be sorted when clicking on the header of this column. Moreover, I can set a mode that allows users to sort data by several criteria at once. To do this, I need to set the sort property of the constructor to "multi". To sort data by several conditions, the user needs to press the Ctrl/Command key and click on the headers of several columns. 

If you want to apply a custom sorting behavior, you can define the desired logic in a separate function and set it for the sort property.

If you need to sort the data when clicking on any external button, you can use the table sort() method. In this article you can read more on what sorting in the Webix library is capable of.

Filtering data

It is hard to imagine a full-fledged table without data filtering. And the Webix DataTable provides such an ability. You can add one of several built-in filters or set your own filtering conditions using the content property. It allows you to filter data on a client or server-side by one or more criteria.

Depending on the data type in the columns, I need to apply different types of filters to them. I can add a selectFilter with a drop-down list of options to the header of the “Company” column. For this column, I previously specified the collection property. It replaces the numeric values ​​with the corresponding company names. The filter pulls up the data of this collection and forms a list of options. In the column settings, it looks like this:

columns:[
  //...
  {
    id:"company", 
    header:["Company",{content:"selectFilter"}], 
    collection:"./data/company.json", ...,
  }, ...,
]

In the browser, you can see the following result:

For the “Car make” column I need to add a textFilter filter, which looks like a plain input field. This filter compares the entered values ​​with the data in the column. I want to remind you that the data comes here in the form of numbers and are converted into the corresponding names of car models. The fact is that the filter compares the entered values ​​with incoming numbers, but it does not quite suit me. So I need to change the default filter behavior and make sure that the entered values ​​are compared with the data from the collection. For this, I need to apply a special function to the filter:

columns:[
  //...
  { id:"car_make", header:["Car make", {
    content:"textFilter", placeholder:"Type car make",
    compare:function(item, value, data){ 
      const colValue = cars_make_data.getItem(item).value;
      const toFilter = colValue.toLowerCase();
      value = value.toString().toLowerCase();
      return toFilter.indexOf(value) !== -1;
    } }], collection:cars_make_data, ...,
  },
  //...
]

I apply the same filter to the “Model” column. Its data comes in the form of strings, so I don't need to set any extra conditions:

columns:[
  //...
  { id:"car_model", header:["Model", {content:"textFilter", placeholder:"Type model"}, ...,],
  //...
]

In the browser, you can see the following result:

Now let's move on to the “Year” column. Its cells display the year of the car. Here I apply an excelFilter filter. Its peculiarity lies in the fact that I can enter data in the input field and set some extra conditions. I can also exclude unwanted elements from filtering via a special checkbox. The column settings look like this:

columns:[
  //...
  { id:"car_year", header:[{text:"Year", content:"excelFilter", mode:"number"}], ...,},
  //...
]

The mode:“number” expression allows comparing numbers only. Instead of an input field or selector, there is a special icon next to the header. When clicking on it, the widget displays a pop-up window for setting conditions. In the browser, you can see the following result:

Among the columns there is one that displays the available car rental dates. For working with dates, the widget has a special datepickerFilter filter. It allows users to select the desired date via the compact pop-up calendar. The column settings look like this:

columns:[
  //...
  { id:"date", header:["Date", {content:"datepickerFilter"}], ..., },
  //...
]

In the browser, you can see the following result: 

The examples above show you how to apply the filters the user needs to search for data in columns. In this article you can read more on data sorting of the Webix library is capable of.

Editing data 

The widget functionality allows users to edit data directly in the cells of the table. To enable this option, I have to set the editable property to true in the table constructor. I can also define the action that opens the editor in the cell. By default, the editor opens when clicking on a cell. But I can also define the opening by double click (“dblclick”) or specify my own action (“custom”). 

When the editing mode is activated, I need to set the required editor type in the column settings. Depending on the data type, each column will have an appropriate editor.

I want to start with the most commonly used text editor. It turns the cell into an input field where the user can enter the data he needs. Let's add it to a few columns in the table. It is pretty simple: I just need to specify the corresponding editor type to the editor property in the column settings. It looks like this:

{
  view:"datatable",
  //...
  editable:true,
  editaction:"dblclick",
  columns:[
    { id:"rank", header:"Rank", editor:"text", ..., },
    { id:"car_model", header:"Model", editor:"text", ..., },
    { id:"manager", header:"Manager", editor:"text", ..., },
    //...
  ],
  //...
}

When the user double-clicks on any of cells in these columns, he will see the following result in the browser:

If the cell contains a large text, it won't be very convenient to edit it in a small input field. For such cases, the widget has a popup editor. It allows users to edit data in a special pop-up window. Let's add this editor to the “Address” column settings:

columns:[
  { id:"address", header:"Address", editor:"popup", ...,},
  //...
],//...

When the user double-clicks on any cell in the “Address” column, he will see the following result in the browser:

Now let's move on to the “Available” column. As you remember, I have set up a template for it that turns the true and false values into the corresponding YES and NO strings. Let's make it so that the user can switch between these values. For this I need to use a special inline-checkbox editor. It allows users to change the values ​​in a cell when they click on it. But to make it work, I also need to set the checkboxRefresh property to true in the table constructor. This property controls the checkbox editors work in the table. The column settings look like this:

{
  //...
  checkboxRefresh:true
  columns:[
    //...
    { id:"active", header:"Available", editor:"inline-checkbox", template:customCheckbox, ..., },
  //...
  ],
  //...
}

When the user clicks on any cell in this column, its value will change to the opposite.

There are columns, which data is pulled up from collections. For these columns I use the combo editor. It allows users to select the desired option from the drop-down list. The list is formed automatically based on the data of its collection. The column settings look like this:

columns:[
  { id:"company", header:"Company", editor:"combo", 
   collection:"./data/company.json", ..., },
  { id:"car_make", header:"Car make", editor:"combo", 
   collection:cars_make_data, ..., },
  { id:"country", header:"Country", editor:"combo",
   collection:"./data/country.json", ..., },
  //...
],
//...

When the user double-clicks on any of cells in these columns, he will see the following result in the browser:

The “Color” column deserves special attention. I would like to remind you that the incoming data are HEX codes of different colors. The Webix table has a special color editor that allows users to select the desired color in a special pop-up window. The column settings look like this:

columns:[
  { id:"color", header:"Color", editor:"color", template:..., },
  //...
], 
//...

When the user double-clicks on any cell in the “Color” column, he will see the following result in the browser:

Now I need to figure out how to edit the data in the “Date” column. To work with dates, the table has a special date editor. As in the case with the filter, the widget displays a compact calendar near the cell, where users can select the desired date. This date will immediately appear in the edited cell. The column settings look like this:

columns:[
  { 
    id:"date", header:"Date", editor:"date", 
    format:webix.i18n.longDateFormatStr, ..., 
  },
  //...
], 
//...

When the user double-clicks on any cell in the “Date” column, he will see the following result in the browser:

In this article you can read more on what data editing in the Webix library is capable of.

Validation

After editing, the data should be saved on the server. It is good practice to check the changed values before saving. You can ask me how to do it at the table? But everything is extremely simple. The Webix library has special rules that can be applied to each editable field. Depending on the type of data, I need to apply different rules.  

There are several columns with numeric values ​​for which I will set the webix.rules.isNumber rule. So now, when editing the cell the widget will check whether its value is a number. 

There is also a column that contains email addresses. I will check its values using the webix.rules.isEmai rule.

For the “Price” column, I want to apply custom validation conditions. Let's make it so that the user can only enter data in the range of $20 to $500. To do this, I need to create a special function:

function(obj){ return (obj>20 && obj<500); }

I will check all the other columns using the webix.rules.isNotEmpty rule. It means that these columns must be filled in anyway.

For applying all of these rules, the table has a special rules property. Inside its object, I need to specify the ids of the required columns and set the appropriate rules to them. It looks like this:

column:[...],
rules:{
  rank:webix.rules.isNumber,
  company:webix.rules.isNotEmpty,
  email:webix.rules.isEmail,
  price:function(obj){ return(obj>20 && obj<500) },	
}

By default, the validation starts after the editor is closed. If the entered values ​​do not correspond to the rules, the whole row will turn in red. In the upper right corner of the edited cell there will appear a red mark.

Headers and Footers  

When working with big data sets they are divided into many columns in the table. For convenience, I can group their headers into specific categories. This approach helps to structure the table and makes it easier to find the information the user wants. The DataTable widget allows me to combine headers using the colspan and rowspan properties. They are similar to the regular HTML table. For example, I can show you how to combine the Price, Card and IBAN columns into the Payment information category. To do this, I need to slightly change the header property of the corresponding columns:

column:[
  //...
  { id:"price", header:[{text:"Payment information", colspan:3}, "Price"], ..., },
  { id:"credit_card", header:["","Card"], ..., },
  { id:"iban", header:["","IBAN"], ..., },
  //...
]

In the browser, you can see the following result:

The headers are enabled by default. The footers need to be activated separately. To do this, I need to set the footer property to true in the widget constructor. Let's define a footer name for the first column and combine it with the second column using the colspan property. In the footer of the “Available” column I will count and display the number of available cars. The column settings look like this:

column:[
  //...
  { id:"stared", header:[...], ..., footer:{ text:"Available:", colspan:2 } },
  //...
  { id:"active", header:[...], ..., footer:{content:"summColumn"}, ..., },
	//...
]

The expression {content:"summColumn"} counts all the true values ​and displays their number in the footer. All changes in the “Available” column will immediately appear in its footer. In the browser, you can see the following result:

Column visibility

If the table consists of many columns, it would be very convenient to hide some options that are unnecessary at the moment. The Webix developers took care of this problem and implemented the headerMenu feature. With its help, the user can control the visibility of columns via a special interface. You can also customize it to your needs.

I want to add the headerMenu icon as a header for the first two columns and customize its popup menu. The code looks like this:

//...
headermenu:{
  width:210,
  data:[ 
    { id:"car_year", value:"Year" },
    { id:"color", value:"Color" },
    { id:"vin_code", value:"VIN" },
    { id:"phone_number", value:"Phone" },
    //...
  ]
},
column:[
  { id:"stared", header:[{ content:"headerMenu", colspan:2, ...,}], ..., },
  { id:"rank", header:["",""], ..., },
  //...
]

In the object of the headermenu property I can set the width of the pop-up window and an array of columns that can be hidden by clicking on the special eye icon.  

In the browser, you can see the following result:

Now the user can easily hide and show the required columns. All he needs is to click on the icon in the upper left corner and select the required items in the drop-down list.

Paging

If the number of rows in the table is large enough, it is not very convenient to use long vertical scrolling. Let's give users an ability to page through the data.

For this, I need to create a paging module in the pager.js file. Its code looks like this: 

//pager.js
const pager = {
  view:"pager",
  id:"pager",
  size:20,
  group:5,
  template:`{common.first()} {common.prev()} 
{common.pages()} {common.next()} {common.last()}`
};

Using the size and group properties I can set the number of visible items on the page (20) and the number of visible buttons on the pager (5). It is all settings. I can also set the template property, which defines buttons for switching pages (other than number buttons).

Now I need to connect the module with the component into the index.html file. I also need to include the variable with the pager into the application constructor:

//index.html
<!--App sources -->
<script src="js/datatable.js" type="text/javascript" charset="utf-8"></script>
<script src="js/pager.js" type="text/javascript" charset="utf-8"></script>
//...
<script type="text/javascript">
  webix.ready(function(){
    webix.ui({
      rows:[
        datatable,
        {cols:[
          {},pager,{}
        ]}
      ]
    });
  });
</script>

Finally, it is necessary to connect the pager to a table. To do this, I need to set the pager property to the pager id in the table constructor. These simple manipulations are used to implement a pager for a table in the Webix library. In the browser, you can see the following result:

So now, when the user clicks on some pager button, the widget will display the corresponding 20 elements on the page.

Operating with Rows

Let's give users an ability to add, delete and drag-n-drop the rows. To do this, I want to add 2 separate columns with corresponding icons for deleting and dragging. In the header of these columns, I will place an icon for adding new rows. 

It is worth noting that the Webix library has several ways for adding icons into the component. For example, I can use icons from the built-in font (<span class = 'webix_icon wxi-drag'> </span>), or special built-in elements (common.trashIcon()).

To do this, I need to go back to the columns property and specify the following settings:

column:[
  //...
  { 
    header:[{text:"<span class='webix_icon wxi-plus-circle'></span>", colspan:2}], 
    width:50, template:"<span class='webix_icon wxi-drag'></span>" 
  },
  { header:["",""], width:50, template:"{common.trashIcon()}" }
]

Now at the end of the table there are 2 additional columns with icons for deleting and drag-n-drop. Since the table has a lot of columns, let's fix these 2 columns with icons at the end of the table. To do this, I need to set the rightSplit property to 2 in the widget constructor. So now the user can see these columns on the right part on top of other elements when scrolling horizontally. In the browser, you can see the following result:

The icons are ready. Now I need to set handlers for the click event of these icons. To catch a click event on any table element with a specific css class, I need to use the onClick property. In the object of this property I need to specify the icon class and set the corresponding handler. In my case, I need to catch a click on the icons with the wxi-plus-circle and wxi-trash classes:

onClick:{
  "wxi-plus-circle":() => addNewElement(), 
  "wxi-trash":(e,id) => removeElement(id), 
  //...,
}

Now let's create these handlers. The function for adding new data looks like this:

function addNewElement(){
  const table = $$("car_rental_table");
  const id_new_elem = table.add({"active":0,"color":"#1c1919","date":new Date()}); 
  table.showItem(id_new_elem); 
}

Using the add() method of the table, I can add new data to it. This method returns the id of the new element. To show a new item in the table, I need to pass its id to the showItem() method of the table.  

The function for deleting data looks like this:

function removeElement(id){
  $$("car_rental_table").remove(id);
}

The remove() method takes the id of the selected element and removes it from the table.

So now I need to implement drag-n-drop the rows. To activate this option I need to set the drag property to "Order" in the table constructor. After this, the user can drag any row to the desired location.

Now the user can drag an element by any part of it. Let's define a special drag area for dragging the rows. It will be an icon with the wxi-drag class .

For doing this, there is a special on property. Inside of its object I need to set the handler for the onBeforeDrag event:

on:{
  onBeforeDrag:function(data, e){ 
    return (e.target||e.srcElement).className == "webix_icon wxi-drag";
  }
}

In the browser, you can see the following result:

Now the user can add, delete and drag-n-drop rows of the table using the special icons.

Toolbar with Controls

Let's allow users to clear filters, add new columns and export data to Excel format. To do this, I need to create a toolbar with corresponding buttons.

In the toolbar.js file I create a toolbar component. Inside this component I define the Reset filters, Add column and Export to Excel buttons. It looks like this:

const toolbar = {
  view:"toolbar",
  css:"webix_dark",
  height:50,
  //...
  cols:[	
    //...	
    { view:"button", label:"Reset filters", click:resetFilters },
    { view:"button", label:"Add column", click:addColumn },
    { view:"button", label:"Export to Excel", click:exportToExcel }
  ]
};

Using the click property, I can set handlers for the button click event. Let's create these handlers. And I want to start from the function that resets the filtering of the data and the filters themselves. The code looks like this: 

function resetFilters(){
  const table = $$("car_rental_table");
  table.filter(); 
  table.showItem(table.getFirstId()); 
  table.setState({filter:{}}); 
}

If I call the filter() method without parameters, the widget will display the data in the initial order. I also use the setState() method of the table to clear the fields of its filters.

The next function can add a new column. Its code looks like this:

function addColumn(){
  const table = $$("car_rental_table");
  table.config.columns.splice(3,0,{
    id:"c"+webix.uid(),
    header:`<span class="webix_icon wxi-close-circle" 
webix_tooltip="Delete column"></span>Extra column`,
    editor:"text",
    width:120
  });
  table.refreshColumns();
}

Using the config.columns property of the table, I get an array with column settings. Using the splice() method  I add an object with new column settings to the 4th position there. When the column configurations are changed, I need to refresh the columns view using the refreshColumns() method of the table.

And there is a function left that exports the table data to Excel format. Its code looks like this:

function exportToExcel(){
  webix.toExcel("car_rental_table", {
    filename:"Car Rental Table",
    filterHTML:true,
    styles:true
  });
}

Inside this function I use the webix.toExcel() method, which takes the table id and an object with certain settings. That's the whole trick.

When everything is ready, I need to include the toolbar.js file into the index.html and add the toolbar variable to the application constructor:

<script type="text/javascript">
  webix.ready(function(){
  webix.ui({
    rows:[
      toolbar,
      datatable,
      {cols:[
        {},pager,{}
      ]}
    ]
  });
});
</script>


In the browser, you can see the following result:

Now the user can reset data filtering, add new columns, and also export data to Excel format.

There is one more nuance left. When creating a new column, I have added an icon with the wxi-close-circle class to its header. So now I need to set a handler on the click event for this icon, to delete the column. I can do it via the onClick property:

onClick:{
  //...
  "wxi-close-circle":(e,id) => deleteColumn(id)
}

Now let's create this handler:

function deleteColumn(id){
  const table = $$("car_rental_table");
  table.clearSelection();
  table.editStop();
  table.refreshColumns(table.config.columns.filter(i=>i.id !== id.column));
}

I get an array with column settings via the config.columns property, filter unnecessary elements out from it and pass the updated array to the refreshColumns() method. 

In the browser, you can see the following result:

Now the user can delete a new column by clicking on a special icon in the upper right corner of its header.

Conclusion

In this article, I have shown you how to create an application based on the DataTable widget of the Webix UI library. Now you know how to customize the table using a wide variety of properties and methods. As you can see, all settings are intuitive and easy to use. In fact, it is only a small part of everything that the Webix table can offer you. To read more about the DataTable features, visit its documentation page. There you can find a detailed description of all capabilities with related samples.

You can find the full source code and live demo here.

Disclaimer: The author provides this code and software “AS IS”, without warranty of any kind, express or implied, including but not limited to fitness for a particular purpose and non-infringement. In no event shall the author be liable for any claim, damages or other liability in connection with the software or code provided here