Browse recipes

Preserve identity and choose form scope

Compare identity, guarded rendering and two form scopes in one interactive view.

Table of contents

APIs used

mount
Starts a Valyrian.js application on a DOM target.
FormStore
Coordinates form state, validation, transforms and submission.

Run this recipe

Starting project
Use the local TSX and ESM project from Stage 1 with Valyrian.js installed and its ESM browser build configured.
File
Replace src/client-entry.tsx with this recipe snippet.
Run
Run npm run build, serve public with npx --yes serve@14.2.5 public --listen 8000, open http://localhost:8000 and use the example's visible controls.
Observable result
The reordered rows keep their key values, the changed v-keep guard renders the selected profile and each field updates its chosen state object.

Starting point

Start with rows identified by stable ids, one selected profile, a local filter and a FormStore field.

Steps

Reverse the rows, select a different profile and edit each form through its matching state layer.

tsx
import { mount } from "valyrian.js";
import { FormStore } from "valyrian.js/forms";

type Row = { id: string; name: string };

const rows: Row[] = [
  { id: "arya", name: "Arya" },
  { id: "sam", name: "Sam" },
];
const localFilter = { query: "" };
const profileForm = new FormStore({
  state: { email: "" },
  schema: {
    type: "object",
    properties: { email: { type: "string", format: "email" } },
    required: ["email"],
  },
  onSubmit: async () => {},
});

const RenderingChoices = {
  selectedId: "arya",

  reverseRows() {
    rows.reverse();
  },

  selectProfile(id: string) {
    this.selectedId = id;
  },

  view() {
    const selected = rows.find((row) => row.id === this.selectedId) || rows[0];
    return (
      <main>
        <button onclick={() => this.reverseRows()}>Reverse rows</button>
        <ul v-for={rows}>
          {(row: Row) => (
            <li key={row.id}>
              <button onclick={() => this.selectProfile(row.id)}>
                {row.name}
              </button>
            </li>
          )}
        </ul>

        <section v-keep={this.selectedId}>
          Selected profile: {selected.name}
        </section>

        <label>
          Local filter
          <input name="query" v-model={localFilter} />
        </label>

        <form v-form={profileForm}>
          <label>
            Validated email
            <input name="email" type="email" v-field={profileForm} />
          </label>
          <button type="submit">Save profile</button>
        </form>
      </main>
    );
  },
};

mount("body", RenderingChoices);

Result

The reordered rows keep their key values, the changed v-keep guard renders the selected profile and each field updates its chosen state object.

Limits

Keep keys stable. Change a v-keep guard whenever the guarded subtree input must render again.

Continue