Options
All
  • Public
  • Public/Protected
  • All
Menu

The 'Resource' base class has only one lifecycle hook, modify, which is called during instantiation of the resource as well as on every update of any of any consumed args.

Typically, a Resource will be used to build higher-level APIs that you'd then use in your apps. For example, maybe you want to build a reactive-wrapper around a non-reactive wrapper, XState which requires that the "State machine interpreter" is stopped when you are discarding the parent context (such as a component).

An example

import { Resource } from 'ember-resources/core';
import { createMachine, interpret } from 'xstate';

const machine = createMachine(); // ... see XState docs for this function this ...

class MyResource extends Resource {
@tracked customState;

constructor(owner) {
super(owner);

registerDestructor(this, () => this.interpreter.stop());
}

modify(positional, named) {
if (!this.interpreter) {
// Initial Setup
this.interpreter = interpret(machine).onTransition(state => this.customState = state);
} else {
// Subsequent Updates
this.interpreter.send('SOME_EVENT', { positional, named });
}
}
}

Once defined, there are two ways to use MyResource

  • in a template
  • in JavaScript

In the template, the Resource can be imported (or re-exported from the helpers directory)

When imported (using RFC 779),

import { MyResource } from './somewhere';

<template>
{{#let (MyResource) as |myResource|}}
{{log myResource.customState}}
{{/let}}
</template>

When using in javascript, you'll need the from utility

import { MyResource } from './somewhere';

class ContainingClass {
state = MyResource.from(this, () => [...])
}

However, when authoring a Resource, it's useful to co-locate an export of a helper function:

export function myResource(destroyable, options) {
return MyResource.from(destroyable, () => ({
foo: () => options.foo,
bar: () => options.bar,
}))
}

This way, consumers only need one import.

Type parameters

  • T: ArgsWrapper = ArgsWrapper

Hierarchy

Index

Constructors

Properties

Methods

Constructors

  • new Resource<T>(owner: unknown): Resource<T>
  • Type parameters

    • T: ArgsWrapper = ArgsWrapper

    Parameters

    • owner: unknown

    Returns Resource<T>

Properties

of: <Instance, Args>(context: object, klass: new (...args: unknown[]) => Instance, thunk?: Thunk | (() => Args)) => Instance = resourceOf

Type declaration

    • <Instance, Args>(context: object, klass: new (...args: unknown[]) => Instance, thunk?: Thunk | (() => Args)): Instance
    • For use in the body of a class.

      note

      prefer from

      of is what allows resources to be used in JS, they hide the reactivity APIs from the consumer so that the surface API is smaller. Though, from an end-user-api ergonomics perspective, you wouldn't typically want to rely on this. As in ember-data-resources

      Given this potential use:

      import { Resource } from 'ember-resources';

      class SomeResource extends Resource {}

      class MyClass {
      data = Resource.of(this, SomeResource, () => [arg list]);
      }

      a better user-facing api, may be provided by:

      export function someResource(context, args) {
      return Resource.of(context, SomeResource, () => ... );
      }

      usage:

      import { someResource } from 'your-library';

      class SomeResource extends Resource {}

      class MyClass {
      data = someResource(this, () => [arg list]);
      }

      When any tracked data in the args thunk is updated, the Resource will be updated as well

      • The this is to keep track of destruction -- so when MyClass is destroyed, all the resources attached to it can also be destroyed.
      • The resource will do nothing until it is accessed. Meaning, if you have a template that guards access to the data, like:
        {{#if this.isModalShowing}}
        <Modal>{{this.data.someProperty}}</Modal>
        {{/if}}
        the Resource will not be instantiated until isModalShowing is true.

      Type parameters

      • Instance: Resource<ArgsWrapper, Instance>

      • Args: unknown[] = unknown[]

      Parameters

      • context: object
      • klass: new (...args: unknown[]) => Instance
          • new (...args: unknown[]): Instance
          • Parameters

            • Rest ...args: unknown[]

            Returns Instance

      • Optional thunk: Thunk | (() => Args)

      Returns Instance

Methods

  • modify(positional: T["positional"], named: T["named"]): void
  • Parameters

    • positional: T["positional"]
    • named: T["named"]

    Returns void

  • from<Instance>(context: object, thunk?: Thunk | (() => unknown)): Instance
  • For use in the body of a class.

    from is what allows resources to be used in JS, they hide the reactivity APIs from the consumer so that the surface API is smaller. Unlike of, due to the fewer arguments required in from, though it may be more convenient to not wrap your resource abstraction in a helper function.

    import { Resource } from 'ember-resources';

    class SomeResource extends Resource {}

    class MyClass {
    data = SomeResource.from(this, () => [ ... ]);
    }

    However, if you have argument defaults or need to change the shape of arguments depending on what ergonomics you want your users to have, a wrapper function may be better.

    export function someResource(context, { foo, bar }) {
    return SomeResource.from(context, () => ... );
    }

    usage:

    import { someResource } from 'your-library';

    class SomeResource extends Resource {}

    class MyClass {
    @tracked foo;
    @tracked bar;

    data = someResource(this, {
    foo: () => this.foo,
    bar: () => this.bar
    });
    }

    Type parameters

    • Instance: Resource<ArgsWrapper, Instance>

    Parameters

    • context: object
    • Optional thunk: Thunk | (() => unknown)

    Returns Instance

Generated using TypeDoc