Skip to content
This repository has been archived by the owner on Dec 27, 2019. It is now read-only.
/ modular Public archive

A tool for building interactive interfaces.

License

Notifications You must be signed in to change notification settings

KargJonas/modular

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

You should give modular-browser-jsx a try - It allows you to use Modular with JSX directly directly in the browser.

Modular is a tool for dynamic, component-based page-generation.

Some neat stuff:

Table of Contents


Installation

If you are using modular on a regular website, you can just include it with a <script>-tag.

Example:

<script src="path/to/modular.js"></script>

npm:

npm i modular-ui
import Modular from "modular-ui";

CDN link:

https://cdn.jsdelivr.net/gh/KargJonas/modular/dist/modular.js


Errors

The development-build of modular (modular.dev.js) has a friendly error system built-in. The most common errors are hand-written and contain easy-to-understand information on how to fix your issue. An error might look like this:

Error


Events

There are some events that might come in handy. (All events are bound to the window-object)

Event-Name Info
prerender Dispatched before render
postrender Dispatched after render
newroute Dispatched after a route-change has ocurred

Modular.el:

Returns ac object, that can be transformed into a DOM-element with Modular.render().

Usage:

Modular.el(TAG_NAME, ATTRIBUTES, CONTENT, CONTENT, ...);
  • TAG_NAME (String):
    The element's tagName.
    ( e.g.: h1, div, p, mycustomtagname, ... ).

  • ATTRIBUTES (Object | Null):
    The attributes of the element.
    There are some special attributes such as style, $bind and __config__.

  • CONTENT [ String | Number | Array | Object (Modular-element) | Function | Element (html) | Null ]:
    The content of the element. You can use basically anything as content.

Only TAG_NAME is required.

Example:

El-Example


Modular.render

Converts a value into a DOM-Element and inserts it into another element.

Usage:

Modular.render(VALUE, ELEMENT_OR_SELECTOR);
  • VALUE [ String | Number | Array | Object (Modular-element) | Function | Element (html) | Null ]:
    The element that will be rendered
  • ELEMENT_OR_SELECTOR (Element | String): The parent element or it's selector.

Example:

Basic-Example


className

Because of the way Modular handles element-attributes it is necessary to use className instead of class in Modular.el().

Example

Modular.el(
    "h1",
    { className: "myVeryAwesomeClass" },#
    "Hello World"
);

Style

Modular has some stuff to offer that might help you with dynamic style:

  • If you want, you can use style-objects. Style objects will be transformed into global style, when the element is rendered.

  • These style-objects can either contain style, specifically for the current element or entire css rules, which style any of the element's children.

  • You can use functions as style. This might seem silly but it makes your code more readable and evaluates the returned object every time the element is rendered.
    Remember: Style functions must return either a String or an Object.

Note

If the provided style is of type string, inline-style is expected. The style will be scoped to the the element itself:

const myStringStyle = "color: red; font-family: monospace;";

Example:

Style-Example

Some more cases:

const styleOne = "color: red; font-family: monospace;";

const styleTwo = {
    color: "green",

    ":hover": {
        color: "red"
    }
};

const styleThree = {
    "h1": {
        color: "green"
    },

    "h1:hover": {
        color: "red"
    }
};

Bindings

Bindings are a way to "tie" an element's properties to a value. You could even tie an element's properties to the ones of another element.

To define a Modular-element's bindings, you can use the $bind-attribute. The attribute's value must be an object. Each key in the object corresponds to a property of the DOM-element, that is created from Modular.el() and each value is the name of a binding the property will be bound to. (The $bind-attribute will be removed upon render, so your DOM-elements stay nice and clean.)

If any changes occur in either in the binding or any of the elements, all elements that are bound to the binding are updated (But not re-rendered!).

Note

If an element's property is added to a binding, the binding is updated. This means that the binding's value will always be the one of the last element that was added.

Usage

$bind: {
    ELEMENT_PROPERTY: BINDING_NAME,
    OTHER_ELEMENT_PROPERTY: OTHER_BINDING_NAME,
    ...
}

Example

Modular.el(
    "h1",
    {
        $bind: {
            innerHTML: "myTextBinding",
            contenteditable: "myBooleanBinding"
        }
    },
    "Username: John"
);

Binding-Example


Modular.getBinding

Allows you to get the current value of a binding.

Example

let myVariable = Modular.getBinding("myFirstBinding");

Modular.setBinding

