Documentation

Report Edit

Inner Layouts

Inner layouts define how widget's children are laid out. If no layout is specified, children are rendered in the same order as how they are defined in the widget tree.

Inner layouts are set using the layout attribute.

Default Layout (No Layout)

In this layout, no special arrangement on the children is made. Elements are laid out in the same order as they are specified.

Some widgets consist of multiple parts, such as form fields. If there is no layout, the parts will be laid down in the same order how they are defined in the render method. For a form field, the label will be displayed first and the input will follow.

First some text.
Index
<div trimWhitespace={false}>
    First some text.
    <TextField value-bind="$page.text" label="Label 1" />
    <Checkbox value-bind="$page.check" label="Label 2">Checkbox</Checkbox>
</div>
Copied!Cx Fiddle

This layout does not work very well for form elements.

LabelsLeftLayout

import { LabelsLeftLayout } from 'cx/ui';

LabelsLeftLayout is used to get horizontal form layouts. In this layout all children content is rendered inside a table where labels go into the first column, while inputs and other content go into the second column.

First some text.

The LabeledContainer control can be used when multiple widgets need to be placed on the right side.

Index
<div layout={LabelsLeftLayout}>
    First some text.
    <TextField value-bind="$page.text" label="Label 1" />
    <Checkbox value-bind="$page.check" label="Label 2">Checkbox</Checkbox>
    <LabeledContainer label="Label 3" trimWhitespace={false}>
        <TextField value-bind="$page.text" />
        <TextField value-bind="$page.text" />
    </LabeledContainer>
</div>
Copied!Cx Fiddle
import { LabelsTopLayout, LabelsTopLayoutCell } from 'cx/ui';

LabelsTopLayout is used for dense forms with very long labels or when labels are put on top in order to save some space.

This layout is also implemented using an HTML table element with two rows. The first row contains all of the labels while the second row contains inputs along with other content. When labels are too long they need to be broken down into multiple lines. To preserve the visual layout, labels are bottom aligned.

Set the width style on form fields to align inputs properly.

Again, you may use the LabeledContainer control to group multiple widgets under a single label.

Index
<div>
    <div layout={LabelsTopLayout}>
        <Select value-bind="$page.title" label="Title" style={{width: "70px"}}>
            <option value="Mr">Mr.</option>
            <option value="Mrs">Mrs.</option>
        </Select>
        <TextField value-bind="$page.firstName" label="Name" placeholder="First Name" style={{width: '150px'}} />
        <TextField value-bind="$page.lastName" placeholder="Last Name" style={{width: '150px'}}/>
    </div>
    <div layout={LabelsTopLayout}>
        <TextField value-bind="$page.street" label="Address" placeholder="Street" style={{width: '150px'}} />
        <TextField value-bind="$page.city" placeholder="City" style={{width: '150px'}}/>
        <TextField value-bind="$page.zip" placeholder="Zip" style={{width: '70px'}}/>
    </div>
</div>
Copied!Cx Fiddle

Vertical Mode

LabelsTopLayout can also be used in vertical mode where each field takes one row.

Index
<div layout={{ type: LabelsTopLayout, vertical: true }}>
    <Select value-bind="$page.title" label="Title" style={{width: "70px"}}>
        <option value="Mr">Mr.</option>
        <option value="Mrs">Mrs.</option>
    </Select>
    <TextField value-bind="$page.firstName" label="Name" placeholder="First Name"
        style={{width: '150px'}}/>
    <TextField value-bind="$page.lastName" placeholder="Last Name" style={{width: '150px'}}/>
</div>
Copied!Cx Fiddle

Multiple Columns

LabelsTopLayout can render fields in multiple columns.

