Dojo Resources
Dojo Resources is designed to provide a consistent pattern to make widgets "resource aware". Resources can be configured to work with any type of data sources. A resource is essentially a Dojo managed data store with built-in caching, pagination, and filtering. 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 as 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 they 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, but 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 includes a data
and an id
that is required to identify the instance of the resource and is 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, such as 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, page size, and a set of controls which includes a put
that is 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 to use the resource
middleware which 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>;
});