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

Field Value Casting

As was discussed earlier, field values live in both Components (in JavaScript) and in the DOM and can by synced both ways. The DOM only stores strings (attributes and text), but components often want numbers, booleans, and arrays.

Qite handles this by keeping a typed value in this.fields and converting it when crossing the DOM boundary. In other words:

  • When Qite reads field values from the DOM, it casts strings into typed values.
  • When Qite writes field values back into the DOM, it casts typed values into strings.

Strings in DOM, typed values in JavaScript

Consider:

<div data-component="Counter" data-count="0"></div>

data-count="0" is a string in HTML. When Qite reads it into fields, it applies casting and stores count as a number in this.fields. Example:

this.fields.get("count");                      // => 0
this.fields.get("count", { as_string: true }); // => "0"

In other words, get() returns the typed value by default, while as_string returns the raw string representation used in the DOM.

Writing values and DOM serialization

When you set a field from JavaScript:

this.fields.set("count", 10);

Qite keeps the typed value 10 in the component state. But when it writes that value into DOM (which happens immediately) it converts it to a string using the configured casting rules.

Most of the time you don't have to think about the string form, but it's important to remember that DOM bindings always end up writing strings.

Customizing casting

Casting is configurable. Fields accepts two dictionaries:

  • cast_from_string determines how to convert values read from DOM into typed values
  • cast_to_string determines how to convert typed values into strings written to DOM

Each dictionary supports:

  • default function used for all fields
  • per-field overrides

A component may provide custom rules like this:

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

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

        this.fields.cast_from_string.price = (name, v) => Number.parseFloat(v);
        this.fields.cast_to_string.price   = (name, v) => v.toFixed(2);
    }
}

This lets you keep the DOM representation predictable while making the typed value convenient for code. But the built-in default caster already handles common cases:

  • integers and floats
  • true and false
  • empty strings as null
  • simple array syntax like [1,2,3]
  • special handling for the disabled="disabled" HTML-attribute.

You only need custom casting when the defaults do not match your needs. The rule of thumb here is that this should probably be very rare and defaults should work for you most of the time.

Why Qite casts instead of leaving strings

Qite could treat everything as strings and force you to parse values manually, but that tends to spread conversion logic across the codebase.

Instead, fields apply casting in one place and keep the typed value stable. Your component code reads this.fields.get("count") and gets a number. The DOM still shows something readable, and the conversion rules stay explicit.

Methods you will commonly use

  • this.fields.get(name, { as_string: true }) returns the raw string value.
  • this.fields.cast_from_string[name] = fn overrides parsing for a field.
  • this.fields.cast_to_string[name] = fn overrides serialization for a field.