/userinterface.js

A small library to build front-end JavaScript applicationss

Primary LanguageJavaScriptMIT LicenseMIT

userinterface.js Build Status Gitpod Ready-to-Code

I made and maintained for a few years a browser extension and I can remember that I would often write tons of code that would later make good features. However those were not easy to maintain and I had two things I wanted to do: one is that I would have a way to write code that could be reused accross the entire application and the second to centralize events.

userinterface.js was built around the idea that logic relating to how the visual looks and how the visual works should be distinguished.

At the same time userinterface.js is providing new ways to write and organize your UI.

Another advantage of using userinterface.js is code reusability through principles of separation of concerns as well UI mechanisms abstraction.

Summary

Getting started

Prerequisites

None, 100% pure Vanilla JS :)

Installing

Scaffold

To get started with userinterface.js all you need to do is scaffold a new project by following the instructions on the userinterface.js-skeleton repository.

Standalone

In order to be able to use userinterface.js in the browser you can use git submodules.

Run git submodule add https://github.com/thoughtsunificator/userinterface.js.git lib/userinterface.js at the root of your project.

userinterface.js is located in the lib/ folder at the root of your project.

Model

A Model is an object representation of a Node. It has three required properties depending on the method: name, method and properties or callback,

The name property will be the identifier of your model it will be used whenever you need to run your model. The method property will describe how your model should be ran. The properties and callback properties will contain the properties of your Elements.

A Model often goes along with a Binding and an Object.

Basic model

We create a model named simple model with the method appendChild it has a two LI element children that have the className simplemodel and textContent My first simple model. Yes you got it, every properties (except children) will be set to the element.

Code:

UserInterface.model({
	name: "simplemodel",
	method: UserInterface.appendChild,
	properties: {
		tagName: "li", // required
		className: "simplemodel",
		textContent: "My first simple model"
	}
});

UserInterface.runModel("simplemodel", { parentNode: document.querySelector("ul") });

Output:

<ul>
	<li class="simplemodel">My first simple model</li>
</ul>

Children

In the previous example we created a simple model, but what if we wanted to do more and add some children to it ? The children property is here for that, it is an Array where you can specify child elements.

UserInterface.model({
	name: "children",
	method: UserInterface.appendChild,
	properties: {
		tagName: "div",
		className: "model",
		children: [
			{
				tagName: "div",
				className: "child",
				textContent: "My first child"
				// and so on..
			}
		]
	}
});

UserInterface.runModel("children", { parentNode: document.body });

Output:

<body>
	<div class="model">
		<div class="child">My first child</div>
	</div>
</body>

Callback

Models are required to have either the properties property or callback property, but exactly what does the callback property do ? It is used when you want to echo some data in your model.

For example here, we have a model called echomodel that has the callback property. This property works the same as the properties property does except that an extra step is added before your model is ran. The callback will return a properties object accordingly to the data you passed through runModel.

UserInterface.model(
	name: "echomodel",
	method: UserInterface.appendChild,
	callback: data => ({
		tagName: "p",
		className: "echomodel",
		textContent: "My "+data.text+" model"
	})
);

UserInterface.runModel("echomodel", { parentNode: document.body, data: {"text": "echo" } });

Output:

<p class="echomodel">My echo model</p>

Binding

A Binding is a callback function that, when bound to a model, is automatically called whenever the model has ran. Bindings will make your models more alive, an example of that would be adding an event listener to your model, that is the place where you will be doing it.

You can also do much more such as using event listeners to connect all of your models together!

A Binding is way to give life to your models enabling them to do things whenever their respective method is executed. That means if you want to add a listener to an Element that's where you will be doing it.

In this example we will change the textContent of our model root element.

UserInterface.model({
	name: "button",
	method: UserInterface.appendChild,
	properties: {
		tagName: "button"
	}
});

UserInterface.bind("button", function(element) {
	element.textContent = "bound";
});

UserInterface.runModel("button", { parentNode: document.body });

Output:

<button>bound</button>

Objects

Objects are the backbone of your models they will store and manipulate data for your Binding. That's where you want to hide the complicated stuff.

Listeners

Listeners allow your models to communicate with each others.

