# Dynamic path placeholders

Decorators and `DataProviderKey` paths can include **placeholders** resolved on the host component at runtime:

- `${prop}` or `{$prop}` — e.g. `"users/${userId}"`, `"api/sessions/${sessionId}/sync"`
- Nested expressions — e.g. `"teams.${teamId}.members"`

Resolution is done by [`resolveDynamicPath`](../../core/decorators/subscriber/dynamicPath.ts). The root property names (`userId`, `sessionId`, …) are watched via `requestAnimationFrame` (see `dynamicPropertyWatch.ts`).

## Default behaviour (`ready` / `not ready`)

| Placeholder value | Path `ready`? | Inserted segment | Notes |
|-------------------|---------------|------------------|-------|
| `undefined` | **no** | — | Wait until defined |
| `null` | **no** | — | Same as undefined |
| `""` | **yes** | empty string | e.g. `sessions//sync` — request may still run |
| `0` | **yes** | `"0"` | Not treated as “missing” |
| `false` | **yes** | `"false"` | |
| `42`, `"alpha"` | **yes** | `"42"`, `"alpha"` | |

When `ready: false`, decorators **do not** call the network (for `@get` / `@post` / `@put` / `@patch`), **unsubscribe** (`@bind` / `@subscribe`), or skip publisher binding (`@publish` / `@handle`). The decorated property is often left unchanged or set to `undefined` (HTTP decorators).

When the placeholder later becomes valid, observers run again and behaviour resumes.

## `skipEmptyPlaceholder` option

Some APIs should not run while an id is still `""`. Opt in per decorator:

```typescript
@get(new Endpoint<User, { userId: string }>("users/${userId}"), {
  skipEmptyPlaceholder: true,
})
```

| Option | Scope |
|--------|--------|
| `skipEmptyPlaceholder?: boolean` | **Only** empty string `''` on a placeholder |
| Default `false` | `""` is inserted into the path (legacy behaviour) |
| `true` | `""` → `ready: false`, same as `null` / `undefined` for that segment |

Does **not** affect `0`, `false`, `null`, or `undefined` (nullish stays “not ready” regardless).

Available on:

| API | Where |
|-----|--------|
| `@get` | 2nd or 3rd argument (`GetOptions`) |
| `@post` / `@put` / `@patch` | `PostOptions` / `ApiSendOptions` |
| `@bind` | `BindOptions` |
| `@publish` | 2nd argument `PublishOptions` |
| `@handle` | `HandleOptions` |

## Per-decorator summary

| Decorator | `ready: false` | `ready: true` |
|-----------|----------------|---------------|
| `@get` | No HTTP; `payload` → `undefined` | `ApiResult` assigned |
| `@post` / `@put` / `@patch` | No HTTP; `payload` → `undefined` | `ApiResult` assigned |
| `@subscribe` / `@bind` | Unsubscribe; prop keeps last value | Subscribe `onAssign` |
| `@publish` | Internal publisher `null`; writes ignored | `publisher.set` on assign |
| `@handle` | No subscription; with `waitForAllDefined`, method not called | Callback on assign |

## See also

- [DataProviderKey](/docs/misc/dataProviderKey) — path syntax
- [Endpoint](/docs/misc/endpoint) — HTTP paths + host generic `U`
- HTTP: [@get](/docs/decorators/get) · [@post](/docs/decorators/post) · [@put](/docs/decorators/put) · [@patch](/docs/decorators/patch)
- Store: [@subscribe](/docs/decorators/subscribe) · [@bind](/docs/decorators/bind) · [@publish](/docs/decorators/publish) · [@handle](/docs/decorators/handle)
- Legacy strings: [@onAssign](/docs/decorators/on-assign) (dynamic paths section)
- Templates: [sub()](/docs/directives/sub) — dynamic paths in Lit (no `skipEmptyPlaceholder` yet)
- Tutorial: [My first component](/docs/getting-started/my-first-component) — `"${dataProvider}"` scope pattern
