consul/ui/packages/consul-ui/app/components/data-source
Michael Klein 03a1a86dfe
ui: chore - upgrade ember and friends (#14518)
* v3.20.2...v3.24.0

* Fix handle undefined outlet in route component

* Don't use template helper for optional modal.open

Using the optional-helper here will trigger a computation
in the same runloop error. This is because we are setting
the `modal`-property when the `<Ref>` component gets
rendered which will update the `this.modal`-property which
will then recompute the `optional`-helper leading to this
error.

Instead we will create an action that will call the `open`-method
on the modal when it is defined. This gets rid of the double
computation error as we will not access the modal property
twice in the same runloop when `modal` is getting set.

* Fix - fn needs to be passed function tab-nav

We create functions in the component file instead
so that fn-helper stops complaining about the
need to pass a function.

* Update ember-exam to 6.1 version

"Makes it compatible" with ember-qunit v5

* scheduleOnce setMaxHeight paged-collection

We need to schedule to get around double-computation error.

* Fix - model.data is removed from ember-data

This has been private API all along - we need to
work around the removal.

Reference: https://github.com/emberjs/data/pull/7338/files#diff-9a8746fc5c86fd57e6122f00fef3155f76f0f3003a24b53fb7c4621d95dcd9bfL1310

* Fix `propContains` instead of `deepEqual` policy

Recent model.data works differently than iterating attributes.
We use `propContains` instead of `deepEqual`. We are only
interested in the properties we assert against and match
the previous behavior with this change.

* Fix `propContains` instead of `deepEqual` token

* Better handling single-records repo test-helper

`model.data` has been removed we need to handle proxies and
model instances differently.

* Fix remaining repository tests with propContains

We don't want to match entire objects - we don't care
about properties we haven't defined in the assertion.

* Don't use template helper for optional modal.open

Using a template helper will give us a recomputation error -
we work around it by creating an explicit action on
the component instead.

* Await `I $verb the $pageObject object` step

* Fix no more customization ember-can

No need to customize, the helper handles destruction
fine on its own.

* Fix - don't pass `optional` functions to fn

We will declare the functions on the component instead.
This gives us the same behavior but no error from
`fn`, which expects a function to be passed.

* Fix - handle `undefined` state on validate modifier

StateChart can yield out an undefined `state` we need
to handle that in the validate modifier

* Fix linting errors tests directory

* Warn / turn off new ember linting issues

We will tackle them one by one and don't want to
autofix issues that could be dangerous to auto-fix.

* Auto-fix linting issues

* More linting configuration

* Fix remaining linting issues

* Fix linting issues new files after rebase

* ui: Remove ember-cli-uglify config now we are using terser (#14574)

Co-authored-by: John Cowen <johncowen@users.noreply.github.com>
2022-09-15 09:43:17 +01:00
..
README.mdx ui: Thread through data-source invalidate method (#13710) 2022-07-14 09:30:35 +01:00
index.hbs ui: Thread through data-source invalidate method (#13710) 2022-07-14 09:30:35 +01:00
index.js ui: chore - upgrade ember and friends (#14518) 2022-09-15 09:43:17 +01:00

README.mdx

# DataSource

Use the `@dataSource` decorator in your repositories to define URI to async
method mapping.

```javascript
class SomethingRepository extends Service {
  @dataSource('/:partition/:nspace/:dc/services')
  async youCouldCallItAnythingTodoWithGettingServices(params) {
    console.log(params);
    // {partition: "partition", nspace: "nspace", dc: "dc"}
    return getTheThing(params);
  }
}
```

```hbs preview-template
<DataSource
  @src={{uri "/partition/nspace/dc/services"}}
  @loading="eager"
  @disabled={{false}}
as |source|>
{{#each source.data as |service|}}
{{service.Name}}<br />
{{/each}}
</DataSource>
```


Please make sure you use the `uri` helper to specify src URIs, this ensures that it is very difficult to miss any URL encoding problems. If you don't use the `uri` helper then an error will be thrown.

## Attributes

| Argument | Type | Default | Description |
| --- | --- | --- | --- |
| `src` | `String` | | The source to subscribe to updates to, this should map to a string based URI |
| `loading` | `String` | eager | Allows the browser to defer loading offscreen DataSources (`eager\|lazy`). Setting to `lazy` only loads the data when the DataSource is visible in the DOM (inc. `display: none\|block;`) |
| `disabled` | `Boolean` | true | When disabled the DataSource is closed |
| `open` | `Boolean` | false | Force the DataSource to open, used to force non-blocking data to refresh (has no effect for blocking data) |
| `onchange` | `Function` |  | The action to fire when the data changes. Emits an Event-like object with a `data` property containing the data. |
| `onerror` | `Function` |  | The action to fire when an error occurs. Emits ErrorEvent object with an `error` property containing the Error. |

## Exported API

| Name | Type | Description |
| --- | --- | --- |
| `data`  | `object` | The loaded dataset once any data has been loaded successfully |
| `error` | `Error` |The error thrown if an error is encountered whilst loading data |
| `invalidate` | `function` | Invalidate the data represented by the datasource, therefore forcing a re-request of data for the DataSource |

The component takes a `src` or an identifier (a uri) for some data and then emits `onchange` events whenever that data changes. If an error occurs whilst listening for data changes, an `onerror` event is emitted.

Setting `@loading="lazy"` uses `IntersectionObserver` to activate/deactive event emitting until the `<DataSource />` element is displayed in the DOM. This means you can use CSS `display: none|block;` to control the loading and stopping loading of data for usage with CSS based tabs and such-like.

Consuls HTTP API DataSources use Consul's blocking query support for live updating of data.

Behind the scenes in the Consul UI we map URIs back to our `ember-data` backed `Repositories` meaning we can essentially redesign the URIs used for our data to more closely fit our needs. For example we currently require that **all** HTTP API URIs begin with `/dc/nspace/` values whether they require them or not.

`DataSource` is not just restricted to HTTP API data, and can be configured to listen for data changes using a variety of methods and sources. For example we have also configured `DataSource` to listen to `LocalStorage` changes using the `settings://` pseudo-protocol in the URI (See examples below).


## Examples

Straightforward usage can use `mut` to easily update data within a template using an event handler approach.

```hbs
  {{! listen for HTTP API changes}}
  <DataSource
    @src={{uri "/partition/nspace/dc/services"}}
    @onchange={{action (mut items) value="data"}}
    @onerror={{action (mut error) value="error"}}
  />
  {{#if error}}
    Something went wrong!
  {{/if}}
  {{#if (not items)}}
    Loading...
  {{/if}}
  {{! the value of items will change whenever the data changes}}
  {{#each items as |item|}}
    {{item.Name}} {{! < Prints the item name }}
  {{/each}}

  {{! listen for Settings (local storage) changes}}
  <DataSource
    @src={{uri "settings://consul:token"}}
    @onchange={{action (mut token) value="data"}}
    @onerror={{action (mut error) value="error"}}
  />
  {{! the value of token will change whenever the data changes}}
  {{token.AccessorID}} {{! < Prints the token AccessorID }}
```

A property approach to easily update data within a template

```hbs
  {{! listen for HTTP API changes}}
  <DataSource
    @src={{uri "/partition/nspace/dc/services"}}
  as |source|>
    {{#if source.error}}
      Something went wrong!
    {{/if}}
    {{#if (not source.data)}}
      Loading...
    {{/if}}
    {{! the value of items will change whenever the data changes}}
    {{#each source.data as |item|}}
      {{item.Name}} {{! < Prints the item name }}
    {{/each}}
  </DataSource>
```

Both approaches can be used in tandem.

DataSources can also be recursively nested for loading in series as opposed to in parallel. Nested DataSources will not start loading until the immediate parent has loaded (ie. it has data) as they are not placed into the DOM until this has happened. However, if a DataSource has started loading, and the immediate parent errors, the nested DataSource will stop receiving updates yet it and its properties will remain accessible within the DOM.

```hbs

  {{! straightforwards error/loading states}}
  {{#if error}}
    Something went wrong!
  {{else if (not loaded)}}
    Loading...
  {{/if}}

  {{! listen for HTTP API changes}}
  <DataSource
    @src={{uri "/partition/nspace/dc/services"}}
    @onerror={{action (mut error) value="error"}}
  as |source|>

    <source.Source
      @src={{uri "/partition/nspace/dc/service/{{source.data.firstObject.Name}}"}}
      @onerror={{action (mut error) value="error"}}
    as |source|>

      {{source.data.Service.Service.Name}} <== Detailed information for the first service

      <source.Source
        @src={{uri "/partition/nspace/dc/proxy/for-service/{{source.data.Service.ID}}"}}
        @onerror={{action (mut error) value="error"}}
        @onchange={{action (mut loaded) true}}
      as |source|>

        {{source.data.DestinationName}}

      </source.Source>

    </source.Source>

  </DataSource>
```

## See

- [Component Source Code](./index.js)
- [Template Source Code](./index.hbs)

---