Skip to content

Commit

Permalink
Update README and GLOSSARY
Browse files Browse the repository at this point in the history
  • Loading branch information
dfreeman committed Mar 18, 2024
1 parent ca7ebac commit 61efc0d
Show file tree
Hide file tree
Showing 2 changed files with 6 additions and 9 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ The entry point to a UI powered by ember-exclaim is the `<ExclaimUi>` component.
- `@implementationMap`: a mapping of names in the `ui` config to information about their backing implementations
- `@onChange(envPathOfChangedValue)`: an optional function that will be invoked when a value in the `env` changes
- `@wrapper`: an optional component that will wrap every rendered component in your UI configuration. The `wrapper` component will receive the `ComponentSpec` as `@spec` ([more on `ComponentSpec` here](ember-exclaim/src/-private/GLOSSARY.md)), the `Environment` as `@env` and the component's resolved `@config`.
- `@useClassicReactivity`: an optional flag that, if set, will cause any environment bindings Exclaim constructs to use classic Ember `computed` machinery rather than native getters and setters that assume data is appropriately `@tracked`.

Each of these things is described in further detail below.

Expand Down Expand Up @@ -169,11 +170,11 @@ The `@implementationMap` given to `<ExclaimUi>` dictates what components it can

The [demo app](https://salsify.github.io/ember-exclaim) for this repo contains [a variety of simple component implementations](tests/dummy/app/components/exclaim-components) that you can use as a starting point for building your own.

An ember-exclaim component implementation will receive two properties when rendered: `config` and `env`.
An ember-exclaim component implementation will receive two arguments when rendered: `@config` and `@env`.

### `@config`

The `@config` argument of the implementing component will contain all other information supplied in the `$component` hash representing it in the UI config. Any `$bind` directives in that config will be automatically be resolved when they are `get` or `set`. As an example, consider a lightweight implementation of the `input` component mentioned above.
The `@config` argument of the implementing component will contain all other information supplied in the `$component` hash representing it in the UI config. Any `$bind` directives in that config will be automatically be resolved when they are read or written. As an example, consider a lightweight implementation of the `input` component mentioned above.

```hbs
<input type="text" value={{@config.value}} oninput={{action (mut @config.value) value='target.value'}}>
Expand All @@ -197,7 +198,7 @@ For example, the [`vbox`](tests/dummy/app/components/exclaim-components/vbox) co
{{/each}}
```

By default, children will inherit the environment of their parent. This environment can be extended by passing a POJO with additional key/value pairs as a second parameter to `{{yield}}`. Check the implementation of [`each`](playground-app/app/components/exclaim-components/each) and [`let`](playground-app/app/components/exclaim-components/let) in the demo app for examples of how this can be used.
By default, children will inherit the environment of their parent. This environment can be extended by passing a POJO with additional key/value pairs as a second parameter to `{{yield}}`. Check [the implementation of `each` and `let`](playground-app/app/components/exclaim-components/) in the demo app for examples of how this can be used.

## Implementing Helpers

Expand Down
8 changes: 2 additions & 6 deletions ember-exclaim/src/-private/GLOSSARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,8 @@ A **`HelperSpec`** is the information necessary to compute the value of a helper

Any hash in the UI config with a `$helper` key will be transformed into a `HelperSpec` instance, following the same resolution rules as `ComponentSpec`s do, but looking for a `helper` function in the `implementationMap` rather than a `componentPath`.

A **`Binding`** is a reference to some value available in the salient `Environment` (see below), much like a variable reference in a programming language. A `Binding` is meaningless on its own, and must always be evaluated in the context of some `Environment`. Note that the `config` for a `ComponentSpec` may contain `Binding`s, which won't be resolved until they're actually used. This allows components to evaluate parts of their config in varying contexts, such as an `each` component rendering the same subcomponent config with varying values for its iteration variable.
A **`Binding`** is a reference to some value available in the salient _environment_ (see below), much like a variable reference in a programming language. A `Binding` is meaningless on its own, and must always be evaluated in the context of some `Environment`. Note that the `config` for a `ComponentSpec` may contain `Binding`s, which won't be resolved until they're actually used. This allows components to evaluate parts of their config in varying contexts, such as an `each` component rendering the same subcomponent config with varying values for its iteration variable.

## Runtime Elements

At runtime, exclaim UIs are evaluated relative to an **`Environment`**, which is analogous to scope in a programming language. An `Environment` contains all the bound values that are available to `Binding`s, and may itself contain `Binding` instances that point at other data within itself. When the time comes to resolve the `config` for a component to actual values, `ComponentSpec` instances expose a `resolveConfig(environment)` method, which returns an `EnvironmentData` instance for the configuration.

An **`EnvironmentData`** object is a proxy for some arbitrary hash or array that resolves any `Binding`s it contains relative to some `Environment`. You can think of `EnvironmentData` as a piece of data that remembers where it came from. For instance, given an `EnvironmentData` instance `data` wrapping the hash `{ hi: 'hello', bye: new Binding('farewell') }`, calling `data.get('hi')` would return the string `'hello'`, and calling `data.get('bye')` would return whatever the associated `Environment` contains for the key `farewell`.

Implementation note: when an `Environment` or `EnvironmentData` instance is asked to `get` a property, it first inspects whether the underlying value for that property is a `Binding`, and if so, resolves it. Once this resolution has occurred the first time, a computed property is generated so that subsequent lookups don't have to re-resolve, and changes to the underlying bound property will be reflected on the host `EnvironmentData` or `Environment`. Any non-primitive result of a `get` on an `Environment` or `EnvironmentData` instance will itself be a `EnvironmentData`, so that `Binding`s nested arbitrarily deep will always be resolved. There is also an `EnvironmentData` variant called `EnvironmentArray` which functions similarly but wraps arrays rather than objects.
At runtime, exclaim UIs are evaluated relative to an _environment_, which is analogous to scope in a programming language. An environment contains all the bound values that are available to `Binding`s. When the time comes to resolve the `config` for a component to actual values, `ComponentSpec` instances expose a `resolveConfig(environment)` method, which creates a copy of its configuration with any `Binding` or `HelperSpec` instances replaced with appropriate getters and setters that will read data from the given environment.

0 comments on commit 61efc0d

Please sign in to comment.