# Master-detail

Display a list of items and show details for the selected item using dcupl queries.

## Key dcupl functions

| Function                             | Purpose                                                     |
| ------------------------------------ | ----------------------------------------------------------- |
| `query.items()`                      | Get list of items for the master view                       |
| `query.one({ itemKey })`             | Get the selected item by key                                |
| `query.one({ itemKey, projection })` | Get the selected item with fields of its referenced items   |
| `query.setCondition()`               | Filter a related list (replaces that attribute's condition) |

## Example data

The examples use products with a category, and reviews that reference a product:

```typescript title="Setup"
import { Dcupl } from '@dcupl/core';

const dcupl = new Dcupl();
dcupl.models.set({
  key: 'Category',
  properties: [{ key: 'name', type: 'string' }],
  data: [{ key: 'cat-1', name: 'Electronics' }],
});
dcupl.models.set({
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'price', type: 'float' },
  ],
  references: [{ key: 'category', type: 'singleValued', model: 'Category' }],
  data: [
    { key: 'prod-123', name: 'Laptop', price: 999, category: 'cat-1' },
    { key: 'prod-456', name: 'Phone', price: 599, category: 'cat-1' },
  ],
});
dcupl.models.set({
  key: 'Review',
  properties: [
    { key: 'rating', type: 'int' },
    { key: 'text', type: 'string' },
  ],
  references: [{ key: 'product', type: 'singleValued', model: 'Product' }],
  data: [
    { key: 'r1', rating: 5, text: 'Fast', product: 'prod-123' },
    { key: 'r2', rating: 4, text: 'Good camera', product: 'prod-456' },
    { key: 'r3', rating: 3, text: 'Loud fan', product: 'prod-123' },
  ],
});
await dcupl.init();
```

## Getting the list

Use `items()` to retrieve all items for the master list.

```typescript
const list = dcupl.lists.create({ modelKey: 'Product' });

// Get all products for the list view
const products = list.catalog.query.items();
```

## Selecting an item

When user selects an item, look it up by its key with `one()`. This does not change the list's query, so the master view keeps its filters. The selection is your own state: to clear it or select another item, change that state and look up the new key.

```typescript
function selectItem(itemKey: string) {
  // null if the list's query filters the item out (start/count paging is ignored)
  return list.catalog.query.one({ itemKey });
}

const selected = selectItem('prod-999');
if (!selected) {
  // Not found: show an empty detail view
}
```

Check the result with `if (!selected)` (or `=== null`): `one()` returns `null`, typed `T | null`, for a missing key or an item the list's query filters out.

To read an item regardless of the list's filters, use the global lookup: `dcupl.query.one({ modelKey: 'Product', itemKey })` (returns `null` if the key does not exist).

A reference comes back as `{ key }`. To show fields of referenced items in the detail view, pass a [projection](/sdk/api/types#projection):

```typescript
const product = list.catalog.query.one({
  itemKey: 'prod-123',
  projection: { $: true, category: { $: true } },
});
// { key: 'prod-123', name: 'Laptop', price: 999, category: { key: 'cat-1', name: 'Electronics' } }
```

## Loading related data

Load associated data for the selected item using a separate list. A condition on a reference matches the referenced item's key:

```typescript
// Create the related list once and reuse it
const reviewList = dcupl.lists.create({ modelKey: 'Review' });

function getReviews(productKey: string) {
  // setCondition replaces the previous condition on 'product'
  reviewList.catalog.query.setCondition({
    attribute: 'product',
    operator: 'eq',
    value: productKey,
  });

  return reviewList.catalog.query.items();
}

getReviews('prod-123'); // r1, r3
```

The same works with a plain string field that holds the key (for example `productId`), but a [reference](/sdk/api/models#reference) is indexed, can be projected, and lets dcupl check that the key exists.

## Complete flow

```typescript
// 1. Create list and get all items
const list = dcupl.lists.create({ modelKey: 'Product' });
const allProducts = list.catalog.query.items();

// 2. User clicks on product "prod-123": the detail with its category
const selectedProduct = list.catalog.query.one({
  itemKey: 'prod-123',
  projection: { $: true, category: { $: true } },
});

// 3. Load related data
const reviewList = dcupl.lists.create({ modelKey: 'Review' });
reviewList.catalog.query.setCondition({
  attribute: 'product',
  operator: 'eq',
  value: 'prod-123',
});
const reviews = reviewList.catalog.query.items(); // r1, r3

// 4. User selects different product - look up the new key and refresh the reviews
const newSelection = list.catalog.query.one({ itemKey: 'prod-456' });
reviewList.catalog.query.setCondition({
  attribute: 'product',
  operator: 'eq',
  value: 'prod-456',
});
const newReviews = reviewList.catalog.query.items(); // r2
```

## Next steps

- [Query Filter](/sdk/queries/query-filter) — Filter operators and conditions
- [Query results](/sdk/queries/query-results) — Retrieving items from queries
- [Models API](/sdk/api/models#reference) — References and their types
- [Projection](/sdk/api/types#projection) — Choosing the fields of returned items
- [Core concepts](/sdk/core-concepts) — Core dcupl concepts and architecture
