You should give modular-browser-jsx a try - It allows you to use Modular with JSX directly directly in the browser.
- Friendly errors 🚨
- Fast rendering ⏱️
- Data-binding ⛓
- Intuitive syntax 👩🏻‍💻
- A solid router 📡
- Awesome style-helpers 🌼
- Installation
- Error System
- Events
- Modular.el()
- Modular.render()
- className
- Style
- Bindings
- Modular.getBinding()
- Modular.setBinding()
- Modular.listenBinding()
- Modular.scan()
- Components
- The router
- Modular.router.navigate()
- __config__
- Why is this useful?
If you are using modular on a regular website, you can just include it with a <script>
-tag.
<script src="path/to/modular.js"></script>
npm i modular-ui
import Modular from "modular-ui";
https://cdn.jsdelivr.net/gh/KargJonas/modular/dist/modular.js
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:
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 |
Returns ac object, that can be transformed into a DOM-element with Modular.render()
.
Modular.el(TAG_NAME, ATTRIBUTES, CONTENT, CONTENT, ...);
-
TAG_NAME (String):
The element'stagName
.
( 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.
Converts a value into a DOM-Element and inserts it into another element.
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.
Because of the way Modular handles element-attributes it is necessary to use className
instead of class
in Modular.el().
Modular.el(
"h1",
{ className: "myVeryAwesomeClass" },#
"Hello World"
);
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.
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;";
const styleOne = "color: red; font-family: monospace;";
const styleTwo = {
color: "green",
":hover": {
color: "red"
}
};
const styleThree = {
"h1": {
color: "green"
},
"h1:hover": {
color: "red"
}
};
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!).
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.
$bind: {
ELEMENT_PROPERTY: BINDING_NAME,
OTHER_ELEMENT_PROPERTY: OTHER_BINDING_NAME,
...
}
Modular.el(
"h1",
{
$bind: {
innerHTML: "myTextBinding",
contenteditable: "myBooleanBinding"
}
},
"Username: John"
);
Allows you to get the current value of a binding.
let myVariable = Modular.getBinding("myFirstBinding");
Allows you to set the current value of a binding.
Modular.setBinding("mySecondBinding", "This is the binding's new content.");
Runs the provided function each time a change occurs in the specified binding. (You can have as many of these as you want.)
Modular.listenBinding("myOtherBinding", (newValue, event) => {
console.log("myOtherBinding just changed to " + newValue);
});
Converts an html-string into an array of Modular-elements. (Comments are handled as text.)
let myModularElement = Modular.scan(`
<h1 id="test-header">This is a test-header</h1>
<p>Test</p>
Text-node
<div>
Test
</div>
`);
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"
);
Modular has a minimalist router built-in. There are four major steps to implement it into your page.
Modular.router.routes = {
"PAGE_PATH": PAGE_VALUE
};
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.
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.
window.addEventListener("newroute", () => {
Modular.render(
Modular.router.page,
"#root"
);
});
// Initialize the router
Modular.router.init();
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"
);
});
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.
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
You can use Modular.router.navigate to switch to the desired url. This can be implemented into a link.
Navigates the router to the provided url.
Modular.router.navigate(PATH);
Modular.router.navigate("/users/premium/john_doe");
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.)
Frameworks like Modular allow you to create dynamic website content and to reuse parts of your page (as components) without major modification.
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.