Docs

The perfect choice for your next project

App State Management

GlueCodes Studio has been built to support you with standards. You won't see much magic here when it comes to coding. The structure is made with HTML extended with attribute directives and extended tags to achieve reactivity. Styles are CSS but smartly linked and powered by CSS Modules. Similarly the logic is written with JavaScript ES7+ with a few rules which allow to achieve the level of automation we aimed for:

  • Function first approach where actions are pure functions.
  • Each action exports default function which handles the logic.
  • Since there is a single [dependency] JSON, you can access the imported things by imports global e.g. imports.someLocalName
  • You can access window object and any [global variables] via global variable

In GlueCodes Studio your business logic is spread across app actions which store their returned/resolved values in a single store. The data changes flow in one direction and UI reacts to changes of the store, updating the only affected parts. The DOM diffing happens in-compilation time and is powered by SolidJS.

There are two types of actions; the ones that supply data before rendering called Providers and those triggered by a user called Commands. Their both returned/resolved values are accessible by their names in UI and [widget input parameters]. In your UI, specifically in slots and reusable slots you get access to global variables actions and actionResults. The variable actions is an object of Commands you can call to perform an action e.g. to return/resolve fetched data.

Let's imagine calling actions.fetchUser. When called (typically on a DOM event), it'll implicitly write to the store and the result will be accessible via actionResults.fetchUser. In the same HTML piece (and also anywhere on the page) you can call a Command and display its result, however bear in mind that initially, before it's ever called the result of a command defaults to null.

In order to fetch data prior to rendering you wouldn't call actions.fetchUser Command. Instead you'd create a Provider and simply by using one of directives or accessing it via global actionResults (e.g.actionResults.fetchUser) in an extended tag, you'd tell the studio to call it before the UI is rendered. Providers also write to the store by returning/resolving and their results are accessible in slots and resuble slots via global variable actionResults. Providers pass through a single argument which is a snapshot of the store, by convention named actionResults. You can set their execution order and have multiple Providers which do a single task and pipe through data to compose the final result. You can also feed your app with incoming data, just see [live providers].

A typical scenario would be to use Commands combined with Providers. Let's imagine you want to add a record to a list so on the same page you have a form and data table. In this case you'd use a Provider e.g. getRecords and Command addRecord. On the form submission, you would call actions.addRecord and then reload providers to give you fresh records. This can be simply done by awaiting actions.addRecord and calling built-in . For more details see [built-in commands].

Note: if a Command or Provider doesn't exist and you reference it in directives or extended tags, the [implementation assistant] will notify you to create it.

Store

It's a SolidJS state which keeps results of Commands and Providers by their names along with few built-in objects like: errors, parseRootNodeDataset and route.

Commands

Command is an action triggered by a user typically via DOM event. It's a function exported as default and it can take any arguments. It can be async and by returning it writes to a store. See the above section to understand its relation with the store. It's wrapped in a single [error handler] which catches whatever errors is thrown and errors are accessible in slots, reusable slots and [widget input parameters]. There are several built-in commands:

  • cancelError - it takes a destructured errorName and marks error as cancelled meaning that the user has already dealt with it. Literally, it means setting isCancelled property to true so in UI you can use it in conditionals.
  • redirect - it takes url to which hard-redirect is to be made.
  • reload - reloads providers, optionally it passes type which can be captured in any provider by accessing the store's reload. Typically you'd have an early-returning if statement which checks for actionResults.reload === ‘someReloadType’ to avoid unnecessary executions. So e.g. in getSomething provider you'd return actionResults.getSomething which would be a reference to earlier returned value.
  • runTogether - it passes commandsToRun which is an array of arrays looking like: [[‘command1’, arg1, arg2...], ...] and runs each - command caching their results before writing them together to the store.
  • onError - an event which is triggered when an error is caught by [error handler]. It passes the error object.
  • onStoreChanged - an event which is triggered when the store changes. It passes an array of action names which caused the changes.

Commands have access to the following global variables:

  • imports - an object of imported functions via [dependencies]
  • global - a combine window and [global variables] object

Providers

Provider is an action executed prior to UI rendering. It's a function exported as default and it passes through a snapshot of the store. You can access results of previously executed Providers by accessing its only argument, by convention named actionResults. You can set the order of execution, [see how]. It can be async and by returning it writes to a store. See the above section to understand its relation with the store. There are several built-in objects in the store:

  • errors - an object of errors by their names, each error has isCancelled property which tells if a user has already dealt with it e.g. removed an input field value which wasn't valid.
  • parseRootNodeDataset - dataset of a DOM node which is a root of the app.
  • route - window.location

Providers have access to the following global variables:

  • imports - an object of imported functions via [dependencies]
  • mediaFiles - an object of media files e.g. mediaFiles['some-image'].png
  • global - a combine window and [global variables] object

Live Providers

There are cases when you want to feed your UI with incoming data. E.g. data coming from a Web Socket or some non-DOM event. With GlueCodes Studio you're covered.

To put it into perspective let's compare this normal Provider:

  • It can be async or non-async.
  • It passes a single argument which is a snapshot of the store, by convention named actionResults.
  • It resolves its data by returning.

with this Live Provider:

  • It must be non-async.
  • It passes a single argument which is a snapshot of the store, by convention named actionResults.
  • It must return a function, either async or non-async.
  • The function it returns, passes destructured:
    • asyncResults - an object of promises of other live providers so you can await other Live Providers.
    • hasBeenInitialized - a flag which tells whether provider has been already executed. Useful to wrap any event registering code so it just happens once.
    • provide - a callback which needs to be called in order to resolve the provider data. Every time it's called with new data it'll write to the store which then affects the UI which accesses its result.

Reusable Functions

In classical MVC apps you have a separation between Controller and Model. In GlueCodes architecture Commands and Providers play the role of Controllers. By the book fat Controllers and especially those with duplicated data access are a bad idea. If you need another layer to e.g. share data accessors among different Commands and Providers, then Reusable Functions might come handy. For now they are available only for enterprise members who can create their own repositories of widgets and shape the entire business domain with Commands, Providers and Reusable Functions.

Widget Input Parameters

Parameters for a widget are supplied by a function which returns an object. It passes destructured actions and actionResults which are Commands and a snapshot of the store respectively. The function can be understood as an adapter/connector between the app and widget. Widgets rely on SolidJS reactivity so quite often you'll see parameters whose names start with "get" or "is" and pass arrow functions. That's how reference to the store is kept so the widget can react to its changes. Widget input parameter functions have access to the following global variables:

  • mediaFiles - an object of media files e.g. mediaFiles['some-image'].png
  • global - a combine window and [global variables] object

Error Handler

There is a single error handler which catches any errors thrown by Commands. Whenever an error is thrown, it ends up in the store's errors object by its name. You can access errors in slots, reusable slots, [widget input parameters] and [providers]. By convention you'd access e.g. SomeErrorName error by actionResults.errors.SomeErrorName