How to create very custom editor?

Hi! I’m trying to create a TreeTable with column “folder_id” which should contain kind of custom editor. In our database we have a tree-like like structure which consists of nested folders. An user should be able to select a folder from that tree.

So we have a TreeTable with several columns in it.

  1. An user clicks on a cell of “folder_id” column.
  2. Large bootstrap modal popup appears.
  3. It has a webix tree view inside it, which contains our folders tree.
  4. The user double-clicks a folders he needs.
  5. The modal closes.
  6. Corresponding “folder_id” cell now contains a text representation of the selected folder, like “folder 1 > nested folder > level 3 folder”. It is for humans.
    “folder_id” column resizes accordingly to “folder_id” column text content.
  7. The user clicks “Save” button.
  8. table.serialize() is being called. But “folder_id” fields in the data returned by serialize() should contain not the text representation, but ID of folders - it is for server, which operates with IDs.

I mean I expect serialize() should return data like this:

{
	id: 1494299107063,
	$level: 1,
	$parent: 0,
	title: "Folder title",
	url: '/'
	is_visible: 1,
	is_mobile: 1,
	folder_id: 123
}

I was able to write some. Here are excerpts from it:

var adjustColumns = function (table) {
	table.adjustColumn('title');
	table.adjustColumn('url');
	table.adjustColumn('is_mobile');
	table.adjustColumn('folder_id');
};

var table = webix.ui({
	container:  'top-menu-left-webix',
	view:       'treetable',
	select:     false,
	navigation: false,
	autoheight: true,
	autowidth:  true,
	editable:   true,
	drag:       true,
	columns: [
		{id:'title',      header:'Title', editor:'text', template:'{common.treetable()} #title#'},
		{id:'url',        header:'URL',   editor:'text',                                                                            width:270},
		{id:'is_visible', header:'<span class="glyphicon glyphicon-eye-open vmiddle"></span>', template:'<label class="top-menu-full-label">{common.checkbox()}</label>',   width:50},
		{id:'is_mobile',  header:'Mobile',               template:'<label class="top-menu-full-label">{common.checkbox()}</label>', width:120},
		{id:'folder_id',  header:'Link',                 template:'<label class="top-menu-full-label link-folder"></label>',        width:150},
		{id:'buttons',    header:'',                     template:'<?=$buttonsTemplate?>',                                          width:180}
	],
	data: data[e.side],
	on: {
		onAfterEditStop: function () {
			//adjustColumns(this);
		}
	}
});

//It's for the TreeTable
table.on_click['link-folder'] = function (e, id, target) {
	linkFolderData = {table:table, id:id};
	$('.top-menu-folders-modal').modal();
};

//It's for popup
var foldersTree = webix.ui({
	container:  'top-menu-folders-webix',
	view:       'tree',
	select:     false,
	navigation: false,
	data: foldersTreeData,
	on: {
		onItemDblClick: function (id, e, node) {
			//Updating data storage
			var menuItem = linkFolderData.table.getItem(linkFolderData.id.row);
			menuItem.folder_id = id;
			linkFolderData.table.updateItem(linkFolderData.id.row, menuItem); // <--- Problem 1
			//Drawing path for selected folder
			var pathPartsList = [];
			do {
				var item = this.getItem(id);
				pathPartsList.push(item.value);
				id = item.parent;
			} while (id > 0);
			pathPartsList = pathPartsList.reverse();
			var html = '';
			for (var a in pathPartsList) {
				html += '<div style="padding-left:'+(a*20)+'px">'+pathPartsList[a]+'</div>';
			}
			$(linkFolderData.table.getItemNode(linkFolderData.id)).find('.link-folder').html(html);
			adjustColumns(linkFolderData.table);  // <--- Problem 2
			//Hiding popup
			$('.top-menu-folders-modal').modal('hide');
		}
	}
});

This part works by itself:

$(linkFolderData.table.getItemNode(linkFolderData.id)).find('.link-folder').html(html);

But if one of these lines is uncommented then all markup resets to one defined in the template:

linkFolderData.table.updateItem(linkFolderData.id.row, menuItem); // <--- Problem 1
adjustColumns(linkFolderData.table);  // <--- Problem 2

Looks like I don’t fully understand how webix works, it’s logic is conflicting with how I think things should work. I tried to create a custom editor via webix.editors - but was puzzled even more. So I’m stuck now. Can you please help me and point out how to implement the algorithm I described above?

Ultimately I have succedeed in my task by using updateItem() method and TreeTable’s scheme.$change field.

Code looks like this:

var editData;

var adjustColumns = function (table) {
	table.adjustColumn('title');
	table.adjustColumn('url');
	table.adjustColumn('is_mobile');
	table.adjustColumn('folder_id');
};

var foldersTree = webix.ui({
	container:  'top-menu-folders-webix',
	view:       'tree',
	select:     false,
	navigation: false,
	data: foldersTreeData,
	on: {
		onItemDblClick: function (id, e, node) {
			//Updating data storage
			var menuItem       = editData.table.getItem(editData.id.row);
			menuItem.folder_id = id;
			editData.table.updateItem(editData.id.row, menuItem);
			//Fixing sizes
			adjustColumns(editData.table);
			editData.table.adjustRowHeight('folder_id');
			//Hiding popup
			$('.top-menu-folders-modal').modal('hide');
		}
	}
});

var table = webix.ui({
	container:      'top-menu-left-webix',
	view:           'treetable',
	select:         false,
	navigation:     false,
	autoheight:     true,
	autowidth:      true,
	editable:       true,
	drag:           true,
	fixedRowHeight: false,
	columns: [
		{id:'title',      header:'Title',         editor:'text', template:'{common.treetable()} #title#'},
		{id:'url',        header:'URL',           editor:'text',                                                                                               width:270},
		{id:'is_visible', header:'<span class="glyphicon glyphicon-eye-open vmiddle"></span>', template:'<label class="top-menu-full-label">{common.checkbox()}</label>', width:50},
		{id:'is_mobile',  header:'Mobile',                       template:'<label class="top-menu-full-label">{common.checkbox()}</label>',                    width:120},
		{id:'folder_id',  header:'Link',                         template:'<label class="top-menu-full-label top-menu-link-folder">#_folder_id_html#</label>', width:150},
		{id:'buttons',    header:'',                             template:'<?=$buttonsTemplate?>',                                                             width:180}
	],
	data: data[e.side],
	scheme: {
		$change: function (item) {
			if (!item.folder_id) {
				item._folder_id_html = '';
			} else {
				var pathPartsList = [];
				var tempId        = item.folder_id;
				do {
					var i = foldersTree.getItem(tempId);
					pathPartsList.push(i.value);
					tempId = i.parent;
				} while (tempId > 0);
				pathPartsList = pathPartsList.reverse();
				var html = '';
				for (var a in pathPartsList) {
					html += '<div style="padding-left:'+(a*20)+'px">'+pathPartsList[a]+'</div>';
				}
				item._folder_id_html = html;
			}
		}
	}
});

table.on_click['top-menu-link-folder'] = function (e, id, target) {
	editData = {table:table, id:id};
	$('.top-menu-folders-modal').modal();
};

I’m not sure if this the right way to do things, but at least it works. And was not too hard to write.