Custom HTML Form Items


#1

This is something that gets brought up in a couple places
(example 1, example 2).

In DHTMLX Suite 5.0, you were able to make your own form controls. It would be great if version 7.0 could do so as well.

Perhaps a form item type called “custom” could be added, which would start out as an Input item, and the user could modify it via functions that are passed to the item.

Here is a proof of concept:
Add something similar to the following to the switch case statement in Form.prototype._addLayoutItem:

case "custom": {
	var Custom = /** @class */ (function (_super) {
		__extends(Custom, _super);
		function Custom() { return _super.apply(this, arguments); }
		
		if (config.changeElement != null) {
			Custom.prototype._draw = function () {
				const vNode = _super.prototype._draw.call(this, ...arguments);
				vNode.onhydrate = () => config.changeElement.call(this, vNode.el.querySelector("input"), config, vNode);
				return vNode;
			};
		}
		return Custom;
	}(input_1.Input));

	var custom_1 = (this._attachments[name] = new Custom(null, config));
	this._state[name] = custom_1.getValue();
	custom_1.events.on(types_1.ItemEvent.beforeChangeProperties, function (props) {
		return _this.events.fire(types_1.FormEvents.beforeChangeProperties, [name, props]);
	});
	custom_1.events.on(types_1.ItemEvent.afterChangeProperties, function (props) {
		_this._changeProps(name, props);
		_this._state[name] = custom_1.getValue();
		_this.events.fire(types_1.FormEvents.afterChangeProperties, [name, props]);
		_this.layout.paint();
	});
	custom_1.events.on(types_1.ItemEvent.change, function (value) {
		_this._state[name] = value;
		_this.events.fire(types_1.FormEvents.change, [name, value]);
	});
	custom_1.events.on(types_1.ItemEvent.beforeHide, function (value, init) {
		if (!init) {
			return _this.events.fire(types_1.FormEvents.beforeHide, [name, value]);
		}
	});
	custom_1.events.on(types_1.ItemEvent.beforeShow, function (value) {
		return _this.events.fire(types_1.FormEvents.beforeShow, [name, value]);
	});
	custom_1.events.on(types_1.ItemEvent.afterHide, function (value, init) {
		_this.layout.getCell(name).hide();
		!init && _this.events.fire(types_1.FormEvents.afterHide, [name, value]);
	});
	custom_1.events.on(types_1.ItemEvent.afterShow, function (value) {
		_this.layout.getCell(name).show();
		_this.events.fire(types_1.FormEvents.afterShow, [name, value]);
	});
	custom_1.events.on(types_1.ItemEvent.beforeValidate, function (value) {
		return _this.events.fire(types_1.FormEvents.beforeValidate, [name, value]);
	});
	custom_1.events.on(types_1.ItemEvent.afterValidate, function (value, isValid) {
		_this.events.fire(types_1.FormEvents.afterValidate, [name, value, isValid]);
	});
	break;
}

And then add something like this at the end of the if (vnode.el == null) block in the function hydrate:

if (vnode.onhydrate) {
    vnode.onhydrate(vnode, withEl);
}

Here’s an example Input using this to create a typeahead dropdown list:

var form = new dhx.Form("form_container", {
    rows: [
        {
            type: "custom",
            name: rootKwargs.name,
            id: rootKwargs.index,
            label: "State",
            value: "Alaska",
            changeElement: (element, config, vNode) => {
                states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'];
                $(element).typeahead({ hint: true, highlight: true, minLength: 1 }, { name: "states", source: function findMatches(query, callback) {
                    const matches = [];
                    const substrRegex = new RegExp(query, 'i');
                    $.each(states, function(i, str) { if (substrRegex.test(str)) { matches.push(str); } });
                    callback(matches);
                } });
            },
        },
    ],
});

image


#2

Thank you for your suggestion.
I’ve sent it to the dev team and it will be observed.