Declarative Custom Element
(DCE) is a part of pure Declarative Web Application
stack. A proof of concept as a part of
WCCG in Declarative custom elements and Declarative Web Application
discussion. NO-JS The functionality of DCE and its data access does not require programming using JavaScript.
It allows to define custom HTML tag with template filled from slots, attributes and data slice
as of now from
local-storage, http-request, location.
UI is re-rendered on each data slice change triggered by initialization or DOM event.
| Live demo: custom-element | Try in Sandbox | tests project | Chrome devtools pugin
The composition assumes the fully functional template and ability to call the template with parameters( custom tag + attributes) .
As the next to HTML abstraction layer - composition, it provides:
After composition the layer of functional component provides
While DCE is no-JS concept, DCE provides the basic declarative constructs to build most of simple apps. Assuming the extending via custom elements and JS. The evolution goal is to adopt most demanded APIs/construct natively into DCE stack over time.
DCE is compatible with closed/open/named root. Enabling as site-scoped styling and registry as encapsulated anonymous scopes in shadow root.
This project is a POC( Proof of Concept ) targeting to become a base for native DCE implementation polyfill.
Use the bootstrap project with all pre-configured or
use via CDN
<script type="module" src="https://unpkg.com/@epa-wg/[email protected]/custom-element.js"></script>
NPM, yarn
npm i -P @epa-wg/custom-element
yarn add @epa-wg/custom-element
slice
triggered by events<custom-element>
<input slice="typed"> //slice/typed : {//slice/typed}
</custom-element>
<custom-element>
<template>
<button slice="clickcount"
slice-event="click"
slice-value="//clickcount + 1" > + </button>
<input slice="clickcount" type="number" value="{//clickcount ?? 0}">
Click count: { //clickcount }
</template>
</custom-element>
More on slice
concept in slice and events demo page
comes from XSLT and XPath. Which is natively implemented in all current browsers, globally tested and well documented.
<custom-element tag="pokemon-tile" hidden>
<h3>{title}</h3> <!-- title is an attribute in instance
mapped into /*/attributes/title -->
<if test="//smile"> <!-- data-smile DCE instance attribute,
mapped into /*/dataset/smile
used in condition -->
<!-- data-smile DCE instance attribute, used as HTML -->
<div>Smile as: {//smile} </div>
</if>
<!-- image would not be visible in sandbox, see live demo -->
<img src="https://unpkg.com/[email protected]/sprites/pokemon/other/dream-world/{pokemon-id}.svg"
alt="{title} image"/>
<!-- image-src and title are DCE instance attributes,
mapped into /*/attributes/
used within output attribute via curly brackets -->
<!-- `slot name=xxx` replaced with elements with `slot=xxx` attribute -->
<p><slot name="description"><i>description is not available</i></slot></p>
</custom-element>
<pokemon-tile title="bulbasaur" data-smile="👼" pokemon-id="1" >
<p slot="description">Bulbasaur is a cute Pokémon born with a large seed firmly affixed to its back;
the seed grows in size as the Pokémon does.</p>
</pokemon-tile>
<pokemon-tile title="ninetales" pokemon-id="38" ></pokemon-tile>
generates HTML
<pokemon-tile title="bulbasaur" data-smile="👼"
image-src="https://unpkg.com/[email protected]/sprites/pokemon/other/dream-world/1.svg"
>
<h3>bulbasaur</h3>
<div>Smile as: 👼</div>
<img src="https://unpkg.com/[email protected]/sprites/pokemon/other/dream-world/1.svg" alt="bulbasaur">
<p>Bulbasaur is a cute Pokémon born with a large seed firmly affixed to its back;
the seed grows in size as the Pokémon does.</p>
</pokemon-tile>
<pokemon-tile title="ninetales"
image-src="https://unpkg.com/[email protected]/sprites/pokemon/other/dream-world/38.svg"
>
<h3>ninetales</h3>
<img src="https://unpkg.com/[email protected]/sprites/pokemon/other/dream-world/38.svg" alt="ninetales">
<p></p>
</pokemon-tile>
, look into sources for samples of CSS encapsulation and external template use.
custom-element
declarationtag
attributeNOTE: attempt to register custom element with already registered tag name would fail due to w3c standard limitations. The scoped custom element registry is still a proposal.
tag
leads to template instantiationWhether template is inline or given by src
attribute, the custom-element
would be instantiated inline if no tag
attribute is given.
constructor creates XML with
slot
attribute stay insideDOM content is replaced with results of instance XML transformation by declaration XSLT.
tag
attributeallows to define the Custom Element tag registered by window.customElements.define()
.
If omitted, the tag is auto-generated and the content is rendered inline.
<custom-element> my tag is {tag} </custom-element>
See demo for tag
attribute use.
src
attributeallows to refer either external template or template within external library by #id
hash in URL.
See demo with various samples.
datadom
is the XML payload for transformation. In order to be embedded into external document,#id
Local referenceallows to refer the template withing same document
url
allows to use the external document as template
url#id
allows to refer the template withing external document
Scoped CSS live demo
DCE can have the own styles which would be scoped to the instances.
In order to prevent the style leaking, it has to be defined withing template
tag:
<custom-element>
<template>
<style>
color: green;
button{ color: blue; }
</style>
<label> green <button>blue</button> </label>
</template>
</custom-element>
In same way as in DCE itself:
<custom-element tag="dce-2">
<template><!-- template needed to avoid styles leaking into global HTML -->
<style>
button{ border: 0.2rem dashed blue; }
</style>
<button><slot>Blue borders</slot></button>
</template>
</custom-element>
<dce-2>dashed blue</dce-2>
<dce-2>
<template> <!-- template needed to avoid styles leaking into global HTML -->
<style>button{border-color:red;}</style>
Red border
</template>
</dce-2>
To be served by IDE and to track the attributes changes, they have to be declared via attribute
:
<custom-element tag="dce-with-attrs" hidden>
<attribute name="p1" >default_P1 </attribute>
<attribute name="p2" select="'always_p2'" ></attribute>
<attribute name="p3" select="//p3 ?? 'def_P3' " ></attribute>
p1: {$p1} <br/> p2: {$p2} <br/> p3: {$p3}
</custom-element>
<dce-with-attrs p1="123" p3="qwe"></dce-with-attrs>
The curly braces {}
in attributes implemented as attribute value template
The names in curly braces are matching the instance attributes. I.e. in XML node /my-component/attributes/
.
To access payload XPath could start with /*/payload/
. I.e. {/*/payload//label}
refers to all label
tags in payload.
<slot name="xxx">
is replaced by payload top elements with slot
attribute matching the name,
i.e. slot xxx
is matching <i slot="xxx">...</i>
in payload.
<custom-element tag="with-description" >
<slot name="description">description is not available</slot>
<!-- same as
<value-of select='/*/payload/*[@slot="description"]'/>
-->
</custom-element>
<with-description>
<p slot="description">Bulbasaur is a cute Pokémon ...</p>
</with-description>
Loop implemented via for-each
is available in {}
in attributes, in for-each
, if
, value-of
, and other XSL tags.
XPath is a selector language to navigate over custom element instance data, attributes, and payload.
The in-browser native implementation as of now supports XSLT 1.0. File the change request for support of another XSLT version or template engine.
On many tags like table
, or link a
the attempt to use XSLT operations could lead to DOM order mismatch to given
in template. In such cases the xhtml:
prefix in front of troubled tag would solve the parsing.
<custom-element tag="dce-2" hidden>
<local-storage key="basket" slice="basket" live type="json"></local-storage>
<xhtml:table xmlns:xhtml="http://www.w3.org/1999/xhtml" >
<xhtml:tbody>
<for-each select="//basket/@*">
<xhtml:tr>
<xhtml:th> {name()} </xhtml:th>
<xhtml:td> {.} </xhtml:td>
</xhtml:tr>
</for-each>
</xhtml:tbody>
<xhtml:tfoot>
<xhtml:tr>
<xhtml:td><slot>🤔</slot></xhtml:td>
<xhtml:th> {sum(//slice/basket/@*)} </xhtml:th>
</xhtml:tr>
</xhtml:tfoot>
</xhtml:table>
</custom-element>
See demo source for detailed sample.
@epa-wg/custom-element plugin gives the view into
current
selected in DOM inspector node
Parent customElement
Declarative Custom Element dce
for custom element ^^
datadom
for easier inspection
xml
as a string
xslt
as a string
xml
and xslt
can be saved to file via for "copy string contents" into clipboard.
The XSLT debugger from your favorite IDE can set the breakpoints withing those files and run transformation under debugger.
{}
does not give a value<b title="{name(*)} : {text()}">xml tag name: <value-of select='name()'/></b>