# Dash

Source: https://docs.interop.io/desktop/capabilities/data-sharing/shared-contexts/dash/index.html

## Context Components

The io.Connect Dash library offers two components for handling shared context objects - `Context` and `Contexts`.

The `Context` component binds to a specific context object and automatically subscribes for changes to it. You can set and update its value and also react to context updates. The `id` of the instantiated `Context` component is the name of the specific context object.

The `Contexts` component exposes an API for handling context objects by name - set and update their values, destroy contexts or set the values of nested properties using their paths in the context object.

To use the io.Connect Shared Contexts functionalities, instantiate the `Context` and/or `Contexts` components and pass an `id` to each component:

```python
import dash
import dash_glue42

app = dash.Dash(__name__)

app.layout = dash_glue42.Glue42(id="io-connect", children=[
    # Using the general API to set and update contexts.
    dash_glue42.Contexts(id="io-connect-shared-contexts"),

    # Subscribe to a specific context.
    dash_glue42.Context(id="app-styling")
])
```

> ℹ️ *See the Dash [Shared Contexts example](https://github.com/InteropIO/dash-example/tree/master/contexts) on GitHub.*

Due to limitations of the Dash framework, the `.` and `{` characters can't be used in component IDs. This means that if you want to use the `Context` component to subscribe to a shared context whose name contains these characters, you can't use the `id` property, but must pass a `contextName` property specifying the name of the context to which to subscribe:

```python
import dash
import dash_glue42

app = dash.Dash(__name__)

app.layout = dash_glue42.Glue42(id="io-connect", children=[
    # Subscribing to a context whose name contains a `.` character.
    dash_glue42.Context(id="app-styling", contextName="App.Styling")
])
```

## Updating Contexts

To update a shared context object, define an app callback. Pass the context component ID and its `update` property in the `Output` of the callback. For `Input` of the callback pass the ID and the property of the component that you want to trigger the context update.

The following examples demonstrate how to update the `"app-styling"` context using the `Contexts` and `Context` components. For the purpose of the examples, the `Input` component is a text input.

When using the `Contexts` component, it's required to pass the name of the context object you want to update:

```python
# Define a callback that will trigger a context update
# when a specified property of a component changes.
@app.callback(
    Output("io-connect-shared-contexts", "update"),
    Input("background-color", "value"),
    prevent_initial_call=True
)
def update_app_styling_context(background_color):

    return {
        "name": "app-styling",
        "data": {
            "backgroundColor": background_color
        }
    }
```

When using the `Context` component to update a specific context object, return only the update data:

```python
@app.callback(
    Output("app-styling", "update"),
    Input("background-color", "value"),
    prevent_initial_call=True
)
def update_app_styling_context(background_color):
    return { "backgroundColor": background_color }
```

Updating a context merges the existing properties of the context object with the properties of the update object. Only the specified context properties are updated, any other existing context properties will remain intact.

## Updating Specific Properties

To update specific properties of a context object that aren't at top level, use the `setPath` or `setPaths` properties of the `Contexts` component.

Use `setPath` to update a single context object property. Return the name of the context to update, the path to the property to be updated and the new value for it. The `path` property of the returned object accepts a dot-separated string - e.g., `"prop1.prop2"`. The `data` property of the returned object holds the new value for the updated property:

```python
@app.callback(
    Output("io-connect-shared-contexts", "setPath"),
    Input("background-color", "value"),
    prevent_initial_call=True
)
def update_app_styling_context(background_color):

    return {
        "name": "app-styling",
        "path": "colors.background",
        "data": background_color
    }
```

Use `setPaths` to update multiple context object properties. Return the name of the context to update, the paths and the new values for the updated properties. The `paths` property of the returned object is an array of objects holding the path and the new value for each updated property. The JavaScript equivalent of each object in the array is the [`PathValue`](https://docs.interop.io/desktop/reference/javascript/shared%20contexts/pathvalue/index.md) object.

The following example demonstrates how to update the values of two context object properties simultaneously:

```python
@app.callback(
    Output("io-connect-shared-contexts", "setPaths"),
    Input("background-color", "value"),
    Input("foreground-color", "value")
    prevent_initial_call=True
)
def update_app_styling_context(background_color, foreground_color):

    return {
        "name": "app-styling",
        "paths": [
            {
                "path": "colors.background",
                "value": background_color
            },
            {
                "path": "colors.foreground",
                "value": foreground_color
            }
        ]
    }
```

## Replacing Contexts

To replace a shared context object, define an app callback. Pass the context component ID and its `set` property in the `Output` of the callback. For `Input` of the callback pass the ID and the property of the component that you want to trigger replacing the context.

The following examples demonstrates how to set an entirely new value for the `"app-styling"` context object using the `Contexts` and `Context` components.

When using the `Contexts` component, it's required to pass the name of the context object whose value you want to set:

```python
# Define a callback that will trigger replacing the context
# when a specified property of a component changes.
@app.callback(
    Output("io-connect-shared-contexts", "set"),
    Input("default-styling-btn", "n_clicks"),
    prevent_initial_call=True
)
def set_default_app_styling(n_clicks):

    return {
        "name": "app-styling",
        "data": {
            "backgroundColor": "white",
            "color": "black"
        }
    }
```

When using the `Context` component to set the value of a specific context object, return only the data:

```python
@app.callback(
    Output("app-styling", "set"),
    Input("default-styling-btn", "n_clicks"),
    prevent_initial_call=True
)
def set_default_app_styling(_):

    return {
        "backgroundColor": "white",
        "color": "black"
    }
```

Setting the value of a context removes and replaces all existing properties of the specified context object with the new ones.

## Handling Context Updates

Use the `Context` component to handle context updates. Define an app callback handler and for `Input` pass the name of the context object and its `context` property which holds the current context value. The value of the `context` property is assigned by the framework and must not be altered by client code. Use the `context` object to extract the latest update from its `data` property, the delta between the latest and the previous states available in its `delta` property, or a string collection of the names of the removed context object properties available in its `removed` property. The `context` object also has an `extraData` property. The `extraData` object has an `updaterId` property holding the ID of the updating Interop instance, and an `isMyUpdate` Boolean property indicating whether the current Interop instance has updated the context. Use this information to avoid potential infinite loops if the subscribed app is also publishing updates to the same shared context. The handler will be triggered each time the context is updated. For `Output` of the callback pass the ID and the property of the component you want to update with the data from the context object.

The following example demonstrates how to update the `style` property of the `"app-wrapper"` component with data from the `"app-styling"` context object:

```python
# Define a callback that will handle updates of the context object.
@app.callback(
    Output("app-wrapper", "style"),
    Input("app-styling", "context")
)
def app_styling_context_changed_handler(context):

    if context is not None:
        data = context.get("data", {})
        background_color = data.get("backgroundColor")
        font_color = data.get("color")

        return {
            "backgroundColor": background_color,
            "color": font_color
        }
```

## Destroying Contexts

Use the `Contexts` component to destroy a context object. For `Input` of the callback pass the ID and the property of the component you want to trigger destroying the context. For `Output`, pass the ID of the `Contexts` component and its `destroy` property. Return an object with the name of the context you want to destroy:

```python
# Define a callback that will destroy the context object.
@app.callback(
    Output("io-connect-shared-contexts", "destroy"),
    Input("destroy-context-btn", "n_clicks"),
    prevent_initial_call=True
)
def destroy_app_styling_context(n_clicks):
    return { "name": "app-styling" }
```
