# Custom Elements
## Introduction
Custom HTML elements is a greater replacement for `include`. It delegates a portion of the rendering to another EJS template.
The basic example bellow defines a custom text input element, that has its input inside an label element and has a text title in front of it:
```html
```
Another EJS template file may "instantiate" this element:
```html
```
The final rendering result is shown bellow:
```html
```
## Concepts
As of this writting, the W3C is currently working in custom elements for the Web, under the [Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components) umbrella. But we are *not* talking about that here, this is a completely different beast (inspired by the rising standard, but yet not the same thing). EJS-HTML custom elements are resolved at render time, before the browser get to the HTML.
Any element that have a dash (`-`) on its name will be treated as custom (this follows the W3C standard). At compile time, they will be identified and compiled to a `renderCustom` call. At rendering time, the `renderCustom` will be called in order to render the custom element and return the HTML result. So it works conceptually like a super-powered include, because it accepts dynamic attributes and complex HTML content.
## Attributes
Attributes in the custom element's open tag will be passed as the `locals` for it.
The attribute name will be transformed from dashed-separated to camel-case notation, for example, the attribute `'my-own-attr'` will be passed as `'myOwnAttr'` local data. The rule is: any dash (U+002D) followed by an ASCII lowercase letter a to z will be removed and the letter will be transformed into its uppercase counterpart.
There are three distinct notations for attributes, depicted bellow.
* boolean/true: `` will produce the following locals object: `{avoidGoats: true}`, much like native HTML boolean attributes. `false` should be represented by its absence.
* string: `` will produce: `{avoid: 'goats', keep: 'all ' + animal + 's'}`
* JavaScript value: `` will produce: `{avoid: ['goats', 'more goats']}`. This allows complex data to be passed as part of the `locals`, not only strings. Note that the syntax is `attr="<%= ... %>"`, with the quotes right next to the EJS escaped tag. Any character between them (including spaces), would concatenate them and result in a string.
## Content Placeholder
The `` tag in a custom element definition will be replaced by the content inside the custom element (`eh` stands for `ejs-html`).
The example bellow shows a basic usage. The declaration and usage are represented in the same code block for brevity, but they are usually written separately.
```html
Hi you
```
Use content placeholders to pass arbitrary HTML content and attributes to anything else.
## Multiple Content Areas
Sometimes it is useful to have multiple placeholder areas. If this is the case, you may name each one with `` in the definition and mark each type with `` on usage.
Example:
```html
TitleBody
Title
Body
```
Note that an empty-named content markup (``) is implied for any content not inside a `eh-content` tag. In the example above, `Body` is treated as if it was written as `Body`
## Default Placeholder Content
A `eh-placeholder` element will be replaced by the content provided for it. If no content is given, you can provide a fallback.
One practical application of this feature is to allow both simple and complex content, from both attribute and HTML content. Like this:
```html
<%= locals.title %>
OneComplex Title
Two
Simple Title
One
Complex Title
Two
```
Note how the default content for the title is a read from the `title` attribute, but if a HTML content for it is provided, it's used instead.
## Divergence From W3C's Web Components
In the current spec, the W3C declares a `` tag to act as ejs-html's ``. The spec is not followed by this lib because (a) its mechanism based on CSS selectors to solve multiple content areas is too complex (b) its usage is hard to optimize on compile time (c) there is no support for default content.
## The CustomRender Callback
Currently, this lib does not attempt to detect which EJS template to use to render a given custom element. You must implement that yourself and provide when rendering each template. For example, if your custom element definitions are in a folder, you are responsible to handle the routing.
A full example bellow, for the given folder structure:
```
-- elements
|
+- my-input.ejs
+- my-dialog.ejs
+- my-header.ejs
-- views
|
+- home.ejs
```
And the following content for `home.ejs`:
```html
```
To render the home page:
```js
let ejs = require('ejs-html'),
fs = require('fs'),
cache = new Map
// Simple caching logic
function compile(path) {
if (!cache.has(path)) {
cache.set(path, ejs.compile(fs.readFileSync(path, 'utf8'), {
filename: path
}))
}
return cache.get(path)
}
compile('views/home.ejs')({}, function renderCustom(name, locals) {
// We are responsible to translate the element name (like 'my-header') to file path
// Note that `renderCustom` is passed as argument again, enabling custom elements to
// also use others
return compile('elements/' + name + '.ejs')(locals, renderCustom)
})
```