Skip to main content

Components

Components are JavaScript modules that add behavior to Custom Element in HTML. They treat HTML as a data source to extract: Values, Information, Events, and Directives, which are then used to update the DOM.


Each component consists of four types of exported functionalities:

  • Controller Default Function. Required
  • Model Object. Required
  • View Function. Optional
  • Template Function. Optional

Controller Default Function

export default function myComponent({ ... }: Component)

Each controller runs for every instance of the registered Custom Elements on the page, providing a variety of built-in helpers. These helpers allow you to capture events, manipulate the DOM, update states, and more, streamlining the development process.

import type { Component } from 'jails-js/types'

export default function myComponent ({ main, elm, on, state }: Component) {

main(() => {
on('click', '[data-add]', add)
})

const add = () => {
state.set( s => s.counter += 1 )
}
}

Async Controller Default Function

You can also define it as async if you want to use await keyword before code execution.

import type { Component } from 'jails-js/types'

export default async function myComponent ({ main, elm, on, state }: Component) {

const data = await getSomething('/some/service')

main(() => {
on('click', '[data-add]', add)
})

const add = () => {
state.set( s => s.counter += 1 )
}
}
Caution

Keep in mind that when using async / await, everything below the await keyword will not execute until the promise is resolved.


Model Object

export const model: Model = {}

The model initializes the component's state while also serving as documentation, allowing other developers to understand which local states the component manages.

import type { Component, Model } from 'jails-js/types'

export default function myComponent ({ main, on, state }: Component) {

main(() => {
on('click', '[data-add]', add)
})

const add = () => {
state.set( counter => counter += 1 )
}
}

//Initial state is counter = 0
export const model: Model = {
counter : 0
}

You can also use the Function version which provides the HTMLElement element of the component so you can retrieve some data from the html such as dataset, attributes etc.

export const model = ({ elm, initialState }) : Model => {
// initialState is retrieved from : <my-component html-model="{name: 'my-name'}">
const counter = Number(elm.dataset.counter) // <my-component data-counter="10">
return {
counter
}
}

View Function

export const view: View = ( state: any ) => state : any

Sometimes, we need to enhance the data passed to our view (HTML) by formatting content based on the component's states. Instead of creating additional variables within the component's state, the View function allows us to override everything that will be sent as state variables. This approach avoids cluttering the state scope with extra variables that would otherwise persist over time.

import type { Component, Model, View } from 'jails-js/types'

export default function myComponent ({ main, on, state }: Component) {

main(() => {
on('click', '[data-add]', add)
})

const add = () => {
state.set( counter => counter += 1 )
}
}

//Initial state is counter = 11
export const model: Model = {
counter : 11
}

export const view: View = ( state ) => {
return {
...state,
biggerThen10 : state.counter > 10? 'bigger' : ''
}
}

The Html :

<my-component>
<h2>Counter</h2>
<div html-class="biggerThen10">{{ counter }}</div>
<button data-add>+</button>
</my-component>

The Result:

<my-component>
<h2>Counter</h2>
<div class="bigger">11</div>
<button data-add>+</button>
</my-component>

In the code above, we see that with the View function, we can create new variables specifically for the HTML view's scope. This allows us to introduce additional variables to simplify usage within the HTML context without actually creating new state variables. The state remains solely with counter variable.

Template Function

export const template: Template = ({ elm: HTMLElement, children: string }) => string

Components in Jails are designed to enhance already-rendered HTML, making them particularly suited for SSR (Server-Side Rendering) or SSG (Static Site Generation) strategies. However, there are scenarios where we may want to export our functionalities as complete, self-contained web components. This allows other teams to simply import these components, with the HTML embedded and all functionality ready to use.

The template function handles the rendering of the component's HTML in an embedded manner, providing a seamless, out-of-the-box experience.

import type { Component, Model, Template } from 'jails-js/types'
import { html, attributes } from 'jails-js/html'

export default function myComponent ({ main, on, state }: Component) {

main(() => {
on('click', '[data-add]', add)
})

const add = () => {
state.set( counter => counter += 1 )
}
}

//Initial state is counter = 11
export const model: Model = {
counter : 0
}

export const template: Template = ({ elm, children }) => {
return html`
${children}
<div class="bigger">{{counter}}</div>
<button data-add ${attributes({ id: 'my-button-id' })}>+</button>
`
}

The Html:

<my-component>
<h2>Counter</h2>
</my-component>

Result would be:

  <my-component>
<h2>Counter</h2>
<div>0</div>
<button data-add id="my-button-id">+</button>
</my-component>