dojo dragon main logo

Dojo Resources

Dojo Resources is designed to provide a consistent pattern to make widgets "resource aware". Resource can be configured to work with both any type of data sources. The resource is essentially a Dojo managed data store with caching, pagination and filtering built in. Coupled with the resource middleware, resources allow consistent, source-agnostic data access for widgets, without the widgets needing to know about the fetching implementation or the raw data format.

Feature Description
Support for memory resources Support for using resources an in-memory data set.
Single data source Resources allow creation of a single source of data for a given template that can be shared between multiple widgets using the data middleware.
Support for async and sync data reads Resource templates can read data in any way they like - once data becomes available, the resource middleware reactively invalidates any affected widgets.
Data transforms Allows specifying the data format that a widget requires, and transparently transforms source data into the expected output format for the widget to consume
Consistent Resource Options Resource options objects are passed to all api functions ensuring that all api functions are pure and provide only the data that was requested.
Sharable Resource Options Resource options can be shared between widgets via the resource middleware, allowing multiple widgets to react to resource changes.

Basic Usage

In order to work with Dojo resources widgets need to use the resource middleware created with the createResourceMiddleware factory from @dojo/framework/middleware/resources. There are two types of "resource-aware" widgets, widgets that expose a resource on their property API and widgets that need to use a resource internally. The same factory is used to create both types of middleware, the main difference is for widgets that require resources to be passed via properties, a resource type is needed on creation.

Using the resource middleware enables working with resource templates in your widget. Resources templates are created using the createResourceTemplate and createResourceTemplateWithInit factories from @dojo/framework/middleware/resources. Dojo resources also provides a utility factory createMemoryResourceTemplate which can be used to create a resource template that can be populated on initialization and works with the data in the client. The initialization include data and an id that is required it identify the instance of the resource and are passed with template into the resource middleware to use with a "resource" aware widget.

App.tsx

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

const resource = createResourceMiddleware();
const factory = create({ resource });
const myTemplate = createMemoryResourceTemplate<{ foo: string }>();

const App = factory(function App({ id, middleware: { resource } }) {
    return <DataAwareWidget resource={resource({ template, initOptions: { id, data: [{ foo: 'string' }] } })} />;
});

Customizing a template's data source

Dojo resources can be configured for a user defined data source, for example a RESTful API. This is done by passing an object with a read function that is responsible for fetching and setting the external data. The read function receives the request that contains details including the offset and page size and a set of controls, including put that need to be used to "set" the read response.

userResourceTemplate.tsx

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

export default createResourceTemplate({
    read: async (request: ResourceReadRequest, controls: ResourceControls) => {
        // The template is injected with read request, offset, size and query
        const { offset, size } = request;
        // The request details are used to determine the data to fetch
        const response = await fetch(`https://my.user.endpount.com?offset=${offset}&size=${size}`);
        const data = await response.json();
        // The template needs to set the response using the resource controls put function
        // along with the original request
        controls.put({ data: data.data, total: data.total }, request);
    }
});

Accessing data within a widget

A "resource aware" widget needs use the resource middleware that provides an API to work with the resource template. The resource middleware needs to be created using the createResourceMiddleware factory from @dojo/framework/core/middleware/resources, passing an interface that defines the expected resource data structure for the widget.

Accessing data in the widget is performed using the getOrRead function from the resource middleware. getOrRead requires options to be passed that tell the resource what data is required, these options should be accessed using the options function. The getOrRead method will return the data if it is available, otherwise it will call the read function of the resource template. If the read function on the resource template is asynchronous the result will be [undefined] while the data is being fetched and the widget will re-render when the data is available.

ResourceAwareWidget.tsx

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

interface ResourceData {
    value: string;
}

const resource = createDataMiddleware<ResourceData>();

const factory = create({ resource });

export const DataAwareWidget = factory(function DataAwareWidget({ id, properties, middleware: { resource } }) {
    const { getOrRead, createOptions } = resource;
    const {
        resource: { template, options = createOptions(id) }
    } = properties();
    const [items] = getOrRead(template, options());
    if (items) {
        return <ul>{items.map((item) => <li>{item.value}</li>)}</ul>;
    }
    return <div>Loading...</div>;
});