Skip to content

Usage

This page gives you an overview of how wecco can be used. This is your page to go especially of you are new to wecco. If you have specific questions concerning the API the JavaScript API has an in-depth reference. For questions on how to write template strings see the HTML template reference.

A wecco application

The following code contains a full "app" for a button that counts the number of times a user clicked it.

import * as wecco from "@weccoframework/core"

class Model {
    constructor(public readonly count: number, public readonly explanation: string) {}

    inc() {
        return new Model(this.count + 1, this.explanation)
    }
}

type Message = "inc"

function update(ctx: wecco.AppContext<Message>, model: Model, message: Message): Model {
    return model.inc()
}

function view (ctx: wecco.AppContext<Message>, model: Model) {
    return wecco.html`
    <p>${model.explanation}</p>
    <p>
        <button class="btn btn-primary" @click=${() => ctx.emit("inc")}>
            You clicked me ${model.count} times
        </button>
    </p>`
}

document.addEventListener("DOMContentLoaded", () => {
    wecco.app(() => new Model(0, "Click the button to increment the counter."), update, view, "#count-clicks-app")
})

wecco apps use the Model-View-Update architecture pattern. This pattern separates the logic into a model that gets rendered via view functions. Updates to the model happen via a central update function.

In the example above the Model class represents the model: a simple counter. Note that inc method the provides the "business capabilities" of the model.

An instance of the model will be rendered by the view function. This function is called by wecco with the current model instance and a context which can be used to emit update messages. The view function uses a tagged template string to create the HTML. The template tag wecco.html is very powerful. It compiles the template string into a hidden HTML <template> element and reuses that everytime the view function is executed. While this allows the developer to simple return a template string, under the hood a lot of caching and reusing is happening to only update what is changed.

The update function is responsible for executing updates on the model. This function is called by wecco everytime an update Message is emitted. The function receives the current model instance and the message and provides a new (or updated) model reference which will then get rendered again.

The call to wecco.app brings these functions together and starts the app. The rendered content will go to the element identified by the CSS expression #count-clicks-app. The first parameter is the "model initializer". This function is called once at the very start of the app to produce the initial model. Use this function to implement URL routing or restore a previous model from the local or session storage.

A custom element

wecco also provides a simple API for defining custom elements. These can be used standalone or as part of an app.

Here is the same count clicks example with a custom element:

import * as wecco from "@weccoframework/core"

interface CountClicks {
    count?: number
}

const CountClicks = wecco.define("count-clicks", (data: CountClicks, context) => {
    data.count = data.count ?: 0

    return wecco.html`<p>
        <button class="btn btn-primary" @click=${() => { data.count++; context.requestUpdate(); }}>
            You clicked me ${data.count} times
        </button>
    </p>`
}, "count")

wecco.define is used to define a custom element. It returns a factory function that can be used to create instances of the web component. In addition it registers the custom element's name so that HTML references to the element will be resolved.

The first parameter to wecco.define defines the name of the component and must follow the custom webcomponent's conventions (i.e. it must contain a dash).

The second parameter is the render callback. The render callback is called everytime the component should update.

The first parameter passed to this callback is the component's data. Using Typescript this parameter's type is defined using a generic type parameter. You commonly use an interface to describe the type.

The second parameter is the notify update callback. It can be used to notify the component that something has changed and the component should update its content. We use this callback to notify when the user has clicked the button and we updated the data-attribute count.

The last line creates an instance of the component passing in an object that implements CountClicks - the data interface - which initializes the component's data state. The result is an instance of HTMLElement which can be added to the DOM using plain DOM manipulation functions. The object also provides a convenience method mount which can be used to add the element to dom: Simply pass in a HTMLElement or a string which is passed to document.querySelector in order to obtain the element to add the new element as a child.

Check out the examples to see these two in action as well as the classical todo app.