A component consists of a View, that is implemented as HTML template with its own CSS, and a Controller that is the Javascript code that controls the view, its presentation and the interaction logic.
Component's files are placed in the same path location and their base name is the same, and it represents itself the component's name:
[<path>/]<component_name>.html
[<path>/]<component_name>.css
(optional)[<path>/]<component_name>.js
The unique identifier of the component is therefore its full path, formed by the <path>
of the component plus the <component_name>
without any extension.
<component_id> :== [<path>/]<component_name>
Components can be loaded by adding the z-load
attribute to a host element, with its value containing a valid component identifier:
<div z-load="<component_id>"></div>
The <path>
of the component can be either relative to the page requesting the component, or an absolute path even if pointing to a different server. In the latter case, CORS must be enabled on the server end in order to allow fetching of components from the remote server.
If the <path>
is relative and not starting with a ".
", the lookup folder will be the application folder, which is by default "/app
".
For example, consider the following time-clock component with its files placed into a widgets folder:
/app/widgets/time-clock.html
/app/widgets/time-clock.css
/app/widgets/time-clock.js
in this case the <component_id>
is widgets/time-clock
, so the component can be loaded into the HTML page by using the following code:
<div z-load="widgets/time-clock"></div>
widgets/time-clock
...so, basically, the view template (time-clock.html + time-clock.css) is rendered inside the host element div
, and the controller code (time-clock.js) is activated and begins to animate the clock's digits.
Custom element
It is possible to define a custom element for a zuix.js component by using the zuix.loadComponent(..)
method:
customElements.define('component-name', class extends HTMLElement {
connectedCallback() {
zuix.loadComponent(this, 'path/of/component-name');
}
});
After defining a custom element, the component can be added to the page by using the custom element tag:
<component-name></component-name>
which has the same effect of using the z-load
attribute on a host element:
<div z-load="path/of/component-name"></div>
Defining a custom element for a Material Design button component.
customElements.define('mdl-button', class extends HTMLElement {
connectedCallback() {
zuix.loadComponent(this, '@lib/controllers/mdl-button', 'ctrl');
}
});
The third parameter <type>
can be supplied to the zuix.loadComponent(..)
in case we are loading only a controller (ctrl
) or only a view template (view
), rather than a complete component (.html + .css + .js).
Using the mdl-button
element in the page:
<mdl-button :class="'primary'">
Hello
</mdl-button>
<mdl-button :type="'flat'">
Web
</mdl-button>
<mdl-button :class="'accent'">
Components
</mdl-button>
Shadow DOM
With custom elements it is also possible to enable the shadow DOM.
zuix.js will auto-detect if a custom element is using shadow DOM when the attachShadow
method is called with the mode
option set to open
. If the mode
is otherwise set to closed
, then the shadow DOM must be passed explicitly as a container
with the zuix.loadComponent(..)
options as shown in the following example:
JS
customElements.define('time-clock', class extends HTMLElement {
connectedCallback() {
zuix.loadComponent(this, 'widgets/time-clock', '', {
container: this.attachShadow({mode: 'closed'})
});
}
});
HTML
<time-clock></time-clock>
Standalone components
Standalone components are components that can be loaded by just importing a single JavaScript module. They can be easily implemented by adding the following lines at the end of the controller code:
File: widgets/time-clock.js
(example)
// bottom of controller class code...
const customTag = 'time-clock';
const componentId = 'widgets/time-clock';
// register component class
zuix.controller(TimeClock, {componentId});
const setup = () => {
// Create custom element <time-clock></time-clock>
customElements.define(customTag, class extends HTMLElement {
connectedCallback() {
zuix.loadComponent(this, componentId);
}
});
};
// Load zuix.js if not already loaded
if (self.zuix === undefined) {
import('https://cdn.jsdelivr.net/npm/zuix-dist@1.1.29/js/zuix.module.min.js')
then(() => setup());
} else setup();
Then the component module can be imported in the head
section of the page
...
<head>
...
<script type="module" src="/app/widgets/time-clock.js"></script>
...
</head>
...
or using the import
statement, or the dynamic import()
function, inside another module
import "/app/widgets/time-clock.module.js"
then, the component, can be added inside the page's body
using the defined tag
<time-clock></time-clock>
Since the code above is already loading zuix.js
library with the import
at line #16, there's no need to include zuix.min.js
in the head
section of the page and so, the component can be loaded with just one line of of code.
The URL of the imported module can also be an absolute URL pointing to a different server.
Note that the additional code above, can also be implemented into a separate file, e.g. time-clock.module.js
, but the registration of component class must be removed (lines #4-5).
View-only component
It's also possible to load just a view without the controller and so consisting of the two files:
<component_id>.html
<component_id>.css
(optional)
To load a view, the type-attribute view
is added to the host element.
<div view z-load="<component_id>">
<!-- loaded view content will be rendered here -->
</div>
In the example below the view template named links is loaded using an absolute URL path:
<div view z-load="https://zuixjs.org/app/content/docs/examples/links"></div>
examples/links
...Controller-only component
A controller-only component, or simply controller, is basically a component whose view is the host element itself. To load a controller the type-attribute ctrl
is added to the host element.
<div ctrl z-load="<component_id>">
<!-- host element content -->
</div>
A controller consists only of a Javascript file:
<component_id>.js
In the following example the Gesture Helper controller is added to a div
container in order to detect swipe left and swipe right gestures over it.
<div ctrl z-load="@lib/controllers/gesture-helper"
(gesture:swipe)="handleSwipeGesture">
<strong>
Gesture detection area
</strong>
<p>
Try swiping left or right.
</p>
</div>
<script>
function handleSwipeGesture(e, tp) {
switch (tp.direction) {
case 'left':
this.playAnimation('animate__fadeOutLeft');
break;
case 'right':
this.playAnimation('animate__fadeOutRight');
break;
}
}
}
</script>
The default
component
Using the internal default
component it is possible to get advantage of zuix.js components' features also on standard HTML elements.
Adding the z-load="default"
attribute or any other z-*
attribute to a div
, will create a new component context where the div
is its view template, and where it will be possible to use all templating and scripting capabilities of zuix.js components:
<div z-model="{number: 42}">
<mdl-button :type="'fab'"
(pointerdown)="model.number--">
remove
</mdl-button>
<strong #number></strong>
<mdl-button :type="'fab'"
(pointerdown)="model.number++">
add
</mdl-button>
<!-- SCOPED CSS: `media="#"` attribute
will apply styles only to the enclosing
component context -->
<style media="#">
:host {text-align: center}
strong {margin: 12px}
</style>
</div>