Template functions that return HTMLElement?

Is there any way to have a template for an item (like a list item) that is defined as a function that returns an HTMLElement or array of HTMLElement?

I would like to be able to create the template for an item using document.createElement() rather than using a string template. This would allow us to template items using other HTML fragment rendering technologies like lit-html, JSX, etc.

It would also allow us to connect event handlers, use properties that are function callbacks, etc.

I am new to the Webix, so I may be missing the obvious here.

Thanks,
Neal

Hello,

Please check Data Templates - Data templates documentation page. Webix Docs

Hi Nastja,

Yes, I have already reviewed this page. For external templates, you can use the template element and use an id to retrieve. However, the template function approach ultimately returns a string. This means that we cannot hook up event handlers, etc. If we want to encapsulate a Webix widget into a React component for example, we may want to be able to have a function that builds JSX and then renders it. There are a lot of other ways to build document fragments and we may also want to use something like lit-html and template stamping for example.

From the documentation:

Templates can be defined in five very flexible ways:

  • as HTML strings within JavaScript objects
  • by creating named templates
  • by extracting them from an HTML container
  • by retrieving them from an external file
  • as a function returning a string

I am asking about support for a function that returns HTMLElement or an HTMLElement array. These would then be added to the host element using appendChild.

As an alternative, the function call could also pass in the host element and we could append the children directly. I guess the function could then return undefined or null to indicate that he have handled the rendering inside the function.

I did a little proof of concept on building a shim function what would accomplish what we want. If could be cleaner and ES5 friendly, but I just did the POC in Chrome and used string interpolation, fat arrow functions, etc. It uses requestAnimationFrame() and querySelector(), but this could be a lot more efficient if the host element was passed into the function. You can see it here: Snippet

The main helper function is this:

function templateFuncFactory(container, templateFunc) {
  	return function(...args) {
    	const content = templateFunc.call(this, ...args);
    	if (content instanceof HTMLElement) {
    		const phid = `ph${webix.uid()}`;
    		requestAnimationFrame(() => {
      			const placeholder = container.querySelector(`#${phid}`);
		        placeholder.parentElement.replaceChild(content, placeholder);  
    		});
    		return `<div id="${phid}"></div>`;
        }

      	return content;
    };
}

Thanks,
Neal

Unfortunately, it is not possible. The template must return an HTML string.

The workaround with temporary DIV tags, as in the above code will work in most cases, but still has limitations ( in case of data export, for example, where the same templates are used and static HTML/text content is expected )

This means that we cannot hook up event handlers, etc.

In most cases, specific event handlers can be replaced with a global one.
Data widgets already provide a built-in solution for click handler inside of templates

https://docs.webix.com/api__mouseevents_onclick_config.html

MouseMove, DblClick, and RightClick can be handled in a similar way.

I see how such kind of templates can be used to improve interoperability with JS frameworks. Still, there is no way to add such functionality without major changes in core library.

Hi Maksim,

Yes, I found the information on global event handling and have that working for simple events. The main reason I was interested was so that we could use web components in cells and properly assign object properties and custom event handlers on these web components. I have improved the above code using a Promise based micro-task scheduler approach that is faster than the requestAnimationFrame technique and also manage a queue of items to be replaced. The end result is it is really fast now!

I had not thought about data export, so thanks for the warning. In this case, I believe I can build a custom element shim that does initialization when the custom element is added to the DOM tree (connectedCallback()), but would need to experiment a bit more. But, for now, we have what we need.

In the long run, the custom element community really needs a standardized platform way of doing template instantiation with bindings. Here is a link to Apple’s proposal for this: webcomponents/Template-Instantiation.md at gh-pages · WICG/webcomponents · GitHub. The lit-html Polymer team at Google has done some work towards this and given more thought to repeating sections, etc. at GitHub - PolymerLabs/template-instantiation.

Thanks,
Neal