Yayaw
Documentation
Modifier et agir

Sélecteur par tableau

Choisir des fiches liées dans un formulaire.

Utilisez tablePicker lorsqu’une liste ne donne pas assez d’informations pour identifier une relation. Le tableau imbriqué possède son catalogue et ses identifiants stables.

Exemple de configuration

Partez du démarrage React ou du démarrage Vue. Le fichier suivant complète leur catalogue de produits. Passez cette configuration via getTableConfig en React ou config en Vue.

order-form.ts
import type { FormConfig } from "@/components/ui/yayaw-table/components/forms/types";
import { customers, orderSchema } from "../shared/orders";
import { customerConfig } from "./order-config";
import { renderProductSelect } from "./product-select";

export const orderForm: FormConfig = {
  id: "orders",
  title: "Edit order",
  presentation: "modal",
  width: "64rem",
  schema: orderSchema,
  fields: [
    { name: "reference", label: "Reference", type: "text", bulkEdit: false },
    {
      name: "customerId",
      label: "Customer",
      type: "tablePicker",
      tablePicker: {
        tableType: "customers",
        config: customerConfig,
        data: customers,
        getRowId: (row) => String(row.id),
        multiple: false,
        syncUrl: false,
        maxHeight: "18rem",
      },
    },
    {
      name: "lines",
      label: "Order lines",
      type: "collection",
      collectionMode: "dialog",
      addLabel: "Add line",
      getItemKey: (line) => String(line.id),
      createItem: () => ({
        id: crypto.randomUUID(),
        productId: "",
        quantity: 1,
        allocations: [],
      }),
      columns: [
        { id: "productId", header: "Product" },
        { id: "quantity", header: "Quantity" },
      ],
      validateItem: (line) =>
        Number(line.quantity) > 0 && line.productId
          ? []
          : ["Choose a product and a positive quantity."],
      validateItems: (lines) =>
        lines.length ? [] : ["Add at least one line."],
      itemFields: [
        {
          name: "productId",
          label: "Product",
          type: "custom",
          renderField: renderProductSelect,
        },
        {
          name: "quantity",
          label: "Quantity",
          type: "number",
          min: 1,
          step: 1,
        },
        {
          name: "allocations",
          label: "Warehouse allocations",
          type: "collection",
          collectionMode: "inline",
          getItemKey: (allocation) => String(allocation.id),
          createItem: () => ({
            id: crypto.randomUUID(),
            warehouse: "",
            quantity: 1,
          }),
          itemFields: [
            { name: "warehouse", label: "Warehouse", type: "text" },
            { name: "quantity", label: "Quantity", type: "number", min: 1 },
          ],
        },
      ],
    },
  ],
};
order-form.ts
import type { FormConfig } from "@/components/ui/yayaw-table-vue/types";
import { customers, orderSchema } from "../shared/orders";
import { products } from "../shared/products";
import { customerConfig } from "./order-config";

export const orderForm: FormConfig = {
  id: "orders",
  title: "Edit order",
  presentation: "modal",
  width: "64rem",
  schema: orderSchema,
  fields: [
    { name: "reference", label: "Reference", type: "text", bulkEdit: false },
    {
      name: "customerId",
      label: "Customer",
      type: "tablePicker",
      tablePicker: {
        tableType: "customers",
        config: customerConfig,
        data: customers,
        getRowId: (row) => String(row.id),
        multiple: false,
        syncUrl: false,
        maxHeight: "18rem",
      },
    },
    {
      name: "lines",
      label: "Order lines",
      type: "collection",
      collectionMode: "dialog",
      addLabel: "Add line",
      getItemKey: (line) => String(line.id),
      createItem: () => ({
        id: crypto.randomUUID(),
        productId: "",
        quantity: 1,
        allocations: [],
      }),
      columns: [
        { id: "productId", header: "Product" },
        { id: "quantity", header: "Quantity" },
      ],
      validateItem: (line) =>
        Number(line.quantity) > 0 && line.productId
          ? []
          : ["Choose a product and a positive quantity."],
      validateItems: (lines) =>
        lines.length ? [] : ["Add at least one line."],
      itemFields: [
        {
          name: "productId",
          label: "Product",
          type: "select",
          options: products.map((product) => ({
            value: product.id,
            label: product.name,
          })),
        },
        {
          name: "quantity",
          label: "Quantity",
          type: "number",
          min: 1,
          step: 1,
        },
        {
          name: "allocations",
          label: "Warehouse allocations",
          type: "collection",
          collectionMode: "inline",
          getItemKey: (allocation) => String(allocation.id),
          createItem: () => ({
            id: crypto.randomUUID(),
            warehouse: "",
            quantity: 1,
          }),
          itemFields: [
            { name: "warehouse", label: "Warehouse", type: "text" },
            { name: "quantity", label: "Quantity", type: "number", min: 1 },
          ],
        },
      ],
    },
  ],
};

Fournir le catalogue imbriqué

La recette commande fournit customerConfig, les clients, les actions et le composant d’entrée utilisés ci-dessus. data fournit une liste locale ; actions.list peut charger les choix distants. Gardez syncUrl: false pour ne pas remplacer la requête de la page principale en choisissant une relation.

Identifiants et mode de sélection

getRowId doit produire une chaîne stable. parseValue la convertit en valeur stockée si nécessaire. Utilisez multiple: false pour une relation scalaire et multiple: true pour un tableau. Respectez cette forme dans les valeurs initiales et validez à nouveau la relation côté serveur.

Pour imbriquer ce sélecteur dans un formulaire de catalogue React, reprenez le wrapper Jotai isolé et la résolution avec contexte de la recette commande complète. Le registre vérifié partage les atomes de formulaire entre instances ; le wrapper évite un éditeur en double et conserve la valeur du champ parent. Vue ne demande pas ce wrapper.

Essayer dans la page

Ouvrez la commande existante et choisissez un client avec le sélecteur de tableau. Ajoutez une ligne pour sélectionner un produit.

Agrandir ↗

Données de démonstration. Les modifications restent dans cet aperçu.

Agrandir ↗

Données de démonstration. Les modifications restent dans cet aperçu.