Component

A component, here intended as a reusable part of a web page, 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 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:

The unique component's identifier is then its full path, formed by the component's <path> 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.

As an example, consider this time-clock component with its view and controller files placed inside a widgets folder:

the <component_id> is widgets/time-clock, and the component can be loaded using the following code in the HTML page:

<div z-load="widgets/time-clock"></div>
Loading widgets/time-clock...

so, basically, the component's view template's files (time-clock.html + time-clock.css) are 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 primary>
Hello
</mdl-button>
<mdl-button>
Web
</mdl-button>
<mdl-button accent>
Components
</mdl-button>

HelloWebComponents

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, if 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 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>

Stand-alone components

Stand-alone components can be easily implemented as JavaScript modules. The name of the module will have the same base name of the widget but ending with .module.js.

File: widgets/time-clock.module.js

import 'https://cdn.jsdelivr.net/npm/zuix-dist/js/zuix.module.min.js';
customElements.define('time-clock', class extends HTMLElement {
connectedCallback() {
zuix.loadComponent(this, 'widgets/time-clock');
}
});

Then the component module can be imported in the head section of the page:

...
<head>
...
<script type="module">
import '/app/widgets/time-clock.module.js';
// any additional imports and application code
// might be placed here as well
</script>
...
</head>
...

and it can be loaded in the page's body using the defined tag:

<time-clock></time-clock>

Since the component module is already loading zuix.js library with the import at line #1, 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 JavaScript import and the relative HTML tag.
The URL of the component's module can also be an absolute URL pointing to a different server.

try Example on CodePen

View-only component

It's also possible to load just a view without the controller and so consisting of the two files:

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>

Loading component 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:

In the following example a 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"
z-options="opts">

<strong>Gesture detection area</strong>
<p>Try swiping left or right.</p>
</div>
<script>
opts = {
on: {
'gesture:swipe': function(e, tp) {
switch(tp.direction) {
case 'left':
this.playAnimation('animate__fadeOutLeft');
break;
case 'right':
this.playAnimation('animate__fadeOutRight');
break;
}
}
}
}
</script>

Gesture detection area

Try swiping left or right.

Options

In the previous example the z-options attribute was used to provide a handler for events emitted by the gesture controller ("gesture:swipe" event).

z-options attribute is used to provide options for a component. The value of this attribute can be the name of a variable in the global scope that stores the options object, or it can be an inline JSON string of the options object.

Event handlers

The on field can be provided in component's options to register handlers for DOM events and custom events emitted by the component.

<div z-load="my_component" z-options="opts"></div>
<script>
opts = {
// Event handlers
on: {
// custom lifecycle events (created with "cp.trigger(..)")
'my_component:event_1': function(e, data) {
// handle event
},
'my_component:event_2': function(e, data) {
// handle event
},
// standard lifecycle events
'view:attach': function(e, element) {
// the view has been attached to the host element
},
'component:loaded': function(e, element) {
// the component has been loaded, but it might
// be waiting for other dependencies before starting
},
'component:ready': function(e, element) {
// the component is now ready and fully operational
},
// standard DOM events
'mouseout': function(e) {
// component's view mouse-out event
},
'click': function(e) {
// click over component's view
},
// ...
},
// Loader events callbacks
ready: function(ctx) { // same as 'component:ready' event
// ctx is the {ComponentContext} object
// associated to the loaded component
console.log('Loaded ' + ctx.componentId + ' with contextId = ' + ctx.contextId);
},
error: function(err) {
// component loading error
console.log('Oh-oh!', err);
}
}
</script>

the ready and error option fields can be used to provide callbacks for handling component's loading events.
Event handlers can also be directly provided using the z-on attribute.

View's styles

The css option flag can be used for disabling the loading of the view's style file. This is how the previous banner example would look like without CSS file:

<!-- 'links.css' file will not be loaded -->
<div view z-load="examples/links"
z-options="{ css: false }">
</div>

and it can also be used to provide a custom CSS style for the view instance:

<!-- 'links.css' file will not be loaded -->
<div view z-load="examples/links"
z-options="my_test_options">
</div>
<script>
my_test_options = {
css: `
:host {
background-color: whitesmoke;
padding: 8px;
}
a {
color: darkgreen;
}
`

}
</script>

