Install
NPM
npm install @webformula/core
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cache-Control" content="no-store" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<!-- load js and css -->
<link href="app.css" rel="stylesheet">
<script src="app.js" type="module"></script>
</head>
<body>
<!-- page template render into this element -->
<page-content></page-content>
</body>
</html>
Routing
@Webformula/core uses directory based routing. All routes go in a 'routes' folder.
app/
└── routes/
├── index/
│ └── index.js # /
├── 404/
│ └── index.js # /404 (or any url that is not found)
├── one/
│ └── index.js # one/
├── two[id?]/
│ └── index.js # two/:id?
├── three/
│ └── [id]/
│ └── index.js # three/:id
└── four/
└── [...all]/
└── index.js # four/* (four/a/b/)
app/routes/index/index.js → /
app/routes/one/index.js → one
app/routes/two[id?]/index.js → two/:id?
app/routes/three/[id]/index.js → three/:id
app/routes/four/[...rest]/index.js →
four/*
Directory route details
routes/index/index.js Root page (/)
routes/404/index.js Not found page. Auto redirect on non
matching routes
index.js Route component file
[id] Directory that represents a url parameter
[id?] Directory that represents an options url parameter
name[id?] Inline url parameter to avoid sub folder
[...rest] Directory that represents catch-all route
[...rest?] Directory that represents optional catch-all
route
Config app
Change settings for binding and routing
<!-- Add script tag with configuration to index.html -->
<head>
...
<script>
/* If false then variable binding will be disabled
*
* Default: true
*/
window.webformulaCoreBinding = true;
/* If false then navigation will retrieve pages from the server
* Application code and styles will be cached, only the page html will be retrieved
*
* Default: true
*/
window.webformulaCoreSpa = true;
</script>
...
</head>
<body>...</body>
Main application file
app.js
Required: Automatically uses app.js as entry file for bundling
/* Main app file
* you can import any code in here
*/
import someModule from './someModule.js';
// routes are automatically loaded based on directory routing
Prevent navigation allows you to lock down the app for uses like authentication
import { routes, preventNavigation } from '@webformula/core';
import home from './routes/home/index.js';
import login from './routes/login/index.js';
routes([
{ path: '/', component: home },
{ path: '/login', component: login }
]);
// if not authenticated redirect to login and prevent navigation
if (!document.cookie.includes('authenticated=true')) {
if (location.pathname !== '/login') location.href = '/login';
preventNavigation(true);
// preventNavigation(false);
}
Main application Styles
app.css
Optional: Will bundle and minify into a single file
@import url('./other.css');
body {
background-color: white;
}
Page
page.js and page.html
import { Component } from '@webformula/core';
import html from './page.html'; // automatically bundles
export default class extends Component {
static title = 'Page'; // html page title
static html = html; // hook up imported html. Supports template literals (${this.someVar})
someVar = 'Some var';
clickIt_bound = this.clickIt.bind(this);
userInput;
constructor() {
super();
}
connectedCallback() {
// called on element hookup to dome. May not be rendered yet
this.userInput = 'some user input';
console.log(this.urlParameters); // { id: 'value' }
console.log(this.searchParameters); // { id: 'value' }
}
disconnectedCallback() {
// called on element removal
}
// not called on initial render
beforeRender() {
// Do work before render
}
afterEnder() {
// Do work after render
this.querySelector('#event-listener-button').addEventListener('click', this.clickIt_bound);
}
// look below to how it is invoked on a button
clickIt() {
console.log('clicked it!');
}
// look below to how it is invoked on a button
changeValueAndRender() {
this.someVar = 'Re-rendered';
this.render(); // initial render is automatic
}
/**
* If not importing html you can use this template method.
* Imported html also supports template literals (undefined)
*/
template() {
return /*html*/`
<div>Page Content</div>
<div>${this.someVar}</div>
<!-- escape html input -->
<div>${this.escape(this.userInput)}</div>
<!-- "page" will reference the current page class -->
<button onclick="page.clickIt()">Click Method</button>
<button id="event-listener-button">Event listener</button>
<button onclick="page.changeValueAndRender()">Change value and render</button>
`;
}
}
Build app
build.js
No need for webpack or other bundlers. ESbuild is packed in and pre configured.
Build config
import build from '@webformula/core/build';
/**
* runs dev server by default on port 3000 with livereload
* basedir defaults to 'app/'
*/
build({ basedir: 'app/' });
Run commands
# Development run
node build.js
# Development run with watch to enable livereload
node --watch-path=./app build.js
# Production run. minifies, gzips, and writes files
NODE_ENV=production node build.js
Serve app
server.js
Handle routing and file serving with middleware. GZIP compression is automatically
handled.
Serve config
Native server
import { createServer } from 'node:http';
import { middlewareNode } from '@webformula/core/middleware';
const middleware = middlewareNode({
basedir: 'docs/',
outdir: 'dist/',
copyFiles: [
{ from: 'docs/favicon.ico', to: 'dist/' }
]
});
createServer(async (req, res) => {
const handled = await middleware(req, res);
if (handled === true) return;
// Do other stuff
}).listen(3000);
Express server
import express from 'express';
import { middlewareExpress } from '@webformula/core/middleware';
const app = express();
app.use(middlewareExpress({
basedir: 'docs/',
outdir: 'dist/',
copyFiles: [
{ from: 'docs/favicon.ico', to: 'dist/' }
]
}));
app.use(express.static('./docs'));
app.listen(3000);
Livereload
# Simply use node --watch to enable livereload
node --watch-path=./src --watch-path=./docs server.js
Devtools
A small set of helper functions
// Look at variable binding info for current page
window.getBoundVariables();
// View current page template string
window.getPageTemplate();
// Get all pages route details
window.getRoutes();