demo D284
A feed enriched from a lookup in React
One order feed joined to a customer lookup, held until known and released enriched
addSource(join: { from, localKey, fields, missing: 'hold' })
This joins a live order feed to a customer lookup as the rows arrive, holding each order until its customer is known and then releasing it enriched. It fills in the name and tier a reader actually wants, so incomplete live records arrive complete.
This is the React version. The grid mounts through the createLatticeGrid adapter, which takes its configuration as ordinary props and hands back the live grid through a ref. The grid below is the same one every other tab runs.
The configuration
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.min.css">
<div id="app"></div>
<script type="module">
import React from 'https://esm.sh/react@18';
import { createRoot } from 'https://esm.sh/react-dom@18/client';
import { createGrid } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.esm.min.js';
import { createLatticeReact } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/modules/react.esm.min.js';
import { createDataRouter } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/modules/data-router.esm.min.js';
const { LatticeGrid, useLatticeRouter, LatticeRouterProvider } = createLatticeReact({ React, createGrid, createDataRouter });
const MONEY = { style: 'currency', currency: 'USD', decimals: 0 };
const TIER_PILL = { decoration: 'pill', variant: { map: { Gold: 'warning', Silver: 'neutral', Bronze: 'info' } } };
const orderColumns = [
{ field: 'id', title: 'Order', layout: { width: 110, pin: 'start' } },
{ field: 'customerId', title: 'Cust. id', layout: { width: 100 } },
{ field: 'name', title: 'Customer' }, // brought across by the join
{ field: 'tier', title: 'Tier', cell: TIER_PILL, layout: { width: 110 } }, // brought across too
{ field: 'amount', title: 'Amount', type: 'number', format: MONEY, total: 'sum' },
];
const customerColumns = [
{ field: 'id', title: 'Id', layout: { width: 90, pin: 'start' } },
{ field: 'name', title: 'Customer' },
{ field: 'tier', title: 'Tier', edit: true, cell: TIER_PILL, layout: { width: 120 } },
];
function App() {
const router = useLatticeRouter({ key: 'kind', rowKey: 'id' });
React.useEffect(() => {
if (!router) return undefined;
// Two feeds fanned in. The order feed names the customer feed as its
// lookup, brings name and tier across on the way in, and holds an order
// until its customer is known (missing: 'hold') rather than showing it
// half-filled.
const ordersSrc = router.addSource('orders', {
join: { from: 'customers', localKey: 'customerId', foreignKey: 'id', fields: ['name', 'tier'], missing: 'hold' },
});
const customersSrc = router.addSource('customers');
customersSrc.load([/* known customers: { id, kind: 'customer', name, tier } */]);
ordersSrc.load([/* orders: { id, kind: 'order', customerId, amount } */]);
// Loading the rest of the customers later releases the held orders
// already enriched; correcting a tier in the customer grid re-enriches
// the orders.
}, [router]);
return (
<LatticeRouterProvider router={router}>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '14px' }}>
<LatticeGrid route="order" rowKey="id" columns={orderColumns} rows={[]} style={{ height: '360px' }} />
<LatticeGrid route="customer" rowKey="id" edit columns={customerColumns} rows={[]} style={{ height: '360px' }} />
</div>
</LatticeRouterProvider>
);
}
createRoot(document.getElementById('app')).render(<App />);
</script>
One feed, enriched from a lookup as it arrives
Live records rarely arrive complete. An order comes in carrying a customer id and an amount, and the name and tier a reader actually wants live in a separate customer list. The data router joins the two as the feed arrives: an order source names the customer source as its lookup, brings the chosen fields across, and hands the grid a row that already carries the name and tier. You configure the join once, addSource('orders', { join: { from: 'customers', localKey: 'customerId', fields: ['name', 'tier'] } }), and every order from then on is enriched on the way in.
The interesting case is the order that arrives before its customer is known. Setting missing: 'hold' withholds that order rather than showing it half-filled with blank columns; when the customer feed loads, the held orders release and arrive already enriched. Both sides stay live, so a corrected tier in the customer list re-enriches the orders that point at it, and the grid reading the orders never has to know the join is happening.
How do I enrich a live feed with data from another source?
Add the lookup as a second source and give the main source a join: name the lookup in from, the field on your rows that matches it in localKey, and the fields to bring across in fields. Rows are enriched as they arrive. Set missing: 'hold' to withhold a row whose lookup is not known yet and release it once the lookup loads, so a reader never sees a half-filled record. Both sources stay live, so editing the lookup re-enriches the rows that depend on it.