Allows you to set the current value of a binding.

Example

Modular.setBinding("mySecondBinding", "This is the binding's new content.");

Modular.listenBinding

Runs the provided function each time a change occurs in the specified binding. (You can have as many of these as you want.)

Example

Modular.listenBinding("myOtherBinding", (newValue, event) => {
    console.log("myOtherBinding just changed to " + newValue);
});

Modular.scan

Converts an html-string into an array of Modular-elements. (Comments are handled as text.)

Example

let myModularElement = Modular.scan(`
    <h1 id="test-header">This is a test-header</h1>
    <p>Test</p>
    Text-node

    <div>
        Test
    </div>
`);

Components:

A component is a compact, self-containing unit. One could be a function, another one an element, an array of functions, strings, numbers, an array of arrays, ... whatever. It's your choice.

A good practice however would be to put major parts of your page into functions. For example the topbar.

// This is your topbar
function Topbar() {
    return Modular.el(...);
}

// Rendering the topbar
Modular.render(Topbar, "#rootElement");

Also, it's a good idea to put elements, that you commonly use into a function and use arguments to customize the element's properties.

// This is just some style.
let myButtonStyle = {
    outline: "none"
}

// This is your Button-component
function Button(text, callback) {
    return Modular.el("button", {
        style: myButtonStyle,
        onclick: callback
    }, text);
}

This has the benefit of reducing code. Now you can have a million buttons if you like and you will still know, what is gong on.

Modular.render(
    // This is an array with some buttons
    [
        Button("This is my first button.",
        () => console.log("Button 1 was pressed.")),

        Button("This is my second button.",
        () => console.log("Button 2 was pressed.")),
    ],
    "#rootElement"
);

Components-and-Arrays-Example


The router

Modular has a minimalist router built-in. There are four major steps to implement it into your page.

1.) Configure the routes

Usage:

Modular.router.routes = {
    "PAGE_PATH": PAGE_VALUE
};

Example:

Modular.router.routes = {
    "path/to/your/page": Value,
    "users/**": OtherValue,
    "**": YetAnotherValue
};

The routes are checked one after the other. When a perfect match to window.location.pathname is found, Modular.router.element is set to the corresponding element and the newroute-event is dispatched (See: Events). (Router-Elements could be anything. It's up to you what you want to do with them. See Modular.render). ** matches anything.

2.) Respond to route-changes & initialize

In order to change the content on the page, you need to set up a system, that renders the desired content, when a change in the route occurs. To start the router, Modular.router.init() must be called.

Example.

window.addEventListener("newroute", () => {
    Modular.render(
        Modular.router.page,
        "#root"
    );
});

// Initialize the router
Modular.router.init();

Note

If you have elements, that you want to show up on any page, it would be very annoying to add your topbar-element to each value in Modular.router.routes. An easier way would be to add the element directly the element in Modular.render:

window.addEventListener("newroute", () => {
    Modular.render(
        [
            Topbar, // The topbar
            Modular.router.page // The page
        ],
        "#root"
    );
});

3.) Server

If you use the router to build a single-page-app, you have to configure the server in a way to ignore all requests for sub-pages and only respond with your main (html) file and the resources required.

Apache:

If you are using Apache, you can just create a file inside your website's folder called ".htaccess" containing the following code.

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^ - [L]
    RewriteRule ^ index.html [L]
</IfModule>

This snippet uses the Apache's RewriteEngine. If you are on Debian, Ubuntu or something similar, you can enable it using these commands:

sudo a2enmod rewrite
sudo service apache2 restart

4.) Navigate

You can use Modular.router.navigate to switch to the desired url. This can be implemented into a link.


Modular.router.navigate

Navigates the router to the provided url.

Usage

Modular.router.navigate(PATH);

Example

Modular.router.navigate("/users/premium/john_doe");

__config__

Every modular element is an object, which contains the attributes of the corresponding DOM-element and a special __config__ attribute. __config__ contains the tagName and the content of the element but also some other stuff such as the type of the Modular-element, the render method, the element's bindings, the DOM-element and the element's render-method itself. (The $bind-attribute will be removed upon render.)


But why is this useful?

Frameworks like Modular allow you to create dynamic website content and to reuse parts of your page (as components) without major modification.

Example use-case:

You have an array of 1000 users. You want to display every user in a list and you want to be able to delete and add users in that list dynamically. Also you want that all premium users have a shared tag.