MODUX
A framework used in front end application creation
Installation
npm install @crispcode/modux --save-dev
You can also create a basic project structure without the need to install modux, by using the npx command:
npx --package=@crispcode/modux -c "modux boilerplate basic create"
How to use
Modux has the following commands available from the command line:
Command|Description
-|-
modux start
| Starts the local modux project in development mode
modux build
| Compiles the project for production. Output will be in the build
directory
modux boilerplate
| Provides information on modux boilerplate. This is used to create project structures based on a template
Add to your package.json scripts:
"scripts": {
"start": "modux start",
"build": "modux build"
}
To run use: npm start
or npm run build
Documentation & Testing
Clone the modux repository to your machine and use the following commands:
To generate a documentation use npm run docs
If you want to check functionality you can use npm run test
Getting started
You can check the manual files for a quick introduction into modux. You can get started here.
Polyfill
In order to support older versions of browsers, you can use polyfill.io
Modux classes
Name | Usage | Description |
---|---|---|
Router | import { Router } from '@crispcode/modux' |
A static class used to manipulate states and urls |
Component | import { Component } from '@crispcode/modux' |
The Component class. Components are the backbone of the application |
Module | import { Module } from '@crispcode/modux' |
The Module class. Modules are the main part of modux |
Utils classes
Name | Usage | Description |
---|---|---|
approx | import { approx } from '@crispcode/modux' |
Used to approximate a number to a certain number of decimals |
Communication | import { Communication } from '@crispcode/modux' |
The Communication class, used to handle http requests |
Cookie | import { Cookie } from '@crispcode/modux' |
A static class used to manipulate cookies |
DateTime | import { DateTime } from '@crispcode/modux' |
A Date class wrapper |
Device | import { Device } from '@crispcode/modux' |
A static class used to get device information |
extend | import { extend } from '@crispcode/modux' |
Extends an object with another object |
font | import { font } from '@crispcode/modux' |
A font loader |
html | import { html } from '@crispcode/modux' |
Convert string to html |
isNumber | import { isNumber } from '@crispcode/modux' |
Checks if the value is a number |
isObject | import { isObject } from '@crispcode/modux' |
Checks if the object is an Object |
loader | import { loader } from '@crispcode/modux' |
The Loader class is used to preload files |
logger | import { logger } from '@crispcode/modux' |
A class to mimic window.console |
loop | import { loop } from '@crispcode/modux' |
Loop through a collection Object or Array |
radians | import { radians } from '@crispcode/modux' |
Convert an angle from degrees to radians |
rnd | import { rnd } from '@crispcode/modux' |
Generate a random number between two values |
scroll | import { scroll } from '@crispcode/modux' |
A library used for scrolling window or an element |
sounds | import { sounds } from '@crispcode/modux' |
A class used to manipulate Sounds |
uid | import { uid } from '@crispcode/modux' |
Generates a random unique identifier |
Url | import { Url } from '@crispcode/modux' |
A class used to manipulate urls |
Configuration
You can create a file called modux.config.js
in the root of your folder, which needs to contain a function with one parameter and returns an object. The parameter of the function will be filled with the current webpack configuration. This gives the user a chance to modify the default webpack configuration based on their needs.
Change Log
We're using the GitHub releases for changelog entries.
Getting started
Understanding modux architecture
-- Module -------- [ PushStore ] ------------------
| / \ |
| / \ |
| / \ |
| ------------------- ------------------- |
| | Component 1 | | Component 2 | |
| ------------------- ------------------- |
---------------------------------------------------
What is a Component ?
- Everything in the modux architecture revolves around components.
- A component is defined by extending the Component class.
- You can think of it as a controller for an html tag.
- The component is initialized as soon as the html tag for it is appended to the application.
What is a Module ?
- A module is what brings all components together. When we say application, we generally refer to the module and its components. But in reality an application can be created from multiple modules.
- You can think of a module as a super component.
- Modules are bound to an existing view by the user.
Once a module has been bound, any activity on its view's children will be monitored, and components will be created / modified / destroyed when changes occur in the DOM.
What is a PushStore ?
- See @crispcode/pushstore
- We use the pushstore as a way of comunicating information between components.
- An instance of PushStore is created when the module is created, and it is then shared with all its defined components.
- It works in a similar way to events, but with the added bonus of not being dependent on the race between emitters and listeners.
- The PushStore instance is also used as a way of storing information for all components of a module.
- Any data stored by any component or the module itself can be accessed by any other component or the module itself.
Building your first component
Lets create a component called header
. For this we will create a folder called header
in our application folder, under the components
folder. ( Note the folder structure of the application is not mandatory, we are just using this structure for visibility ). Here we will add 1 js file, 1 scss file and 1 html file.
/application
/components
/header
index.js
style.scss
template.html
The contents of index.js
will look something like this:
import { Component } from '@crispcode/modux'
// We are loading the html file as string ( handled by the default webpack configuration of modux )
import template from './template.html'
// We are loading the style sheet for this component ( handled by the default webpack configuration of modux )
import './style.scss'
export class ComponentHeader extends Component {
get template () {
// The template getter should return a string that can be converted to HTML or an HTMLElement
// NOTE: The string must have 1 wrapping element. See in template.html
return template
}
// The execute() method is the component constructor.
// This will be called when an instance of the ComponentHeader needs to be created.
execute () {
// this.element will always have the result of this.template converted to html, for easy access to the view
this.element.querySelector('h1').innerHTML = 'Hello World!'
}
}
The contents of template.html
can look something like this:
<!--
Remember that templates need to have a wrapping element.
In this case it's div[class="component-header"] tag
-->
<div class="component-header">
<h1> This text will be replaced when the component is created </h1>
<span> This is some static html stuff that we don't really focus on right now.</span>
</div>
The contents of style.scss
can look something like this:
.component-header {
background-color: #000000;
h1 {
color: #ffffff;
}
}
That's it! You have now created your fist component.
Building your first application
Now that we know how to create a component, lets see how it will all come together in a simple application.
First, lets create our folder structure:
/application
/public
- any file added here will be copied as is by webpack
/components
/header
index.js
style.scss
template.html
/footer
index.js
style.scss
template.html
/layout
index.js
template.html
app.js
app.scss
app.html
Once the folder structure is complete, lets install modux, by running this command in the folder root: npm install @crispcode/modux --save-dev
.
Now that we have modux in our project, we can focus on creating the base html page. Which is defined in app.html
. The entry files are all controlled by the default webpack configuration for modux, so we don't need to worry about that.
The contents of app.html
can look something like this:
<html>
<head>
<title> My application <title>
<!-- We add this as recommended by modux to polyfill unsupported js functionality in certain browsers -->
<script crossorigin="anonymous" src="https://polyfill.io/v3/polyfill.min.js?flags=gated&features=default%2CMutationObserver%2CString.prototype.padStart%2Cconsole.info"></script>
<!-- This will be the file that webpack will output containing all our style sheets -->
<link rel="stylesheet" href="app.min.css" />
</head>
<body>
<!-- We will use this tag to create our application -->
<div class="content"></div>
<!-- This will be the file that webpack will output containing all our code -->
<script src="app.min.js"></script>
</body>
</html>
Now that we have an html entry, lets create the 2 components:
First we create the header component, similar to how we did it above
The contents of
/application/components/header/template.html
<!-- Remember that templates need to have a wrapping element. In this case it's div[class="component-header"] tag --> <div class="component-header"> <h1> This text will be replaced when the component is created </h1> </div>
The contents of
/application/components/header/index.css
.component-header { background-color: #000000; h1 { color: #ffffff; } }
The contents of
/application/components/header/index.js
import { Component } from '@crispcode/modux' // We are loading the html file as string ( handled by the default webpack configuration of modux ) import template from './template.html' // We are loading the style sheet for this component ( handled by the default webpack configuration of modux ) import './style.scss' export class ComponentHeader extends Component { get template () { // The template getter should return a string that can be converted to HTML or an HTMLElement // NOTE: The string must have 1 wrapping element. See in template.html return template } // The execute() method is the component constructor. // This will be called when an instance of the ComponentHeader needs to be created. execute () { // this.element will always have the result of this.template converted to html, for easy access to the view // We will update the message in H1 based on our configuration this.element.querySelector('h1').innerHTML = this.store.get('message-header') } }
Then we create the footer component, similar to the header
The contents of
/application/components/footer/template.html
<!-- Remember that templates need to have a wrapping element. In this case it's div[class="component-footer"] tag --> <div class="component-footer"> <span class="copyright"> This text will be replaced when the component is created </span> </div>
The contents of
/application/components/footer/index.css
.component-footer { background-color: #000000; .copyright { color: #ffffff; } }
The contents of
/application/components/footer/index.js
import { Component } from '@crispcode/modux' // We are loading the html file as string ( handled by the default webpack configuration of modux ) import template from './template.html' // We are loading the style sheet for this component ( handled by the default webpack configuration of modux ) import './style.scss' export class ComponentFooter extends Component { get template () { return template } execute () { this.element.querySelector('.copyright').innerHTML = this.store.get('copyright') } }
We have now the two components, but let's group them under one parent component. This will be the layout component and we will skip the styles for it.
The contents of
/application/components/layout/template.html
<div class="component-layout"> <!-- This is how we define the position of a component in a page. As soon as an element with the attribute data-modux-component is found, the application will create an instance of that component ( if defined in its dependency ), and insert it's template as a child of that parent element. --> <section data-modux-component="header"></section> <section data-modux-component="footer"></section> </div>
The contents of
/application/components/layout/index.js
`
jsimport { Component } from '@crispcode/modux'
import template from './template.html'
// We don't really want this component to do anything aside from holding the other two components export class ComponentLayout extends Component {
get template () { return template }
}
Now lets bind it all together in a module. Since ```app.js``` is the main entry point for wepback config, here is were we create our module.
The contents of ``` /application/app.js
/* globals window, document */
'use strict'
import { Module, logger } from '@crispcode/modux'
import { ComponentLayout } from './components/layout'
import { ComponentHeader } from './components/header'
import { ComponentFooter } from './components/footer'
let initialize = () => {
// This is information that will be used in the header and footer components
store.set( 'message-header', 'Hello World! I am data stored in the application config' )
store.set( 'copyright', '© CrispCode' )
// Just so that we have some details about what is happening we are going to set logger to debug mode.
logger.enabled( true )
logger.info( 'Application start' )
// Create application
let app = new Module( 'app' )
app
.addDependency( 'layout', ComponentLayout )
.addDependency( 'header', ComponentHeader )
.addDependency( 'footer', ComponentFooter )
// Start application bound to the content element
app.bootstrap( document.body.querySelector( '.content' ), 'layout' )
}
// We will start the application as soon as the page is loaded.
window.addEventListener( 'load', () => {
initialize()
} )
GREAT! We are almost finished. All we need now is to add a build script to package json. This is as easy as adding the following properties to package.json
{
"name": "myapp",
"version": "1.0.0",
"devDependencies": {
...
},
.
.
.
"scripts": {
"test": "modux start",
"build": "modux build",
}
}
Let's see what we've created. Run npm test
.
There are a lot of features available in modux, such as a lot of utilities, as well as a router class for managing url changes. You can always clone the modux repository and run npm run docs
to get the full documentation for it.
If you have any issues or questions please feel free to refer to the issues page in github. Bye!
Basic usage
/components/layout/template.html
<section class="layout"></section>
/components/layout/index.js
'use strict'
import { Component } from '@crispcode/modux'
import template from './template.html'
import './index.scss'
export class Layout extends Component {
get template () {
return template
}
execute() {
this.element.innerHTML = "Layout loaded"
}
}
/app.js
/* globals window, document */
'use strict'
import { Module, logger } from '@crispcode/modux'
import { Layout } from './components/layout'
let initialize = () => {
// Configure logger
logger.enabled( true )
logger.info( 'Application start' )
// Create application
let app = new Module( 'app' )
app
.addDependency( 'layout', Layout )
// Start application
app.bootstrap( document.querySelector( 'body' ), 'layout' )
}
window.addEventListener( 'load', () => {
initialize()
} )
/app.html
<!DOCTYPE html>
<head>
<title> App </title>
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, user-scalable=no">
<script crossorigin="anonymous" src="https://polyfill.io/v3/polyfill.min.js?flags=gated&features=default%2CMutationObserver%2CString.prototype.padStart%2Cconsole.info"></script>
<base href="/" />
<link rel="stylesheet" href="app.min.css" />
</head>
<body>
<script src="app.min.js"></script>
</body>
</html>
Basic usage router
/components/layout/template.html
<section class="layout">
<span class="url"></span>
<a href="/link1" data-modux-link="">LINK 1</a>
<a href="/link2" data-modux-link="">LINK 2</a>
<a href="/link3" data-modux-link="">LINK 3</a>
</section>
/components/layout/index.js
'use strict'
import { Component } from '@crispcode/modux'
import template from './template.html'
export class Layout extends Component {
get template () {
return template
}
onStateChange ( url ) {
this.element.querySelector('.url').innerHTML = url.toString()
}
}
/app.js
/* globals window, document */
'use strict'
import { Module } from '@crispcode/modux'
import { Layout } from './components/layout'
let initialize = () => {
// Create application
let app = new Module( 'app' )
app
.addDependency( 'layout', Layout )
// Start application
app.bootstrap( document.querySelector( 'body' ), 'layout' )
}
window.addEventListener( 'load', () => {
initialize()
} )