Components

This article describes the handling of components in batonjs and the defineComponent function.

Component handling in batonjs

Batonjs has no special mechanism for defining components and exclusively uses web components.
You can use external web components, or you can make use of batonjs to create web components.
Especially for easy creation of components that do not have internal state, batonjs provides the defineComponent function.

The following is a summary of how to use components in batonjs.

  • When you already have a web component that you want to use
    Use its web component.
  • When you want to create a component that has no internal state
    You can easily create it with the following defineComponent.
  • When you want to create a component with internal state
    Create web components from scratch. You can also use batonjs for internal state management.

defineComponent

This function defines a web component using the batonjs mechanism. In batonjs, a web component created with this function is called a "baton component".
A baton component cannot have internal state, and is purely functional, changing only depending on the given attributes.
Also, from the web standards perspective, a baton component is a custom element that does not have a shadow DOM.

Example
defineComponent(
  'aa-field', 
  `<div class="head">
    <label></label>
    <p class="error-message"></p>
  </div>
  <div class="body">
    <slot></slot>
    <p class="description"></p>
  </div>`, 
  state => ({
    ".head label": {textContent: state.label}, 
    ".head .error-message": {"style-display": state["error-message"] ? "block" : "none", textContent: state["error-message"]}, 
    ".body .description": {textContent: state.description}
  })
)
Parameters
name type default description
name string mandatory Tag name of the custom element
template string | HTMLTemplateElement | function mandatory A template for the content of the component. It corresponds to the innerHTML property of this DOM element, and multiple elements (DocumentFragment) are also possible.
If it is a string, it is interpreted as an HTML string.
If it is a template element, its content is cloned.
If it is a function, it is called.
show function mandatory A function that defines the dynamic part of the component.
Same as show passed to the baton function, but the state is the list of attributes being monitored.
options object {} Same as the third argument of customElements.define
options.extends string N/A ame as the third argument of customElements.define. It is used to define custom built-in elements.
observedAttributes Array|null null A list of attribute names that will be set in the observedAttributes property of the custom element. The attributes listed here will be monitored and the component will be redrawn when the attribute value changes.
If omitted or given null, this function will auto-detect. Note that there are limits to automatic detection (see below).
Return Value

none

Mechanism of defineComponent

Components created with defineComponent work in the same way as baton applications.
Comparing the baton component with the baton application will help you understand the difference.

Comparison items baton application baton component
state Page state. It changes according to user operations, etc. Attribute list for custom elements. It will change as the attribute values change.
show function A UI declaration that originates from an entire HTML document or from a DOM element specified by baseEl. A UI declaration originating from this custom element.
Target HTML The entire HTML document or the DOM element specified in baseEl. A cloned version of the template given in the second argument of the defineComponent function. That will be the content of this custom element.
Slot

The contents of a component are given through two paths. From the component creator as the second argument of defineComponent (the template parameter) and from the component user as a child element of a custom element.

Normally, the children of the custom element are deleted and overwritten by the defineComponent template.
However, if the template contains a slot element, the slot element will be overwritten with the old content (i.e., the custom element's child elements).
This means that when creating a component, if you want to receive child elements from the outside, you can write a slot element in the child element's position and it will be inserted there.

Limitations of automatic detection of attributes

The fifth argument of defineComponent, observedAttributes, is a list of observed attributes of the component, and the defineComponent function can automatically detect these attributes.
Automatic detection is performed by examining the third argument, show, as a string with the toString function, but requires that the javascript code be in a certain form.
Here is a list of what you can and cannot do with the show function.

You can use function expressions with the function keyword. function (state) {...}
You can use arrow function expressions. state => ...
The number of function parameters cannot be other than 1. (state, otherArg) => ...
You can freely name the function parameter. anyName => ...
Destructuring assignment of function parameters cannot be used. ({a, b, c}) => ...
You cannot assign a function parameter as-is to another variable. anyName => {var x = anyName; ...}
You can access the properties of function parameters using the dot notation. state => ({foo: state.foo, ...})
You can access the properties of function parameters using bracketed notation. state => ({foo: state["my-foo"], bar: state['my-bar'], ...})
You cannot use non-literal property names when accessing function parameter properties in bracketed notation. state => ({foo: state[someVariable], bar: state['my' + '-bar'], ...})

The implementation of converting function objects to strings may change as browsers evolve. Even if it works properly now, it may not work sometime in the future.
Please use it after careful consideration, weighing its convenience against its maintainability.