API reference
This page provides a reference to the Javascript API for wecco.
Apps
wecco apps are a combination of view functions, data models and mutations on
those models in combination with a well-defined lifecycle that causes the
views to be invoked any time the data changes. Those apps follow the
model-view-update architecture pattern which is often described as the
elm architecture.
Contexts
wecco apps use an idiom called contexts which means, that all functions
that make up an app and which are called by the framework
receive a context parameter which contains data and callbacks for each app
part.
In wecco, by convention, a context parameter is usually handled via object
deconstruction, cherry-picking only those properties needed.
The following sections describe the types of context and their usage.
View
A view defines how the UI looks (i.e. which HTML elements to render) given some data (the model).
A view is just a function that conforms to the following type definition:
type View<MODEL, MESSAGE> = (ctx: ViewContext<MODEL, MESSAGE>) => ElementUpdate
interface ViewContext<MODEL, MESSAGE> {
emit: MessageEmitter<MESSAGE>
get model(): MODEL
}
The view function produces an ElementUpdate, which is how wecco updates
element's content. This can either be a string, a value returned from the
wecco.html string tag, and HTMLElement, null or an array of those values.
The context passed to the view contains the model to render as well as
a callback emit which is used to emit a message to be handled.
Update
An update function mutates the data (the model) based on some input, which is called a message. Think of a message as something like a command pattern object which describes what operation to perform and which arguments to use for this operation.
The result of such an update cycle is a (possibly new) version of the data to render.
An update function must conform to the following type definition:
type Updater<MODEL, MESSAGE> = (ctx: UpdaterContext<MODEL, MESSAGE>) => ModelResult<MODEL>
export interface UpdaterContext<MODEL, MESSAGE> extends ViewContext<MODEL, MESSAGE> {
get message(): MESSAGE
}
Notice the return type ModelResult<MODEL>. This type union includes instances
of MODEL, Promises that resolve to a MODEL as well as the sentinel
NoModelChange value (which signals, that nothing needs to be updated).
wecco.createApp
Creates a new app. The arguments define the app's model, view and update handler.
modelInitializer- initializer to create the initial model - either synchronously or asynchronouslyupdater- applies update messages to the model - either synchronously or asynchronouslyview- renders the model - always synchronously
The function returns an App (see below) that can be used to control
the app by emitting messages.
The following "sketch" shows the "wireframe" for each app:
type Model = // ...
type Message = // ...
function update({model}: wecco.UpdaterContext<Model, Message>): Model {
// ...
}
function view ({ emit, model }: wecco.ViewContext<Model, Message>): wecco.ElementUpdate {
return wecco.html`
...
`
}
document.addEventListener("DOMContentLoaded", () => {
wecco.createApp(() => /* some model value */, update, view)
.mount("#app")
})
wecco.App
An App is returned from wecco.createApp. It provides two methods:
mount- which "mounts" the app onto some HTML elementemit- which can be used to send messages to the app used to control the app from the outside.
wecco.NoModelChange
A sentinel value representing the updater's decision not to update the model
and skip the re-rendering cycle. May be returned from an update function to
indicate that side effects happend (such as updating the storage) but no
re-rendering is needed.
Custom Elements
wecco.define
define is used to define a new web component which is also a custom web element.
The function accepts the following arguments:
name- the name of the custom element. This name is used as the custom element's tag name, so it must follow the custom element specs (i.e. the name must contain a dash)renderCallback- the render callback will be called whenever the elements content needs to be updated. The callback receives aRenderContext` parameter (see below) which, by convention, is handled by object deconstruction.options- an optional options object. See below for additional information
The value returned from define is an instance of a ComponentFactory which
can produce instances of the defined component by passing in initial data.
const CountClicks = wecco.define<number>("count-clicks", ...)
// ...
const countClicksElement = CountClicks(3)
someHTMLElement.appendChild(countClicksElement)
Render Callback
The context passed to each invocation of the render callback, follows the following interface:
interface RenderContext<T> {
get data(): T
requestUpdate: () => void
emit: (event: string, payload?: any) => void
addEventListener: (event: string, listener: (payload?: any) => void) => void
once: (id: string, callback: OnceCallback) => void
}
data- getter to get the data to be rendered. Almost all custom elements use thisrequestUpdate- a callback to be bound to some trigger from within the rendered HTML. Invoking this callback signals to the framework thatdatahas been updated and rendering should be triggered again for the element's rendered content to reflect this change. As withdata, almost all custom elements use this.emit- can be used to emit aCustomEventfrom the element. Pass in the event's name and an (optional payload). This method is especially usefull when using custom events.addEventListener- used to subscribe forCustomEvents.once- register some callback to be called exactly once for each instance of the element. Use this to register a callback that, loads inital data (i.e. viafetch) or register timeouts, intervals, ...
Options
You can pass an optional options parameter to define which supports a
two-way binding of data fields to either HTML element attributes or
JavaScript properties of the element. The options type is defined as
interface DefineOptions<T> {
observedAttributes?: Array<keyof T>
observedProperties?: Array<keyof T>
}
Full Example
The following full example shows a simple click-counting button:
import * as wecco from "@weccoframework/core"
interface CountClicks {
count?: number
}
const CountClicks = wecco.define("count-clicks", ({ data, requestUpdate }: wecco.RenderContext<CountClicks>) => {
data.count = data.count ?? 0
return wecco.html`<p>
<button
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
@click=${() => { data.count++; requestUpdate() }}>
You clicked me ${data.count} times
</button>
</p>`
}, {
observedAttributes: ["count"],
})
wecco.component
Creates an instance of a defined element which will use the given data and
will be added to the given host element. This is an alternative approach to
using the ComponentFactory returned by define which does not require a
handle to the component factory but uses the browser's built-in registry for
custom web components.
The following two snippets as essentially the same:
vs.
wecco.define("some-component" // ...
const componentData = // ...
wecco.component("some-component", componentData).mount("#body")
The arguments passed to component are:
componentName- the component's namedata- the bound datahost- the host to add the component to. This is optional; you can leave it out and add the element to the DOM by any other means.
Rendering HTML
wecco.updateElement
updateElement applies the given update request to the given target.
target- the target to update. This can be a DOMElementor a CSS selector string.request- the update "request". May be an update function or anElementUpdater. The following things can be used:nullto remove any content from the targetstringwhich is used as inner text (note that this is notinnerHTML)- a DOM
Elementwhich gets appended as a child of target - a
wecco.ElementUpdateFunctionwhich is callback function that receives the target to update - a
wecco.ElementUpdateran interface for types used internally by wecco (but may be used externally as well). See the documentation onwecco.htmlandwecco.shadowbelow for the most common uses of this type - an array of any of the above
notifyUpdatedwhether to send an update event after the element update
updateElement emits custom events to notify any subscribers that an update happens/happend. The following
events are emitted:
updatestart- emitted for thetargetbefore update startsupdateend- emitted for thetargetadter update endsupdate- emitted for every child nested belowtargetthat was visited during the update
Subscribe to those event in case you need to handle custom processing such as managing animations.
wecco.html
Used as a tag for a template string to create an ElementUpdater that updates
a DOM node. See HTML tag template string reference for details.
wecco.shadow
Used to create a HTML ShadowDocument to isolate an element from the surrounding
CSS and JavaScript.