Index
<div layout={{type: LabelsTopLayout, columns: 3}}>
    <TextField label="Field1" value-bind="$page.field1" />
    <TextField label="Field2" value-bind="$page.field2" />
    <TextField label="Field3" value-bind="$page.field3" />
    <TextField label="Field4" value-bind="$page.field4" />
    <TextField label="Field5" value-bind="$page.field5" />
    <TextField label="Field6" value-bind="$page.field6" />
    <TextField label="Field7" value-bind="$page.field7" />
    <TextField label="Field8" value-bind="$page.field8" />
    <TextField label="Field9" value-bind="$page.field9" />
</div>
Copied!Cx Fiddle

Use LabelsTopLayoutCell to achieve complex layouts in which some fields span across multiple columns or rows.

Index
<div class="widgets">
    <LabelsTopLayout columns={2} mod="fixed" style="width: 300px">
        <TextField label="Field1" value-bind="$page.field1" style="width: 100%"/>
        <LabelsTopLayoutCell rowSpan={2} style="padding-left: 16px">
            <LabeledContainer label="Field2">
                <Radio value-bind="$page.field2" option={1}>Option 1</Radio>
                <Radio value-bind="$page.field2" option={2}>Option 2</Radio>
                <Radio value-bind="$page.field2" option={3}>Option 3</Radio>
            </LabeledContainer>
        </LabelsTopLayoutCell>
        <TextField label="Field3" value-bind="$page.field3" style="width: 100%"/>
        <LabelsTopLayoutCell colSpan={2}>
            <TextArea label="Field4" value-bind="$page.field8" style="width: 100%" rows={5}/>
        </LabelsTopLayoutCell>
        <TextField label="Field5" value-bind="$page.field5" style="width: 100%"/>
        <TextField label="Field6" value-bind="$page.field6" style="width: 100%"/>
    </LabelsTopLayout>
</div>
Copied!Cx Fiddle
import { FirstVisibleChildLayout } from 'cx/ui';

The FirstVisibleChildLayout is used when you want to display a single child at a time. Think of this layout as a complex if ... else ... statement.

The following example shows how to use this layout for an asynchronous loading operation.

Data not loaded yet.
ControllerIndex
class FetchController extends Controller {
    fetch() {
        this.store.set('$page.fetch.status', 'LOADING');
        setTimeout(() => {
            if (Math.random() > 0.5) {
            this.store.set('$page.fetch.status', 'SUCCESS');
            this.store.set('$page.fetch.result', Math.random() * 100);
            } else {
            this.store.set('$page.fetch.status', 'ERROR');
            }
        }, 1000);
    }
}
Copied!Cx Fiddle

Initially, page.fetch.status is undefined, so the first three div elements will not be visible and the default message will appear.

After you click Fetch, the default message disappears and the loading message will display until the result is fetched. The default message will disappear even if it does not have visible-expr set. That happens because the layout stops processing content after the first child is rendered, in this case it being the loading message div.

UseParentLayout

import { UseParentLayout } from 'cx/ui';

UseParentLayout is not considered a real layout. It is an instruction that the widget should use parent's layout instead of its own. UseParentLayout can be used only on widgets which render only its children and do not add any markup (pure containers). UseParentLayout is commonly used with LabelsTopLayout or LabelsLeftLayout as parent layouts.

Index
<div layout={LabelsLeftLayout}>
    <Checkbox value-bind="$page.showSection1">Section 1</Checkbox>
    <PureContainer layout={UseParentLayout} visible-bind="$page.showSection1">
        <TextField value-bind="$page.text" label="Label 1"/>
        <TextField value-bind="$page.text" label="Label 2"/>
        <Checkbox value-bind="$page.showSection2">Section 2</Checkbox>
        <PureContainer layout={UseParentLayout} visible-bind="$page.showSection2">
            <TextField value-bind="$page.text" label="Label 3"/>
            <TextField value-bind="$page.text" label="Label 4"/>
        </PureContainer>
    </PureContainer>
</div>
Copied!Cx Fiddle

Notice how the widget tree being deeply nested still renders as a simple list of elements that share the same layout.

Outer Layouts

For defining global application layouts, check out the Outer Layouts page.