Clone on Gitea
Introduction
Explained by ducks
FAQ
Getting Started
Components
Component Basics
Auto Initialization From DOM
Templates
Children and Parents
Roles
Strictness
Fields
What is a Field
Binding Fields to DOM
Reading and Writing Fields
Value Casting
Flags
Events
Overview
Event Handlers
DOM Events
Custom Events
Propagation and Flow
Advanced Event Control
Beyond Components
States
Overview
StateEngine
DisplayStateEngine
Field Matchers
Ordering and priority
Debugging
Advanced usage
Ajax
ComponentUI
VirtualForm
Unit Testing
Helpers
Overview
Cookies
Arrays

Templates

When you need to create new components after the page is loaded you use templates. Templates allow you to define component markup in HTML and instantiate it dynamically when needed.

Qite does not use render functions — it instead uses native HTML <template> elements. The browser already provides <template> as a safe, inert container for markup. Its content is not rendered until cloned. Qite builds on that instead of inventing its own rendering abstraction.

When to use templates

Templates are very useful when you don't know ahead of time how many instances of a particular component you might need on your page as time passes and things change. For instance, you might want to dynamically add form fields (which you decided are going to be instances of TextInputComponent class) if user, say, needs to add more delivery addresses into his profile.

A good way to handle this would be to have an "Add address" button which, when clicked, creates a new TextInputComponent using a template.

Defining the address template

You would first define a template in your HTML:

<template 
  data-component="TextInput"
  data-tag-name="div"
  data-template-name="default"
>
    <label data-field="label"></label>
    <input data-part="value" name="" value=""/>
    <div class="hint" data-part="hint"></div>
    <div class="errors" data-field="validation_errors"></div>
</template>

This template describes how a TextInputComponent should be constructed. Nothing inside it is rendered immediately because <template> content is inert by default.

Instead of wrapping the component in a <div data-component="TextInput">, Qite reads data-component, data-tag-name and data-template-name directly from the <template> tag and constructs the root element for you.

  • data-component="TextInput" tells Qite which component class to instantiate.
  • data-tag-name="div" defines which HTML tag should be created.
  • data-template-name="default" gives this template a name.

When this template is used, Qite creates a <div data-component="TextInput"> element, inserts the template content inside it, scans it and instantiates a TextInputComponent

All standard Qite components have an .html file in their directory which contains the expected HTML structure. For TextInputComponent, check src/components/text_input/text_input.html. You can copy that structure and move it inside <template> tags.

Default vs named templates

A component may have more than one template.

The data-template-name attribute allows you to define multiple variations:

<template 
  data-component="TextInput"
  data-tag-name="div"
  data-template-name="default"
>
    <!-- default layout -->
</template>

<template 
  data-component="TextInput"
  data-tag-name="div"
  data-template-name="compact"
>
    <!-- compact layout -->
</template>

The template with name "default" is used automatically when no specific template is requested.

To explicitly choose a template:

TextInputComponent.create({
    parent: this,
    template_name: "compact"
});

If you omit template_name, Qite uses "default"

Setting fields and flags

When calling create() you can also pass fields and flags to be set like this:

TextInputComponent.create({
    parent: this,
    fields: { name: "value" },
    flags:  ["flag_1", "!flag_2"]
});

Both fields and flags will be set before component is attached to the parent.

Flags "flag_1" and "flag_2" must be added to the component beforehand. If the component uses strict fields, the "name" field must exist as well. Otherwise, an appropriate exception will be thrown. Learn more on the "Flags" and "Fields" pages.

Adding address fields dynamically

Now suppose you have a parent component responsible for managing a list of delivery addresses.

import BaseComponent from "/assets/vendor/qite/src/components/base_component.js";
import TextInputComponent from "/assets/vendor/qite/src/components/text_input/text_input_component.js";

export default class DeliveryComponent extends BaseComponent {
    constructor(tree_node) {
        super(tree_node);

        this.events.add([
            ["click", "add_address", () => this.addAddress() ],
        ]);
    }

    addAddress() {
        TextInputComponent.create({
            parent: this,
            template_name: "default"
        });
    }
}

Qite.components.Delivery = DeliveryComponent;

And in HTML:

<div data-component="Delivery">
    <button data-component="Button" data-roles="add_address">
      Add address
    </button>
</div>

When the user clicks the button, addAddress() is called. TextInputComponent.create(...) then:

  1. Finds the <template> matching data-component="TextInput" and data-template-name="default"
  2. Creates a new root element with data-tag-name
  3. Inserts template content inside it.
  4. Appends it to the parent.
  5. Scans that subtree.
  6. Instantiates a new TextInputComponent
  7. Links it into the component hierarchy.

The newly created component behaves exactly like one that was present at page load.

Hierarchy and wiring

When you pass parent: this to create()

  • The new component is appended to this.children
  • Its parent property is set.
  • Event subscriptions are wired.
  • Its mounted promise is resolved after hierarchy attachment.

There is no special case for template-created components. They become first-class components in the tree.

Templates keep HTML in HTML

With this approach:

  • HTML structure lives in HTML.
  • Component behavior lives in JavaScript.
  • Styling lives in CSS.

You are not building HTML strings, not mixing markup inside JavaScript, and not re-rendering entire blocks. Qite clones structured DOM and attaches behavior to it.

Creating a component from a template triggers scanning only for the cloned subtree. It does not rescan the entire document.

What to read next

Now that you understand how to create components dynamically, the next page explains how parent and child components relate to each other in more detail.