# @bind

Binds a class property to a path in a publisher. The property updates when publisher data changes.

For Lit re-renders, also add `@state()` on the same property.

**See also:** [@subscribe](#docs/_decorators/subscribe.md/subscribe), [@publish](#docs/_decorators/publish.md/publish), [@get](#docs/_decorators/get.md/get).

## Principle

The decorator subscribes via `PublisherManager` using dot notation. Publisher updates flow into the decorated property.

## Import

<sonic-code language="typescript">
  <template>
import { bind } from "@supersoniks/concorde/decorators";
  </template>
</sonic-code>

## Example

<sonic-code language="typescript">
<template>
@customElement("demo-bind")
export class DemoBind extends LitElement {
  static styles = [tailwind];
  //
  @bind("demoData.firstName")
  @state()
  firstName = "";
  //
  @bind("demoData.lastName")
  @state()
  lastName: string = "";
  //
  @bind("demoData.count")
  @state()
  count: number = 0;
  //
  render() {
    return //......
  }
  //
  updateData() {
    const demoData = PublisherManager.get("demoData");
    const demoUsers = PublisherManager.get("demoUsers");
    const randomIndex = Math.floor(Math.random() * demoUsers.get().length);
    const randomUser = demoUsers.get()[randomIndex];
    demoData.set({
      firstName: randomUser.firstName,
      lastName: randomUser.lastName,
      count: (demoData.count.get() || 0) + 1,
    });
  }
}
//
</template>
</sonic-code>

<sonic-code>
  <template>
    <demo-bind></demo-bind>
  </template>
</sonic-code>

## `DataProviderKey` (strict typing)

`@bind` accepts either a string path (legacy) or a `DataProviderKey<T>`. The property type must match `T`. Use `reflect: true` to push local writes back to the publisher (see below). See [DataProviderKey](#docs/_misc/dataProviderKey.md/dataProviderKey).

<sonic-code language="typescript">
  <template>
import { bind } from "@supersoniks/concorde/decorators";
import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
//
type Data = { count: number };
const dataKey = new DataProviderKey<Data>("data");
//
@bind(dataKey.count, { reflect: true })
@state()
count: number = 0;
  </template>
</sonic-code>

## Reflect (`reflect: true`)

Two-way sync: reads from the publisher and local assignments call `publisher.set(...)`. An internal guard avoids infinite loops.

<sonic-code language="typescript">
  <template>
@bind("userData.profile.avatarUrl", { reflect: true })
@state()
avatar: string;
  </template>
</sonic-code>

<sonic-code language="typescript">
  <template>
@customElement("demo-bind-reflect")
export class DemoBindReflect extends LitElement {
  static styles = [tailwind];
//
  @bind("bindReflectDemo.count", { reflect: true })
  @state()
  withReflect: number = 0;
//
  @bind("bindReflectDemo.count")
  @state()
  withoutReflect: number = 0;
//
  render() {
    return html`
      <div class="mb-3">
        from publisher : ${sub("bindReflectDemo.count") || 0} <br />
        from component with reflect : ${this.withReflect || 0} <br />
        from component without reflect : ${this.withoutReflect || 0}
      </div>
      <sonic-button @click=${() => this.withReflect++}
        >Increment with reflect</sonic-button
      >
      <sonic-button @click=${() => this.withoutReflect++}
        >Increment without reflect</sonic-button
      >
    `;
  }
}
//
</template>
</sonic-code>

<sonic-code toggleCode>
  <template>
<demo-bind-reflect></demo-bind-reflect>
  </template>
</sonic-code>

## Path syntax

- First segment: data provider id.
- Nested properties: dot notation.
- Arrays: numeric index (`items.0`).

### Dynamic paths

Use `${prop}` or `${this.prop}` inside a **normal string literal** (not a JS template literal with backticks). `@bind` re-subscribes when a reactive dependency changes.

> Properties referenced in the pattern must be reactive (`@property`, etc.) or you must call `requestUpdate` manually.

<sonic-code>
  <template>
    <demo-bind-dynamic></demo-bind-dynamic>
  </template>
</sonic-code>

## Behavior

- Subscribes at `connectedCallback`, unsubscribes at `disconnectedCallback`.
- If the path does not exist yet, a publisher may be created with `null`.

## Notes

Works with any component that has the usual DOM lifecycle (`LitElement`, `Subscriber` mixin, etc.).

Shared data: [Sharing data](#docs/_getting-started/pubsub.md/pubsub).
