Skip to content

Control Flow #16

Description

@schmylan

HTML-based control flow.

The goal is to adhere as closely to the HTML spec as possible and leave all expressions and logic as undefined as possible so that it can vary depending on the implementing language. To accomplish this, it leans on mostly HTML-inspired markup language and pokes holes in various places for foreign logic to exist using Mustache-inspired curly braces {}.

Note

  • Xero doesn't need any suspense or fallback concepts since Xero handles those naturally.
  • This experiments with lowercase to feel more like a keyword than a class.
  • Syntax is made to feel closer to C#/JavaScript for familiarity.
  • Child Anders-arrow shouldn't be required.

SolidJS seems to be leading the pack in this department these days. Here's a quick reference: https://www.solidjs.com/docs/latest/api#control-flow

// SolidJS loop
<For each={state.list} fallback={<div>Loading...</div>}>
 {(item) => <div>{item}</div>}
</For>

// SolidJS show
<Show when={state.count > 0} fallback={<div>Loading...</div>}>
  <div>My Content</div>
</Show>

// SolidJS switch
<Switch fallback={<div>Not Found</div>}>
 <Match when={state.route === "home"}>
   <Home />
 </Match>
 <Match when={state.route === "settings"}>
   <Settings />
 </Match>
</Switch>

Loops

Giving the iterator's value a name is optional and will default to using item as the variable name.

<foreach in={{myLlist}}>
  <div>{{item.FirstName}}</div>
</foreach>

Deeply nested loops will require you to supply a name for the iterator's value.

<foreach contact in={{myContacts}}>
  <foreach address in={{contact.Addresses}}>
    <div>{{address.Zip}}</div>
  </foreach>
</foreach>

Conditionals

HTML is often seen with valueless attributes (like checked). If what's contained inside the {} evaluates to false, the contents are not included in the output.

<if {{count > 0}}>
  <div>My Content</div>
</if>

Avoid verbosity with a Swift-inspired if-let. Inner content is only included if the attribute value is non-null (or truthy). The attribute name can then be used in-scope to save on verbosity. If there are multiple attributes doing this, then each must be non-null (or truthy) to include its contents. The syntax used inside the intital {{}} (e.g. the elvis operator in this example) is outside the scope of this spec and depends on the syntax of the implementing language.

<if firstName={{user?.Profile?.Name?.First}}>
  <div>Hello {{firstName}}</div>
</if>
<else>
  <p>Loading...</p>
</else>

Maybe we can make use of the latest pattern matching features of C#? Basically only the first one to evaluate to true would be rendered. Initially this made use of the keyword switch but was replaced with if to avoid frequent naming collisions with a switch component commonly seen in mobile UI.

<if {{temp < 32}}>
  <Solid />
</if>
<else-if {{temp == 32}}>
  <Transition />
</else-if>
<else-if {{temp > 32}}>
  <Liquid />
</else-if>
<else>
  <Unknown />
</else>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions