Datasource

Datasource is the contract between Monster controls and data. Use it when plain demo state is no longer enough and you need stable read, write and state handling without pushing fetch logic into every component.

Tutorial Goal

Understand where transport logic belongs and how UI components should consume it

By the end of this tutorial you will know when to start with the base Datasource, when to switch to server-backed variants and how controls such as datatables and save buttons use the same contract.

Before You Start

Know how examples and runtime files are loaded

Datasource is not the first Monster topic. If imports, example files or the Playground still feel unclear, use First Page before this page.

Know the boundary

A datasource owns loading, writing and state transitions. Components should own presentation, option handling and user interaction, not raw transport code.

Think in public state, not ad-hoc fetches

Once multiple controls depend on the same records, read/write status and error handling need one shared contract. Datasource is that contract.

The Minimal Flow

1. Start from a datasource, not from a widget callback

The base class gives you one place to encapsulate records, selection, loading status and persistence rules.

import { Datasource } from "@schukai/monster/source/data/datasource.mjs";

2. Put record shape and behavior into one class

That keeps the transport contract explicit and stops UI code from guessing paths or payload shapes in random places.

class ProductDatasource extends Datasource {
    get defaults() {
        return Object.assign({}, super.defaults, {
            records: [],
            state: {
                loading: false,
                dirty: false,
            },
        });
    }
}

3. Use a server-backed datasource when transport matters

As soon as data comes from an API, move to the server datasource family rather than bolting fetch calls onto every control.

import { Server } from "@schukai/monster/source/data/datasource/server.mjs";

class ProductServerDatasource extends Server {
    get defaults() {
        return Object.assign({}, super.defaults, {
            read: {
                url: "/api/products",
                path: "items",
            },
            write: {
                url: "/api/products",
            },
        });
    }
}

4. Let controls consume the datasource through public hooks

Components such as datatables, save buttons or selectors should point to the datasource, not duplicate its read/write responsibilities.

<monster-datasource-rest
    id="products-rest"
    data-monster-option-features-autoinit="true"
    data-monster-option-read-url="/api/products"
    data-monster-option-read-path="items">
</monster-datasource-rest>

<monster-save-button
    data-monster-option-datasource-selector="#products-rest">
</monster-save-button>

Important mental model

Datasource is not just a fetch helper. It is the state boundary between UI and transport. Once a component bypasses that boundary, status handling, dirty tracking and persistence rules start to fragment.

Core Concepts

ConceptUse it forTypical mistake
recordsThe current data set a control should render or manipulate.Letting every control maintain its own copy of the same records.
readLoading records from a remote or local source in one declarative place.Triggering ad-hoc network requests from click handlers and render logic.
writePersisting changed records back to the backend through a stable contract.Posting from unrelated components that do not own the data policy.
stateRepresenting loading, dirty and error conditions for connected controls.Treating async state as hidden local flags that other controls cannot see.
selector bindingConnecting controls to an existing datasource element declaratively.Passing copied snapshots instead of sharing one datasource instance.

Where Datasource Fits Best

The contract becomes most valuable as soon as more than one control cares about the same data and status. That is where shared state and persistence discipline matter.

Datatables and filters

One datasource coordinates records, pagination, filters and persistence.

Save and change indicators

Buttons can reflect dirty or persisted state without guessing where the data came from.

Remote option sources

Select-like controls can consume normalized records instead of embedding fetch code inside the control.

Cross-control error handling

Notify or monitor components can react to one datasource error surface instead of many scattered try/catch blocks.

Common Mistakes

Putting fetch logic into the component

If the control owns HTTP details directly, reuse and testability drop immediately.

Splitting one dataset across unrelated stores

Dirty state, paging and writes become incoherent when each widget carries its own partial copy.

Using datasource as a generic dumping ground

Keep UI formatting and transport concerns separate. Datasource should own data contracts, not view rendering details.

From Tutorial to Real Control

Datasource to Save Button

Wire one data contract into a write action

SaveButton is a practical next step after this tutorial. The control only works well when dirty state, write lifecycle and completion feedback come from one coherent datasource contract instead of ad-hoc fetch calls spread across the page.

What to inspect

Look at state transitions, not just requests

Pay attention to how loading, dirty state and persisted state are modeled. The value of datasource is not the network call itself. The value is the stable state boundary that multiple controls can depend on without drifting apart.

Where to Go Next

Build a CustomControl

If your data contract is clear and you now need a value-bearing UI primitive, continue into CustomControl.

Read the API contract

For the full datasource hierarchy, continue to Datasource.

The current width of the area is too small to display the content correctly.