In this example we are creating and running a model called myModel that will himself run another model and pass it the context myObj.

Contexts represent a reserved area for models to communicate with each others, they're often represented as Object but could pretty much be anything.

After the second model ran it will listen to the "greeting" announce. Did you notice the event listener in our first model ? Yes, whenever our first model is clicked it will announce "greeting" to the context myObj and pass it an empty object as data. This empty object could also be anything that you want to pass to the other model.

When your model receives an announce it also comes along with data.

UserInterface.model({
	name: "myModel",
	method: UserInterface.appendChild,
	properties: {
		tagName: "div"
	}
});

UserInterface.model({
	name: "someobscuremodel",
	method: UserInterface.appendChild,
	properties: {
		tagName: "div"
	}
});

UserInterface.bind("myModel", function(element) {

	const _myObj = new Obj();

	element.addEventListener("click", function() {
		UserInterface.announce(_myObj, "greeting", {});
	});

	UserInterface.runModel("someobscuremodel", { parentNode: document.body, bindingArgs:[_myObj] });

});

UserInterface.bind("someobscuremodel", function(element, myObj) {

	UserInterface.listen(myObj, "greeting", function(data) {
		// do something useful with data or greet back
	});

});

UserInterface.runModel("button", { parentNode: document.body });

Methods

  • appendChild Append your model to the target

  • insertBefore Insert your model before the target

  • removeElement Remove the target

  • replaceElement Replace the target with your model

  • updateElement Update the target according to your model

  • wrapElement Wrap the target inside your model

  • removeListeners Remove the listeners of the target

API

model(model)

Load a model

bind(name, callback)

Link a model to a "binding", that is a callback function

runModel(name, parameters)

Update the DOM accordingly to a model

createNodes(properties)Array.<Element>

Transform a model into one or many Elements

getModelProperties(name, [data])Object

Returns the properties of a model

listen(context, title, callback)

Load a listener

removeListener(listener)

Remove a listener

announce(context, title, content)

Message one or many listeners

model(model)

Load a model

Kind: global function

Param Type Description
model object
model.name string The name of the model
model.method string One of the following methods name: appendChild, insertBefore, removeElement, updateElement, replaceElement, wrapElement, clearListeners
model.properties Object Processed properties along with any properties an Element¹ can have
model.callback function Callback of processed properties along with any properties an Element¹ can have
[model.properties.children] Array.<Object> An array of the "properties" object

bind(name, callback)

Link a model to a "binding", that is a callback function

Kind: global function

Param Type Description
name string The name of the model
callback function The function binding the model

runModel(name, parameters)

Update the DOM accordingly to a model

Kind: global function

Param Type Description
name string The name of the model
parameters Object The parameters of the model
parameters.parentNode Element The target Node
[parameters.data] Object The data that will be echoed on the model
[parameters.bindingArgs] Array The arguments that go along with the binding

createNodes(properties) ⇒ Array.<Element>

Transform a model into one or many Elements

Kind: global function Returns: Array.<Element> - An array of Elements¹

Param Type Description
properties Object | function Processed properties along with any properties a Element can have or a callback returning them

getModelProperties(name, [data]) ⇒ Object

Returns the properties of a model

Kind: global function Returns: Object - The "properties" object of the model

Param Type Description
name string The name of the model
[data] Object The data that will be echoed on the model

listen(context, title, callback)

Load a listener

Kind: global function

Param Type Description
context * Where the announce will be broadcasted
title string The content of the message
callback function

removeListener(listener)

[removeListener description]

Kind: global function

Param Type Description
listener Object

announce(context, title, content)

Message one or many listeners

Kind: global function

Param Type Description
context * Where the announce will be broadcasted
title string The title of the announce
content * The content of the announce

Common errors

Cannot set property 'binding' of undefined

UserInterface.js could not find the model specified when calling UserInterface.bind.

Cannot destructure property 'method' of 'model' as it is undefined.

UserInterface.js could not find the model specified when calling UserInterface.runModel.

Open an issue if you think your issue is not listed above.

Collection

userinterface.js also provides a collection that contains a few basic models to get you started.

Demos

Running the tests

npm install

Then simply run npm test to run the tests