dojo dragon main logo

Resource Concepts

Dojo resources is designed to provide a cohesive and consistent mechanism for working with data within a Dojo application. There are core concepts for Dojo Resources:

  • Resource Templates
  • Resource Store
  • Resource Middleware

Templates

The resource template is a description that the resources uses to read, find and init data in the store. Resource templates are flexible to enable connecting resources to multiple different providers, for example RESTful APIs, client data (using an in-memory template).

Templates should be stateless so that they can be re-used across through-out an application, using the resource options to determine the data required and the resource controls to interact with the resource store, for example putting the data into the store.

Create a template using createResourceTemplate from the @dojo/framework/core/middleware/resources module.

userResourceTemplate.ts

import { createResourceTemplate } from '@dojo/framework/core/middleware/resources';

interface User {
    firsName: string;
    lastName: string;
    username: string;
    email: string;
}

// The type for the data is passed to the `createResourceTemplate` factory
export default createResourceTemplate<User>({
    read: (request: ResourceReadRequest, controls: ResourceControls) => {
        // use the `request` to "fetch" the data from the data-source
        // and use the controls to set the data into the store.
    },
    find: (request: ResourceFindRequest, controls: ResourceControls) => {
        // use the controls with the request to set the found item based
        // on the request
    }
});

The resource controls are injected to all the data template functions to enable working with the backing resource store. get() is used to return data from the store based on the request passed and put() is used to set data into the store for the request.

See the Resource Templates section for more details.

Store

The resource store is where all the data is stored and responsible for wiring widgets using the resource middleware with the ResourceTemplate passed to the widget. The store invalidates all widgets that have subscribed for events based on the type and details of the event as well as handling asynchronous results from the resource template. The resource store is automatically created for a template that is passed to a widget. A user never explicitly creates or works directly with the resource store.

MyWidget.tsx

import { create, tsx } from '@dojo/framework/core/vdom';
import { createResourceMiddleware } from '@dojo/framework/core/middleware/resources';

import DataAwareWidget from './DataAwareWidget';
import userResourceTemplate from './userResourceTemplate';

const resource = createResourceMiddleware();
const factory = create({ resource });

export default factory(function MyWidget({ middleware: { resource }}) {
    return (
        <div>
            {/* The resource store is created internally for the template passed to the widget */}
            <DataAwareWidget resource={resource({ template: userResourceTemplate })}>
        </div>
    );
});

Middleware

The resource middleware is the interface required to work with resource templates and "resource-aware" widgets. The middleware exposes the complete API for working with resource templates.

MyResourceAwareWidget.tsx

import { create, tsx } from '@dojo/framework/core/vdom';
import { createResourceMiddleware } from '@dojo/framework/core/middleware/resources';

import FancyLoadingIndicator from './FancyLoadingIndicator';

// The resource item interface
interface ResourceItem {
    label: string;
}

// create the resource middleware passing the type of the resource required
// passing the generic type means that the `resource` property will
// be exposed on the widget's property interface
const resource = createResourceMiddleware<ResourceItem>();

// pass the created middleware to the `create` function
const factory = create({ resource });

export default factory(function MyResourceAwareWidget({ id, properties, middleware: { resource } }) {
    // de-structure the required resource APIs, these can also be accessed
    // directly from `resource`
    const { getOrRead, isLoading, createOptions } = resource;
    // get the `template` and `options` from the widgets properties
    // the options are optional so need to be defaulted using the
    // createOptions function from `resource`
    const {
        resource: { template, options = createOptions(id) }
    };
    // Call `getOrRead` to request the data based on the `template` and `options`
    const [items = []] = getOrRead(template, options({ page: 1, size: 20 }));
    // Check if the resource is current loading
    if (isLoading(template, options())) {
        // if the resource is loading return a fancy loading indicator
        return <FancyLoadingIndicator />;
    }
    // If the items have been loaded return them in a list
    return <div>{items.map((item) => <li>{item.label}</li>)}</div>;
});

Please see the resource middleware for more information.