Properties per state
Per-state property overrides let you change a widget’s appearance or behavior when one or more states are active, without duplicating the widget definition. The two paths express overrides differently:
- Programmatic — a
states: {}block on the widget itself, mapping state name to the props that change while that state is active. - JSON — a suffix on the prop name,
"<prop>.<stateName>": <value>. The base prop holds the default; each suffix supplies the override.
A complete example
Section titled “A complete example”A repeater accepts up to five users. Once the array reaches that limit a limitReached state activates, swapping the add button label.
The state lives in formConfig.states; the override sits in the repeater’s states block.
import { gui } from '@golemui/gui-shared';import { GuiForm } from '@golemui/gui-react';
const config = { formDef: [ gui.inputs.repeater('users', { addLabel: 'Add user', states: { limitReached: { addLabel: "Limit reached, you can't add more" }, }, children: [ gui.inputs.textInput('name', { label: 'Name' }), ], }), ], formConfig: { states: { limitReached: '$form.users?.length === 5' }, },};
export function MyForm() { return <GuiForm config={config} />;}import { Component } from '@angular/core';import { CommonModule } from '@angular/common';import { gui } from '@golemui/gui-shared';import { FormComponent } from '@golemui/gui-angular';
@Component({ imports: [CommonModule, FormComponent], selector: 'app-my-form', template: `<gui-form [config]="config"></gui-form>`,})export class MyForm { protected config = { formDef: [ gui.inputs.repeater('users', { addLabel: 'Add user', states: { limitReached: { addLabel: "Limit reached, you can't add more" }, }, children: [ gui.inputs.textInput('name', { label: 'Name' }), ], }), ], formConfig: { states: { limitReached: '$form.users?.length === 5' }, }, };}import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import { gui } from '@golemui/gui-shared';import '@golemui/gui-lit';
@customElement('my-form')export class MyForm extends LitElement { config = { formDef: [ gui.inputs.repeater('users', { addLabel: 'Add user', states: { limitReached: { addLabel: "Limit reached, you can't add more" }, }, children: [ gui.inputs.textInput('name', { label: 'Name' }), ], }), ], formConfig: { states: { limitReached: '$form.users?.length === 5' }, }, };
override createRenderRoot() { return this; }
override render() { return html`<gui-form .config=${this.config}></gui-form>`; }}<script setup lang="ts">import { gui } from '@golemui/gui-shared';import { GuiForm } from '@golemui/gui-vue';
const config = { formDef: [ gui.inputs.repeater('users', { addLabel: 'Add user', states: { limitReached: { addLabel: "Limit reached, you can't add more" }, }, children: [ gui.inputs.textInput('name', { label: 'Name' }), ], }), ], formConfig: { states: { limitReached: '$form.users?.length === 5' }, },};</script>
<template> <GuiForm :config="config" /></template>import '@golemui/gui-components/index.css';import '@golemui/gui-lit';import { gui } from '@golemui/gui-shared';
const form = document.getElementById('app-form');form.config = { formDef: [ gui.inputs.repeater('users', { addLabel: 'Add user', states: { limitReached: { addLabel: "Limit reached, you can't add more" }, }, children: [ gui.inputs.textInput('name', { label: 'Name' }), ], }), ], formConfig: { states: { limitReached: '$form.users?.length === 5' }, },};The state lives at the form root; the override is a "addLabel.limitReached" suffix on the repeater.
{ "states": { "limitReached": "$form.users?.length === 5" }, "form": [ { "kind": "input", "type": "repeater", "path": "users", "addLabel": "Add user", "addLabel.limitReached": "Limit reached, you can't add more", "children": [ { "kind": "input", "type": "textinput", "path": "name", "label": "Name" } ] } ]}Wire the parsed JSON into your form component the same way as any other JSON formDef:
import { GuiForm } from '@golemui/gui-react';import formDef from './my-form.json';
export function MyForm() { return <GuiForm config={{ formDef }} />;}import { Component } from '@angular/core';import { CommonModule } from '@angular/common';import { FormComponent } from '@golemui/gui-angular';import formDef from './my-form.json';
@Component({ imports: [CommonModule, FormComponent], selector: 'app-my-form', template: `<gui-form [config]="config"></gui-form>`,})export class MyForm { protected config = { formDef };}import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-lit';import formDef from './my-form.json';
@customElement('my-form')export class MyForm extends LitElement { config = { formDef };
override createRenderRoot() { return this; }
override render() { return html`<gui-form .config=${this.config}></gui-form>`; }}<script setup lang="ts">import { GuiForm } from '@golemui/gui-vue';import formDef from './my-form.json';
const config = { formDef };</script>
<template> <GuiForm :config="config" /></template>import '@golemui/gui-components/index.css';import '@golemui/gui-lit';import formDef from './my-form.json';
const form = document.getElementById('app-form');form.config = { formDef };When limitReached is active the add button label switches; when it’s inactive the default addLabel applies. You can override any prop the widget supports — labels, icons, disabled flags, even nested arrays.
Multiple overrides on the same widget
Section titled “Multiple overrides on the same widget”Stack as many per-state overrides as you need.
gui.actions.button('save', { label: 'Save', disabled: false, states: { busy: { label: 'Saving…', disabled: true }, success: { label: 'Saved!', disabled: true }, },});{ "kind": "action", "type": "button", "uid": "save", "label": "Save", "disabled": false, "label.busy": "Saving…", "disabled.busy": true, "label.success": "Saved!", "disabled.success": true}See also
Section titled “See also”- Inline
when— for one-off prop conditions without naming a state. - Composing states — combining multiple states with AND / OR semantics, including the colon-chain convention.