The code for this post is available here.

Assumed knowledge: some prior experience with the terminal and ssh keys. This project builds upon a previous barebones boilerplate code project.

Before diving into building a web app, it’s best to get an idea of how the code will be structured. That is, formulate a design (or plan, architecture) for how to write the code in a way that it can scale without becoming incomprehensible. The best way to do this is to have an understanding of how we think about code, or how we mentally model the solution to the problem (application) we are trying to solve (build). The patterns used in this boilerplate are what I use to write code for prototyping things quickly, as it matches the way I model the flow and connections between different parts of the codebase. You might develop your own system, and have your own way of modelling things mentally.

Set up

  • Firstly, clone the repository, and checkout the code-design branch.

    • git clone git@github.com:drohen/js-canvas-boilerplate.git

    • cd js-canvas-boilerplate

    • git checkout code-design

  • Ensure dependencies are installed, run: npm i

  • Ensure the code runs in development mode: npm start

Dependencies

There are now 2 new dependencies added to this project:

Typings

There are now 3 global types available to this project, defined in typings/index.d.ts:

  • Observer: a generic observable interface describing an object with 3 methods:

    • next: a method that receives some data based on the generic type, called based on some logic followed by the observable that calls it

    • complete: a method that is called when next will no longer be called by the observable

    • error: a method that is called when an error occurs within the observable, likely resulting in no further calls to occur

  • MountFn: a generic function type definition, describing a function that receives an instance of some type that extends an HTMLElement (here it will be used with custom elements)

  • ClickObservable: a interface defining a readonly property that is an observable, as it has a single method subscribe that receives an Observer as the parameter.

Static files

At this stage, we only have 2 files concerning static data, the public/index.html and public/main.css:

  • The HTML file now includes 2 new template elements (defined by their unique id values), along with their contents:

    • ui-block only wraps a div element, which contains a slot element. The div will be used to define the layout flow of the element, while the slot defines where any appended nodes will be inserted.

    • ui-button wraps a button element. The button contains a call to action text.

  • The CSS file contains styles for these two components. As this file is imported into the shadow DOM of the custom elements, the child elements of the components are “namespaced” by using the host css pseudo-class.

Entrypoint

The application requires some kind of entrypoint. Per common programming convention, the core class of this project is called “Main.” Although, as this class isn’t automatically instantiated, it’s provided a static method that does the instantiation, and this is called immediately after the class definition. This has been added to the previously empty src/index.ts file.

Observables

As mention, one of the patterns that will be used in this project is a simplified version of the “observer.” Alongside the src/index.ts file created earlier, there’s now another file named src/subscriberHandler.ts. This file provides an “abstraction” for making a class or property “observable” by a “subscriber” object. The subscriber object can optionally provide next, complete, or error methods, per the type interface definition.

The example code in src/index.ts at this stage shows a property called uiLoader as providing a subscribe method, where parent Main class instance becomes a subscriber, waiting for new DOM components to be emitted. There’s also a call to next, within the update method. Although the component isn’t being used explicitly as a subscriber, it can still be passed the data it’s expecting to receive via this method.

Custom Elements

A relatively new addition to the web browser API is Web Components, which brings us custom elements. Also alongside the src/index.ts file is a UI directory, which contains many UI related files.

  • There are 2 helper classes, used for building and defining the UI:

    • uiLoader imports the custom element class objects, causing them to be defined within the custom element registry, so they can be successfully mounted to the DOM. It also takes care of emitting the instances of the components when they’re mounted, via an observable. There’s an example in this stage that attaches the custom elements to the DOM within the load method.

    • uiComponent is a base class inherited by the custom element class objects. It helps to emit the instance when mounted, attach the main stylesheet to the element’s shadow DOM, and exposes a callback for when the instance is mounted.

  • There are 2 custom element class objects, corresponding to the template elements defined in public/index.html:

    • uiBlock refers to the custom element with the id ui-block and is also an observer, whose next method is referenced in the src/index.ts update loop. This next method demonstrates how the DOM component can be manipulated programatically, while not needing a direct reference to any external context.

    • uiButton similarly refers to a custom element, ui-button, and provides an observable interface via the onClick property. It’s onDefined method, called via the base UIComponent class, adds an event listener to the button contain within the shadow DOM. On click, the button will call the subscriber’s next method.

Before next steps

By following the pattern outlined, you can rapidly build a web app with a manageable code structure. Other posts in this blog will build upon the design.

To view this code on the Internet, as demonstrated in the earlier post (if you have set up Github pages with your repository):

  • Build the code with npm run build.

  • Push to the main branch to update the UI.

  • It should now show an interactive button that loads a number counter to the view.

Next steps

Creating a Canvas and Input System for Prototyping Interactive Graphics