I just started learning about the HTML custom elements, and through reading a series of intros, tutorials, and documentation, I think I have a good handle on how it works, but I have a philosophical question on the proper way to use or not use the <template> tag.
Custom elements give you the ability to encapsulate new functionality, simplifying the structure of your HTML document, and allowing you to simply insert a <my-custom-element>...</my-custom-element> tag instead of <div class="my-custom-element"><span class="part1">...</span><span class="part2">...</span></div>.
The class definition for the element then sets up the structure and functionality of that element. A bunch of the tutorials then describe how to use <template>...</template> and <slot>...</slot> to set up the contents of the custom element. You would then have to then include the template code in every HTML document in which you want to use the element rather than setting it up in the custom element class's constructor. Doesn't this run counter to the fact that custom elements help simplify and encapsulate functionality in a way that makes them more portable? Or am I misunderstanding the proper usage and/or placement of the template within the document?
Looking through SO, the closest I can find to addressing this is this question:
How to stamp out template in self contained custom elements with vanilla js?
But the answer essentially sidesteps this all together and says "Don't use <template>," and so doesn't really clear up my confusion.
Actually <template> elements can be imported from another document via HTML Imports, along with the Javascript code that will define the custom element:
<link rel="import" src="my-custom-element.html">
...
<custom-element></custom-element>
So it doesn't need to be included in a every HTML document. This post shows a minimal example.
HTML Imports are implemented only in Chrome and Opera. If you want to use them in the with Firefox and Safari you'll need to use the HTML Imports polyfill.
On the other hand and for the moment, Mozilla and Apple don't intend to implement HTML Imports natively in their respective browsers. Therefore they recommend to define custom elements with pure Javascript modules (with import or <script src="...">), and promote template literals strings instead, which offer some advantages (variables, functions), but are sometimes more complicated to code in an IDE (because of their string representation).
Maybe in the future standard HTML modules will be adopted by all browsers, and <template> will come back in the spotlight...
Note that without HTML Imports you can still import yourself some HTML documents with fetch():
fetch( "template.html" )
.then( stream => stream.text() )
.then( text =>
customElements.define( "c-e", class extends HTMLElement {
constructor() {
super()
this.attachShadow( { mode: 'open'} )
.innerHTML = text
}
} )
)
Update 2019
HTML Imports won't be supported natively after Chrome 73. You should then use the other solutions listed above (the polyfill, an alternate module loader, JS import, or a direct download with fetch).
Disclaimer: I'm an author of the rich-component library mentioned below.
After some time of experimenting with custom elements and recently raising a full blown project based solely upon them I'd like to share my insights on this:
any component tiny as it is, is a candidate to grow to some beast
HTML part of it may grow to a point where it is very non-convenient to keep it within JS
do use template, built and parsed once and from that point cloned and injected into the shadow root - this is the same best practice as to use document fragment instead of mutating a living DOM
if the template contents should be changed from component's instance to instance - some kind of data binding framework may be used, and if minimalist approach on those is taken - it might still be easier and more performant to deal with a cloned-from-template document fragment than operate on string or template literals
In order to not write the same dozens of lines over and over again I've prepared rich-component library, which:
normalizes some API for template provisioning and all those 'clone template, create shadow, inject template's content into it' lines of repeating code
known to fetch html contents when html URL is provided
caches the templates so the fetch is done only once
Related
I wonder, is it possible to create a visual no-code builder to work with JS components (e.g. React JSX) if them are manually hard-coded before?
Let me explain what I mean.
Suppose a simple React component pages/index.js previously written by a developer manually:
function HomePage() {
return <div>Welcome to Next.js!</div>
}
export default HomePage
How can we change such a component in the front-end using a visual builder?
For example, we want to add a new HTML element (e.g. H1) inside the existing div.
As I can understand, the builder first needs to know in which JS file the HTML markup is stored to update it. So we can add id="homepage" to the div first and then store a relation as a config like
{"homepage": "pages/index.js"}
And now if we add a new element inside <div id="homepage">, the builder adds the element to the DOM of the div at the client, then takes the whole updated DOM of the div and writes back to the file index.js according to the config
Ok, but the file contains not only HTML markup - it's JS (React) code.
How to keep all the JS code e.g. function HomePage(), return, export default and so on ?
As an option, we can separately load all the JS code as HTML including non-HTML code as #text nodes. Then update the DOM and re-write everything back to the file.
But it sounds complicated and may cause unexpected issues.
So what solution would be the best?
Or maybe there is a ready React-specific solution?
Or maybe it's a bad idea at all to parse and re-write manually hard-coded components by visual builder and the only solution is to store everything as JSON like "homepage":{"div", {"class":""}, "Welcome..."} which is more easy for re-writing ? (but requires a new render)
It depends
I believe the answer that you are looking for very much depends on the level of flexibility that you want your no-code builder to have.
Depending on that, your project could benefit of the trade-offs and advantages of different solutions.
Let's briefly remember that basically a React component will need some props that then will be taken through a render template and output a working HTML. This is assuming a basic case where you don't need your react components to be smarter. Additionally, JSX is just sugar coating over function calls, so you could basically just compose functions to output a React component independently of using the JSX syntax. Hence no need to declare HTML, just changing the output of your no-code tool to JS instead of HTML.
For example, if you can modify how the no-code tool render, you can specify that when moving an element inside another you basically:
Highly Flexible & Customisable
In a highly flexible setup, I will recommend going through the last option you numbered, having a Data-Driven UI is the most common of the cases for complex systems. For example, Figma has an option to export the designs as react components, you can read how they do it here. Basically they take the tag output from figma which is a JSON of tags and create some React componets using templates. If you define "you own UI language" you could have quite a good control over what blocks you can build and define the way of interacting with them (e.g. adding a img component could be quite easy if you know the props and what it needs to render, then creating a React template for it is easy).
Limitations: you require to plan quite well the API of the parser and interaction between different sets of items.
Simple no-code builder
For simpler scenarios you could go with the first approach that you mention, you won't even need to add ids, some tools like React Developer Tools can already inspect the VirtualDOM to understand which part of the render belongs to which React Element (using react internals, which could take some time to understand, but for example you can inspect in the rendered how they use the data-reactid for identification). Knowing this, you can already define the template functions for the render() method (so the JSX output), and separate it code wise, so that when you generate the code, the HTML template is split from the React code as much as possible.
Silly example of how it could look:
// htmlBlockTemplate.js
export const helloPageTemplate = (props) =>
`<div> <h1>${props.title}</h1> </div>` // This can be generated from the `no-code`
// page.jsx
export const Page = (props) => {
return helloPageTemplate(props)
}
Using functions it could look like:
const Page = (props) =>
return React.createElement('div', null,
React.createElement('h1', title: prop.title, `The title ${title}`)
);
}
Limitations: eventho you could, adding custom components (like another React Component or a web component), it becomes more difficult since you will also have to deal with the import graph and probably.
Inevitably you will need to tweak how the render of the component works (rather by creating a parser from your UI language, or changing how the react component is written. If you have a good control of AST, then writing a parser for either of the cases should not be a problem.
I have a jQuery heavy app I'm in the middle of that has many jQuery plugins and I want to restructure the app, so I'm looking at different frameworks like Angular, React, Riot 2.0, etc.
I like React and Riot, but I can't figure out how I'd make your typical jQuery app (unmetered access to the DOM) into this new world of virtual DOM, etc.
Some of these components are complex and I wouldn't want to rewrite them all into the "VDOM way" and create another nightmare.
Does anyone have an answer to this? Maybe jQuery plugin heavy apps aren't suited, or is there a way to split/combine the UI using both, or maybe a React/Riot like framework that would play well with jQuery?
To access the DOM with jQuery inside a Riot 2.0 custom tag, you can use the mount event and this.root like this:
<my-custom-tag>
<h3>Riot.JS Custom tag + jQuery plugin</h3>
<p>My paragraph</p>
<script>
this.on('mount', function() {
var $node = $(this.root);
$node.find('p').html('updated by jQuery!');
});
</script>
</my-custom-tag>
I am not sure it is the "best practice" but it works with Riot 2.0.10 (Feb 19, 2015).
If the custom tag contains form or input elements, it's simpler, you can access them by their name property, you don't need the mount event:
<my-custom-form>
<input name="email" type="text">
<script>
$(this.email).val('mike#worldcompany.com');
</script>
</my-custom-form>
Riot 2.0 was released just 4 days ago so there are obviously no extensions yet.
But it's a good candidate to transform jQuery- based apps to the "modern world" with using custom tags or "components" in React community.
Custom tags lets you build reusable pieces of client side code with a less need for jQuery selectors and manipulation methods. And HTML and JS is less scattered on the file system.
And Riot 2.0 is designed to play well together with jQuery. You can freely use jQuery plugins together with custom tags so you can transform your app iteratively - one tag at the time if you like.
This is an excellent guide to wrapping DOM libs with React components:
https://github.com/rpflorence/react-training/blob/gh-pages/lessons/05-wrapping-dom-libs.md
In angular someone has probably already recreated the thing you want. You use directives to either implement reusable components or wrap existing ones. To wrap a plugin:
initialize in link (based on isolate scope)
use scope.$watch(key, fn) to update the plugin when something changes
use scope.$apply() in plugin callbacks to tell angular something might have changed, update any relevant two way bindings, and invoke any relevant expression bindings
use scope.$on('$destroy', fn) to clean up
See ui-bootstrap for examples and api design.
The same applies to React, but components instead of directives.
initialize in componentDidMount (based on props)
update the plugin in componentDidUpdate (based on props)
call this.props.onWhatever in plugin callbacks
clean up in componentWillUnmount
See react-bootstrap for examples and api design.
And I don't think Riot is relevant here.
We had the same problem, i.e. turn a pretty big global jquery based admin frontend into nestable conflict free components - w.o much training effort for our partners - so riotjs was just ideal for us.
We agreed on the solution below (see also Michael Rambeau's answer) and are up to now quite content with that.
Via a mixin we give all our riot component tags this function:
var ComponentTagsMixin = {
init: function () {
//<other stuff needed in all tags>
// jquery local to the vDom of this instance:
$: function(selector) {
// you could fail here if (!this.isMounted)
return $(this.root.querySelector(selector))
}
}
```
and so, within the tags you simply turn $ into this.$.
Example, jquery nestable:
$('#nestable').nestable(....)
$('.dd').nestable('expandAll');
into
this.$('#nestable').nestable(....)
this.$('.dd').nestable('expandAll');
and now this allows to have many of those components within the same page w/o conflict and local namespaces and data.
As already pointed out, this is only accessible after the tag is mounted, so run your stuff within an this.on('mount'...) block.
This works remarkably well with legacy jquery libs since they typically allow the user to redefine their Dom selectors for double run-ability within a page anyway.
I've got an HTML document template.html which looks something like:
<div id="Foo">
<span class="Bar"></span>
...
</div>
Which I use as a template to construct some DOM for a component in Javascript.
At first, I just added it to my page example.html by copy-pasting the markup and wrapping it inside of a <template> tag. Then in my Javascript I constructed a clone to add it to the page by doing something like:
var myTemplate = Document.getElementById('#myTemplateId').cloneNode(true);
Document.getElementById('#newParent').appendChild(myTemplate, true);
While this works, I obviously don't want to have to copy and paste my templates over to be able to use my component.
What I would like to do is somehow package my template markup in with my Javascript. I still want to keep my Javascript and Markup separate for development and sanity, but at build time (I'm using GruntJS as my task runner) for the component I want to combine them somehow so they can be packaged in one, easy to link to file. What I mean by that is I want some way to package the template in with my Javascript, as Javascript. That said, I guess my broader question is:
How can you save/package Markup into a Javascript file or object (JSON) so that it can be used as a template to generate DOM in Javascript later?
The closest I have come to finding a potential solution was to convert the Markup document template.html to a string, save it to a variable, and then add it to a temporary element via .innerHTML and then convert it to make it a DocumentFragment. While that could work, there has to be a better way, and converting the Markup to a string doesn't feel right.
Update:
I know through my research that the trend of the industry is moving to HTML imports. However, it's not widely supported yet, so in the meantime, I still want to be able to package my templates into my js.
I have a page which has many buttons that perform an action with javascript. No problem here, just a html page and multiple .js file, one .js file for every action. Just to separate stuff a bit and make everything a bit more manageable. I will write a script to combine them all at deployment.
A lot of my javascript actions need html elements to function. For example to change a color the color.js needs a div with form elements so that user can change the color and press the button to save changes. The color.js binds functions to the onClick of the buttons and such. No problem here.
A problem comes up when there are many javascript actions using many html elements. All these html elements need to put into one html page. This makes managing things complicated. For example when i change the color field id i need to go to color.js and do the change. In addition i have to find the html element in my huge html page and also do the change.
Is it possible to put the html elements needed by my javascript actions into the .js file and somehow include it in my html page?
Of course i can escape all html elements and put it in a javascript variable and document.write it. But escaping is not a nice thing to do and changing html afterwards is quite painful.
I'm also not looking for a ajax function, cause with many different actions this will cause network problems.
Cheers for any thoughts on the subject!
You can use templates to generate dynamic HTML via JavaScript. Most of Templates Engines have the same implementation:
A template structure (DOM) or HTML string
A method to render the template
The data that fills the template (Array, Object)
Settings to configure the template engine
I have used some template engines, one of them is Hogan.js which has a friendly sugared-syntax, very easy to use, but lower performance. It implements the Mustache syntax.
Other template engine is in Underscore.js library, which provides no sugared-syntax but a native javascript syntax, giving more power, and best performance.
The last one that I have used is kendo-ui templates.Kendo UI templates have a heavy emphasis on performance over feature glut. It will trade convenient "syntax sugar" for improved performance.
If you want to try more engines, here you can get a lot of them:
Template-Engine-Chooser
By other hand, if you want just to load static HTML (e.g. an static view), you can use jQuery.load(url) to load data from the server and place the returned HTML into the matched element. .load() is a shorthand method of jQuery.ajax()
Recomendation: whichever option you choose, I recommend using cache, either to save the view or to save the compiled template, thus the performance is improved by avoiding request of the same resource.
When I look in the dojo documentation for template all I get is for dijit and examples only show you been able to use them in a widgit. I'm looking for the equivalent of the below methods in js prototype
var tmpl = new Template(url)
tmpl.evaluate(templateObj)
Does dojo have a template method that you can use in a dojo.declare( class ){} like you can do in js prototype. If not how could I go about similar functionality
Thanks
You may be interested in dojo.string.substitute (you'll need to dojo.require("dojo.string")).
http://dojotoolkit.org/api/dojo/string/substitute
[Edit] Also, if you're interested in acquiring a template for use in substitution from a URL on the same server, you may also want to look into dojo.cache (which is also what is often used to fetch widget templates):
http://dojotoolkit.org/reference-guide/dojo/cache.html
To clarify missingno's response, I don't think dojo.parser is what you're interested in right now; its job is to scan the DOM and transform DOM nodes into widgets and other Dojo components. dijit._Templated only uses dojo.parser when child widgets are involved (i.e. widgetsInTemplate is true); on the other hand, it uses dojo.string.substitute in all cases, to initially parse ${...} strings (e.g. ${id}) in the template.
I don't know Prototype, but this sounds like dojo.parser stuff. It is what is used by dijit._Templated behind the scenes (you can chack that in the source code if you want...)
Just note that you probably wouldn't need to cal this yourself - there is parseOnLoad=true for automatically parsing your initial HTML.