Priority

The priority option sets what the components' loading order will be. Its value is a number that can also have negative values. The default value is zero and items with lesser value will get loaded first.

Loading priority can also be set directly on the host element tag through the z-priority attribute.

try Example on CodePen

Lazy loading

If many components are placed on a page, the lazy-loading feature can be enabled in order to load components only if they are visible in the page's viewport. This will speed up page booting time and increase responsiveness.

Lazy-loading can be automatic or manual.

Automatic — to automatically load components as they scroll into view, set the attribute z-lazy="scroll" on the body element or the element that is actually hosting the scrollbars.

The attribute z-lazy="false" can be added on those child elements that we want to be loaded right away even if they are not in the viewport area yet.

<div class="vertical-scrollable" z-lazy="scroll">
<!-- the first view will be loaded right away
because of the 'z-lazy=false' option -->

<div view z-load="content/preface" z-lazy="false"></div>
<!-- other views inside the 'z-lazy' scroll container will
be loaded only as the user scrolls the page down -->

<div view z-load="content/chapter_01"></div>
<div view z-load="content/chapter_02"></div>
<!-- ... --->
<div view z-load="content/chapter_12"></div>
<div view z-load="content/appendix"></div>
</div>
try Example on CodePen

Manual — to manually load components that are actually visible in the viewport, set the z-lazy="true" on the container where lazy-loaded elements are hosted. Then zuix.componentize() method must be called in order to check for visible components to load.

zuix.componentize(host_element);

Lazy loading can also be set in the component's options using the lazyLoad option field.

With the zuix.lazyLoad(false) method is possible to disable lazy loading at a global level, and a following zuix.componentize() call, will make all components to be loaded in one shot.

try Example on CodePen

Other options

These described so far are the more commonly used options. A list of all available option fields is provided in the ContextOptions API.

The default component

By using the internal default component, it is possible to get advantage of zuix.js components' features, also on standard HTML elements.

<div z-load="default" z-options="{ ready: onSimpleDivReady }">
I am a simple <code>div</code> element...
</div>

<script>
function onSimpleDivReady(ctx) {
const msg = ' and now also a <code>zuix.js</code> component!';
ctx.$.append(msg).addClass('animate__animated')
.playAnimation({
classes: 'animate__bounce',
options: {
delay: '1s',
duration: '1s',
'iteration-count': 5
}
});
}
</script>

I am a simple div element...

The z-load="default" attribute can be omitted if any of the other z- attributes are in place (z-behavior, z-on, z-options, z-model).

<div z-options="{ ready: onSimpleDivReady }">
I am a simple <code>div</code> element...
</div>
// ...

Libraries and theming

zuix.js has an internal session store used to store zuix.js configuration, that can also be used to store any other custom data.

// to store a session object
zuix.store('<object_name>', <object_value>);
// to get a session object
const myStoreObject = zuix.store('<object_name>');

The store named 'config' is reserved for zuix.js configuration data, and it has the following format:

zuix.store('config', {
// default values
"resourcePath": "/app/",
"libraryPath": {
"@lib": "https://zuixjs.github.io/zkit/lib/",
"@hgui": "https://genielabs.github.io/homegenie-web-ui/app/",
"@cdnjs": "https://cdnjs.cloudflare.com/ajax/libs/"
},
// override default config for a given <host_name>
"my_repo.github.io": {
"resourcePath": "/my_repo/app/",
"libraryPath": {
"@lib": "https://zuixjs.github.io/zkit/lib/",
"@hgui": "https://genielabs.github.io/homegenie-web-ui/app/",
"@cdnjs": "https://cdnjs.cloudflare.com/ajax/libs/"
}
}
});

where resourcePath is the default base path when loading components from a relative location, and libraryPath is a list of shortcuts that can be used as prefix in component's <path> to load components from different locations.

This is how the @lib prefix was used in the controller example, as a shortcut to ZKit component's library.

This way it's also possible to change a components' set by just changing those paths to another location where components preserve the same files and folders structure.

So, theming become not just a matter of changing styles and graphics, but also a whole set of components and functionality could be customized for a different theme by just pointing to a different library path in zuix.js configuration store.

Getting started
View
GitHub logo
JavaScript library for component-based websites and applications.