@webqit/webflo 0.11.5 → 0.11.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +374 -125
- package/package.json +1 -1
- package/src/runtime-pi/client/Runtime.js +3 -3
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ Ok, we've put all of that up for a straight read!
|
|
|
22
22
|
+ [Webflo Applications](#webflo-applications)
|
|
23
23
|
+ [Workflow API](#workflow-api)
|
|
24
24
|
+ [Webflo Config](#webflo-config)
|
|
25
|
+
+ [Technology Stack](#technology-stack)
|
|
25
26
|
+ [Getting Started](#getting-started)
|
|
26
27
|
+ [Getting Involved](#getting-involved)
|
|
27
28
|
|
|
@@ -135,13 +136,13 @@ For when your application involves routing:
|
|
|
135
136
|
+ [WHATWG URL](https://url.spec.whatwg.org/) and [WHATWG URLPattern](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) are used for all things *URL* and *URL pattern matching*, respectively - across client, server, and Service Worker environments. ([Details ahead](#))
|
|
136
137
|
|
|
137
138
|
For when your application involves pages and a UI:
|
|
138
|
-
+ [The HTML Standard](https://html.spec.whatwg.org/) is held for all things *markup* - across client, server, and Service Worker environments! Webflo is all about using conventional `.html`-based pages and templates, valid HTML syntax, etc. You are able to get away with a "zero-JavaScript" proposition or
|
|
139
|
+
+ [The HTML Standard](https://html.spec.whatwg.org/) is held for all things *markup* - across client, server, and Service Worker environments! Webflo is all about using conventional `.html`-based pages and templates, valid HTML syntax, etc. You are able to get away with a "zero-JavaScript" proposition, or a *Progressive Enhancement* proposition that makes do with "just-enough JavaScript"!
|
|
139
140
|
|
|
140
|
-
> Your markup is also easily extendable with [OOHTML](
|
|
141
|
+
> Your markup is also easily extendable with [OOHTML](#oohtml) - a set of new features for HTML that makes it fun to hand-author your UI! Within OOHTML are [HTML Modules](https://github.com/webqit/oohtml#html-modules) and [HTML Imports](https://github.com/webqit/oohtml#html-imports), [Reactive Scripts](https://github.com/webqit/oohtml#subscript) and more!
|
|
141
142
|
|
|
142
|
-
+ [WHATWG DOM](https://dom.spec.whatwg.org/) is universally available - not only on the client-side, but also on the server-side via [OOHTML-SSR](
|
|
143
|
+
+ [WHATWG DOM](https://dom.spec.whatwg.org/) is universally available - not only on the client-side, but also on the server-side via [OOHTML-SSR](#oohtml-ssr) - for all things *dynamic pages*: rendering, manipulation, interactivity, etc.
|
|
143
144
|
|
|
144
|
-
> Your DOM is also easily enrichable with [Custom Elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements), plus [Subscript Elements](https://github.com/webqit/oohtml#subscript) and [The State API
|
|
145
|
+
> Your DOM is also easily enrichable with [Custom Elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements), plus [Subscript Elements](https://github.com/webqit/oohtml#subscript) and [The State API](https://github.com/webqit/oohtml#state-api) from OOHTML.
|
|
145
146
|
|
|
146
147
|
For when your application needs to give an app-like experience:
|
|
147
148
|
+ [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API), extended with full support for routing, come into play for offline and [Progressive Web Apps (PWA)](https://web.dev/progressive-web-apps/) capabilities.
|
|
@@ -153,7 +154,7 @@ This and more - ahead! For building web-native apps!
|
|
|
153
154
|
|
|
154
155
|
## Installation
|
|
155
156
|
|
|
156
|
-
Every Webflo project starts on an empty directory that you can create on your machine. The command below
|
|
157
|
+
Every Webflo project starts on an empty directory that you can create on your machine. The command below makes a new directory `my-app` from the terminal and navigates into it.
|
|
157
158
|
|
|
158
159
|
```shell
|
|
159
160
|
mkdir my-app
|
|
@@ -198,7 +199,7 @@ All is now set! The commands `npm start` and `npm run generate` will be coming i
|
|
|
198
199
|
|
|
199
200
|
### "Hello World!"
|
|
200
201
|
|
|
201
|
-
To be sure that Webflo is listening, run `npx webflo help` on the terminal. An overview of available commands
|
|
202
|
+
To be sure that Webflo is listening, run `npx webflo help` on the terminal. An overview of available commands should be shown.
|
|
202
203
|
|
|
203
204
|
If you can't wait to say *Hello World!* 😅, you can have an HTML page say that right now!
|
|
204
205
|
+ Create an `index.html` file in a new subdirectory `public`.
|
|
@@ -238,7 +239,7 @@ If you can't wait to say *Hello World!* 😅, you can have an HTML page say that
|
|
|
238
239
|
|
|
239
240
|
### Handler Functions and Layout
|
|
240
241
|
|
|
241
|
-
Whether building a *server-based*, *browser-based*, or *universal* application, Webflo gives
|
|
242
|
+
Whether building a *server-based*, *browser-based*, or *universal* application, Webflo gives you one consistent way to handle routing and navigation: using *handler functions*!
|
|
242
243
|
|
|
243
244
|
```js
|
|
244
245
|
/**
|
|
@@ -249,7 +250,10 @@ export default function(event, context, next) {
|
|
|
249
250
|
}
|
|
250
251
|
```
|
|
251
252
|
|
|
252
|
-
|
|
253
|
+
> **Note**
|
|
254
|
+
> <br>Other [*method*](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods)-specific function names may be used: `get`, `post`, `put`, `patch`, `del` (for *delete*), `options`, `head`, etc.
|
|
255
|
+
|
|
256
|
+
Each function receives an `event` object representing details - e.g. `event.request`, `event.url`, `event.session` - about the current request. ([Details ahead](#workflow-api).)
|
|
253
257
|
|
|
254
258
|
For *server-based* applications (e.g. traditional web apps and API backends), server-side handlers go into a directory named `server`.
|
|
255
259
|
|
|
@@ -372,7 +376,7 @@ export default function(event, context, next) {
|
|
|
372
376
|
|
|
373
377
|
This step-based workflow helps to decomplicate routing and gets us scaling horizontally as our application grows larger.
|
|
374
378
|
|
|
375
|
-
Workflows may be designed with *wildcard* steps using a hyphen `-` as step name.
|
|
379
|
+
Workflows may be designed with *wildcard* steps using a hyphen `-` as step name. At runtime, a wildcard step matches any URL segment at its level in the layout! A `this.stepname` property could be used to see which URL segment has been matched.
|
|
376
380
|
|
|
377
381
|
```js
|
|
378
382
|
/**
|
|
@@ -424,7 +428,7 @@ Webflo takes a *default action* when `next()` is called at the *edge* of the wor
|
|
|
424
428
|
|
|
425
429
|
For workflows in **the `/server` directory**, the *default action* of `next()`ing at the edge is to go match and return a static file in the `public` directory.
|
|
426
430
|
|
|
427
|
-
So, above, should our handler receive static file requests like `http://localhost:3000/logo.png`, the
|
|
431
|
+
So, above, should our handler receive static file requests like `http://localhost:3000/logo.png`, the statement `return next()` would get Webflo to match and return the logo at `public/logo.png`, if any; a `404` response otherwise.
|
|
428
432
|
|
|
429
433
|
```shell
|
|
430
434
|
my-app
|
|
@@ -433,7 +437,7 @@ my-app
|
|
|
433
437
|
```
|
|
434
438
|
|
|
435
439
|
> **Note**
|
|
436
|
-
> <br>The root handler effectively becomes the single point of entry to the application - being that it sees even static
|
|
440
|
+
> <br>The root handler effectively becomes the single point of entry to the application - being that it sees even requests for static files!
|
|
437
441
|
|
|
438
442
|
Now, for workflows in **the `/worker` directory**, the *default action* of `next()`ing at the edge is to send the request through the network to the server. (But Webflo will know to attempt resolving the request from the application's caching system built into the Service Worker.)
|
|
439
443
|
|
|
@@ -562,7 +566,7 @@ But, we can also access the route in a way that gets the data rendered into the
|
|
|
562
566
|
Now, for Single Page Applications, subsequent navigations, after the initial page load, just ask for the data on destination URLs and perform [Client-Side Rendering](#client-and-server-side-rendering) on the same running document. Navigation is sleek and instant!
|
|
563
567
|
|
|
564
568
|
> **Note**
|
|
565
|
-
> <br>Unless disabled
|
|
569
|
+
> <br>Unless disabled, [SPA Routing](#spa_routing) is automatically built into your app's JS bundle from the `npm run generate` command. So, it just works!
|
|
566
570
|
|
|
567
571
|
With no extra work, your application can function as either a *Multi Page App (MPA)* or a *Single Page App (SPA)*!
|
|
568
572
|
|
|
@@ -596,7 +600,7 @@ my-app
|
|
|
596
600
|
|
|
597
601
|
This, in both cases, is templating - the ability to define HTML *partials* once, and have them reused multiple times. Webflo just concerns itself with templating, and the choice of a Multi Page Application or Single Page Application becomes yours! And heck, you can even have the best of both worlds in the same application - with an architecture we'll call [Multi SPA](#in-a-multi-spa-layout)! It's all a *layout* thing!
|
|
598
602
|
|
|
599
|
-
Now, with pages in Webflo being [DOM-based](#overview) (both client-side and [server-side](
|
|
603
|
+
Now, with pages in Webflo being [DOM-based](#overview) (both client-side and [server-side](#oohtml-ssr)), documents can be manipulated directly with DOM APIs, e.g. to replace or insert nodes, attributes, etc. But even better, templating in Webflo is based on the [HTML Modules](https://github.com/webqit/oohtml#html-modules) and [HTML Imports](https://github.com/webqit/oohtml#html-imports) features in [OOHTML](#oohtml) - unless disabled in config. These features provide a powerful declarative templating system on top of the standard [HTML `<template>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template) element - with a *module*, *export* and *import* paradigm.
|
|
600
604
|
|
|
601
605
|
Here, you are able to define reusable contents in a `<template>` element...
|
|
602
606
|
|
|
@@ -703,7 +707,7 @@ public/products
|
|
|
703
707
|
|
|
704
708
|
#### In a Single Page Layout
|
|
705
709
|
|
|
706
|
-
In a Single Page layout (as [
|
|
710
|
+
In a Single Page layout (as seen [earlier](#layout-and-templating-overview)), page-specific contents - e.g. main sections - are typically bundled together into one `bundle.html` file that can be embedded on the document root. Nested routes end up as nested `<template>` elements that form the equivalent of thw application's URL structure.
|
|
707
711
|
|
|
708
712
|
```html
|
|
709
713
|
<!--
|
|
@@ -817,7 +821,7 @@ The Webflo `generate` command automatically figures out a given architecture and
|
|
|
817
821
|
|
|
818
822
|
#### Bundling
|
|
819
823
|
|
|
820
|
-
Template `.html` files are bundled from the filesystem into a single file using the [OOHTML CLI](
|
|
824
|
+
Template `.html` files are bundled from the filesystem into a single file using the [OOHTML CLI](#oohtml-cli) utility. On installing this utility, you may want to add the following to your npm scripts in `package.json`.
|
|
821
825
|
|
|
822
826
|
```json
|
|
823
827
|
"scripts": {
|
|
@@ -830,13 +834,13 @@ The `--recursive` flag gets the bundler to recursively bundle *subroots* in a [M
|
|
|
830
834
|
The `--auto-embed` flag gets the bundler to automatically embed the generated `bundle.html` file on the matched `index.html` document. A value of `routes` for the flag ends up as the name of the *embed* template: `<template name="routes" src="/bundle.html"></template>`.
|
|
831
835
|
|
|
832
836
|
> **Note**
|
|
833
|
-
> <br>If your HTML files are actually based off the `public` directory, you'll need to tell the above command to run in the `public` directory, either by [configuring the bundler](https://github.com/webqit/oohtml-cli#other-options), or by rewriting the command with a prefix: `cd public && oohtml bundle --recursive --auto-embed=routes`.
|
|
837
|
+
> <br>If your HTML files are actually based off the `public` directory, you'll need to tell the above command to run in the `public` directory, either by [configuring the bundler](https://github.com/webqit/oohtml-cli#other-options), or by rewriting the command with a prefix: `cd public && oohtml bundle --recursive --auto-embed=routes`.
|
|
834
838
|
|
|
835
839
|
### Client and Server-Side Rendering
|
|
836
840
|
|
|
837
|
-
With pages in Webflo being [DOM-based](#overview) (both client-side and [server-side](
|
|
841
|
+
With pages in Webflo being [DOM-based](#overview) (both client-side and [server-side](#oohtml-ssr)), we are able to access and manipulate documents and elements using familiar DOM APIs - e.g. to replace or insert contents, attributes, etc. Rendering in Webflo is based on this concept!
|
|
838
842
|
|
|
839
|
-
Here, Webflo simply makes sure that the data obtained from each route is available as part of the `document` object, such that it is accessible to our rendering logic as a `data` property on the `document.state` object - `document.state.data
|
|
843
|
+
Here, Webflo simply makes sure that the data obtained from each route is available as part of the `document` object, such that it is accessible to our rendering logic as a `data` property on the [`document.state`](#the-idea-of-state) object - [`document.state.data`](#the-documentstatedata-object).
|
|
840
844
|
|
|
841
845
|
So, we could embed a script on our page and render this data on the relevant parts of the document.
|
|
842
846
|
|
|
@@ -880,7 +884,7 @@ public
|
|
|
880
884
|
|
|
881
885
|
From here, even the most-rudimentary form of rendering (using vanilla HTML and native DOM methods) becomes possible, and this is a good thing: you get away with less tooling until you absolutely need to add up on tooling!
|
|
882
886
|
|
|
883
|
-
However, since the `document` objects in Webflo natively support [OOHTML](
|
|
887
|
+
However, since the `document` objects in Webflo natively support [OOHTML](#oohtml) - unless disabled in config, we are able to write reactive UI logic! Here, OOHTML makes it possible to embed reactive `<script>` elements (called [Subscript](https://github.com/webqit/oohtml#subscript)) right within HTML elements - where each expression automatically self-updates whenever references to data, or its properties, get an update!
|
|
884
888
|
|
|
885
889
|
```html
|
|
886
890
|
<!--
|
|
@@ -929,7 +933,9 @@ Going forward, we can get to write more succinct code! Using the [Namespaced HTM
|
|
|
929
933
|
document.title = title;
|
|
930
934
|
let { headline1, headline2 } = this.namespace;
|
|
931
935
|
$(headline1).html(title);
|
|
932
|
-
|
|
936
|
+
if (headline2) {
|
|
937
|
+
$(headline2).html(title);
|
|
938
|
+
}
|
|
933
939
|
</script>
|
|
934
940
|
</body>
|
|
935
941
|
</html>
|
|
@@ -1021,16 +1027,122 @@ export async function render(event, data, next) {
|
|
|
1021
1027
|
|
|
1022
1028
|
Custom render functions must return a value, and `window` objects are accepted. (Actually, any object that has a `toString()` method can be returned.)
|
|
1023
1029
|
|
|
1030
|
+
#### The Idea of State
|
|
1031
|
+
|
|
1032
|
+
There often needs to be a central point in an application where things are stored and managed. You could think of it is having a global object initialized `window.store = {}` on which different parts of an application can store and retrieve values. This is the basic idea of state. But it also doesn't go without the idea of *observability* - something that lets the different parts of the application observe and respond to changes made on this object!
|
|
1033
|
+
|
|
1034
|
+
*State* and *Observability* in Webflo applications come down to this basic form: there is an object...
|
|
1035
|
+
|
|
1036
|
+
```js
|
|
1037
|
+
state = {}
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
...and there is a way to observe property changes on it...
|
|
1041
|
+
|
|
1042
|
+
```js
|
|
1043
|
+
Observer.observe(state, changes => {
|
|
1044
|
+
changes.forEach(change => {
|
|
1045
|
+
console.log(change.name, change.value);
|
|
1046
|
+
});
|
|
1047
|
+
});
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
```js
|
|
1051
|
+
Observer.observe(state, propertyName, change => {
|
|
1052
|
+
console.log(change.name, change.value);
|
|
1053
|
+
});
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
...plus, all references to the object and its properties from within embedded Subscript code are reactive.
|
|
1057
|
+
|
|
1058
|
+
```html
|
|
1059
|
+
<script type="subscript">
|
|
1060
|
+
// Always log the value of this property in realtime
|
|
1061
|
+
console.log(state.propertyName);
|
|
1062
|
+
</script>
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
This way, all the moving parts of your application remain coordinated, and can easily be rendered to reflect them on the UI!
|
|
1066
|
+
|
|
1067
|
+
For all things application state, Webflo leverages the [State API](https://github.com/webqit/oohtml#state-api) that's natively available in OOHTML-based documents - both client-side and server-side. This API exposes an application-wide `document.state` object and a per-element `element.state` object. And these are *live* read/write objects that can be observed for property changes using the [Observer API](#the-observer-api). It comes off as the simplest approach to state and reactivity!
|
|
1068
|
+
|
|
1069
|
+
> **Note**
|
|
1070
|
+
> <br>The State API is not available when the [OOHTML support level](#oohtml) in config is switched away from `full` and `scripting`.
|
|
1071
|
+
|
|
1072
|
+
#### The `document.state.data` Object
|
|
1073
|
+
|
|
1074
|
+
This property reperesents the application data at any point in time - obtained from route handers on each navigation. Webflo simply updates this property and lets the page's [rendering logic](#client-and-server-side-rendering), or other parts of the application, take over.
|
|
1075
|
+
|
|
1076
|
+
```js
|
|
1077
|
+
Observer.observe(document.state, 'data', e => {
|
|
1078
|
+
console.log('Current page data is: ', e.value);
|
|
1079
|
+
});
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
```html
|
|
1083
|
+
<script type="subscript">
|
|
1084
|
+
let { title } = document.state.data;
|
|
1085
|
+
document.title = title;
|
|
1086
|
+
</script>
|
|
1087
|
+
```
|
|
1088
|
+
|
|
1089
|
+
#### The `document.state.url` Object
|
|
1090
|
+
|
|
1091
|
+
This is a *live* object that reperesents the properties of the application URL at any point in time. The object exposes the same URL properties as with the [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) API, but as *live* properties that can be observed as navigation happens, and modified to initiate navigation - all using the [Observer API](#the-observer-api).
|
|
1092
|
+
|
|
1093
|
+
```js
|
|
1094
|
+
console.log(document.state.url) // { hash, host, hostname, href, origin, password, pathname, port, protocol, search, searchParams, username }
|
|
1095
|
+
```
|
|
1096
|
+
|
|
1097
|
+
```js
|
|
1098
|
+
Observer.observe(document.state.url, 'hash', e => {
|
|
1099
|
+
console.log(document.state.url.hash === e.value); // true
|
|
1100
|
+
});
|
|
1101
|
+
```
|
|
1102
|
+
|
|
1103
|
+
```js
|
|
1104
|
+
// Navigates to "/login#form" as if a link was clicked
|
|
1105
|
+
document.addEventListener('synthetic-navigation', e => {
|
|
1106
|
+
Observer.set(document.state.url, 'href', '/login#form');
|
|
1107
|
+
});
|
|
1108
|
+
|
|
1109
|
+
// Or...
|
|
1110
|
+
document.addEventListener('synthetic-navigation', e => {
|
|
1111
|
+
Observer.set(document.state.url, { pathname: '/login', hash: '#form' });
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
console.log(document.state.url.hash); // #form
|
|
1115
|
+
```
|
|
1116
|
+
|
|
1117
|
+
There is also the *convenience* `query` property that offers the URL parameters as a *live* object.
|
|
1118
|
+
|
|
1119
|
+
```js
|
|
1120
|
+
// For URL: http://localhost:3000/login?as=student
|
|
1121
|
+
console.log(document.state.url.query.as) // student
|
|
1122
|
+
|
|
1123
|
+
// Re-rewrite the URL and initiate navigation by simply modifying a query parameter
|
|
1124
|
+
document.addEventListener('synthetic-navigation', e => {
|
|
1125
|
+
Observer.set(document.state.url.query, 'as', 'business');
|
|
1126
|
+
});
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
```html
|
|
1130
|
+
<script type="subscript">
|
|
1131
|
+
let { query: { as: role } } = document.state.url;
|
|
1132
|
+
document.title = 'Login as ' + role;
|
|
1133
|
+
</script>
|
|
1134
|
+
```
|
|
1135
|
+
|
|
1024
1136
|
### Requests and Responses
|
|
1025
1137
|
|
|
1026
|
-
On each request, the event object passed to route handlers exposes the incoming request as `event.request`. This is an instance of `event.Request` - an extension of the [WHATWG Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) class. The event object also exposes `event.Response` - an extension of the [WHATWG Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) class, for returning instance-based responses.
|
|
1138
|
+
On each request, the event object passed to route handlers exposes the incoming request as `event.request`. This is an instance of `event.Request` - an extension of the [WHATWG Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) class. The event object also exposes `event.Response` - an extension of the [WHATWG Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) class, for returning instance-based responses. You enjoy routing that is based on standard interfaces!
|
|
1027
1139
|
|
|
1028
|
-
|
|
1140
|
+
Routes in Webflo can be designed for different types of request/response scenarios. Here are some important ones:
|
|
1029
1141
|
|
|
1030
1142
|
#### Scenario 1: Static File Requests and Responses
|
|
1031
1143
|
|
|
1032
1144
|
Static file requests like `http://localhost:3000/logo.png` are expected to get a file response. These requests are automatically handled by Webflo when `next()`ed forward by route handlers, or where there are no route handlers.
|
|
1033
|
-
+ On the server, Webflo serves files from the `public` directory. File
|
|
1145
|
+
+ On the server, Webflo serves files from the `public` directory. File contents along with the appropriate headers like [`Content-Type`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type), [`Content-Length`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length), etc. are returned as an instance of `event.Response`. Where a request has an [`Accept-Encoding`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header set (e.g. `gzip`, `br`) and there exists a matching *compressed version* of the said file on the file system (e.g. `./public/logo.png.gz`, `./public/logo.png.br`), the compressed version is served and the appropriate [`Content-Encoding`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding) response header is set.
|
|
1034
1146
|
+ On the client, Webflo serves static files from the network, or from the application cache, where available.
|
|
1035
1147
|
|
|
1036
1148
|
#### Scenario 2: API Requests and Responses
|
|
@@ -1120,111 +1232,121 @@ Webflo also offers a *convenience* method.
|
|
|
1120
1232
|
|
|
1121
1233
|
```js
|
|
1122
1234
|
console.log(event.request.headers.cookies); // { 'Cookie-1': 'cookie-val', 'Cookie-2': 'cookie2-val' };
|
|
1123
|
-
|
|
1235
|
+
```
|
|
1124
1236
|
|
|
1125
1237
|
### Webflo Applications
|
|
1126
1238
|
|
|
1127
|
-
In just a few concepts, Webflo comes ready for any type of application!
|
|
1239
|
+
In just a few concepts, Webflo comes ready for any type of application!
|
|
1128
1240
|
|
|
1129
|
-
+ [Application State](#application-state)
|
|
1130
1241
|
+ [Client-Side Applications](#client-side-applications)
|
|
1242
|
+
+ [Progressive Web Apps](#progressive-web-apps)
|
|
1131
1243
|
+ [API Backends](#api-backends)
|
|
1132
1244
|
+ [Static Sites](#static-sites)
|
|
1133
1245
|
|
|
1134
|
-
#### Application State
|
|
1135
|
-
|
|
1136
|
-
For all things application state, Webflo leverages the [State API](https://github.com/webqit/oohtml#state-api) that's natively available in OOHTML-based documents - both client-side and server-side. This API exposes an application-wide `document.state` object and a per-element `element.state` object. And these are *live* read/write objects that can be observed for property changes using the [Observer API](https://github.com/webqit/observer). It comes off as the simplest approach to state and reactivity!
|
|
1137
|
-
|
|
1138
|
-
> **Note**
|
|
1139
|
-
> <br>The State API is not available when the OOHTML support level in config is switched away from `full` and `scripting`.
|
|
1140
|
-
|
|
1141
|
-
##### The `document.state.data` Object
|
|
1142
|
-
|
|
1143
|
-
This property represents the data obtained from route handers on each navigation. Webflo simply exposes this data and lets the page's [rendering logic](#client-and-server-side-rendering), or other parts of the application, take over.
|
|
1144
|
-
|
|
1145
|
-
```js
|
|
1146
|
-
Observer.observe(document.state, 'data', e => {
|
|
1147
|
-
console.log('Current page data is: ', e.value);
|
|
1148
|
-
});
|
|
1149
|
-
```
|
|
1150
|
-
|
|
1151
|
-
##### The `document.state.url` Object
|
|
1152
|
-
|
|
1153
|
-
This is a *live* object that reperesents the properties of the application URL at any point in time. The object exposes the same URL properties as with the [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) API, but as *live* properties that can be observed as navigation happens, and modified to initiate navigation - all using the [Observer API](https://github.com/webqit/observer).
|
|
1154
|
-
|
|
1155
|
-
```js
|
|
1156
|
-
console.log(document.state.url) // { hash, host, hostname, href, origin, password, pathname, port, protocol, search, searchParams, username }
|
|
1157
|
-
```
|
|
1158
|
-
|
|
1159
|
-
```js
|
|
1160
|
-
Observer.observe(document.state.url, 'hash', e => {
|
|
1161
|
-
console.log(document.state.url.hash === e.value); // true
|
|
1162
|
-
});
|
|
1163
|
-
```
|
|
1164
|
-
|
|
1165
|
-
```js
|
|
1166
|
-
// Navigates to "/login#form" as if a link was clicked
|
|
1167
|
-
document.addEventListener('synthetic-navigation', e => {
|
|
1168
|
-
Observer.set(document.state.url, 'href', '/login#form');
|
|
1169
|
-
});
|
|
1170
|
-
|
|
1171
|
-
// Or...
|
|
1172
|
-
document.addEventListener('synthetic-navigation', e => {
|
|
1173
|
-
Observer.set(document.state.url, { pathname: '/login', hash: '#form' });
|
|
1174
|
-
});
|
|
1175
|
-
|
|
1176
|
-
console.log(document.state.url.hash); // #form
|
|
1177
|
-
```
|
|
1178
|
-
|
|
1179
|
-
There is also the *convenience* `query` property that offers the URL parameters as a *live* object.
|
|
1180
|
-
|
|
1181
|
-
```js
|
|
1182
|
-
// For URL: http://localhost:3000/login?as=student
|
|
1183
|
-
console.log(document.state.url.query.as) // student
|
|
1184
|
-
|
|
1185
|
-
// Re-rewrite the URL and initiate navigation by simply modifying a query parameter
|
|
1186
|
-
document.addEventListener('synthetic-navigation', e => {
|
|
1187
|
-
Observer.set(document.state.url.query, 'as', 'business');
|
|
1188
|
-
});
|
|
1189
|
-
```
|
|
1190
|
-
|
|
1191
1246
|
#### Client-Side Applications
|
|
1192
1247
|
|
|
1193
|
-
Web pages that embed the Webflo client JS bundle deliver a great user experience.
|
|
1194
|
-
+ **First-paint-ready.** On the first page request, you get a [server-rendered](#client-and-server-side-rendering) HTML page that's optimized for the first paint of your application.
|
|
1195
|
-
+ **Fluid and app-like.** On being loaded, the state of the application is restored through hydration, and [subsequent navigations](#spa-navigation) are sleek and instant, while performing [Client-Side Rendering](#client-and-server-side-rendering).
|
|
1248
|
+
Web pages that embed the Webflo client JS bundle deliver a great user experience. It's simple: the `npm run generate` command does both the building and embedding of the script, or scripts, for the document root, or document roots (in a [Multi Page](#in-a-multi-page-layout) / [Multi SPA](#in-a-multi-spa-layout) layout)!
|
|
1196
1249
|
|
|
1197
|
-
|
|
1250
|
+
On being loaded, the state of the application is initialized, or is restored through hydration - where [Server-Side Rendering](#client-and-server-side-rendering) was involved to optimize for first paint, and an app-like experience kicks in! For [Single-Page Applications](#in-a-single-page-layout), [Client-Side Rendering](#client-and-server-side-rendering) is performed on each navigation.
|
|
1198
1251
|
|
|
1199
1252
|
##### SPA Navigation
|
|
1200
1253
|
|
|
1201
|
-
Unless disabled in
|
|
1254
|
+
Unless disabled in config, it is factored-in at build time for the application client JS to be able to automatially figure out when to intercept a navigation event and prevent a full page reload, and when not to. It follows the following rules:
|
|
1202
1255
|
+ When it ascertains that the destination URL is based on the current running `index.html` document in the browser (an SPA architecture), a full page reload is prevented for *soft* navigation. But where the destination URL points out of the current document root (a [Multi SPA](#in-a-multi-spa-layout) architecture), navigation is allowed as a normal page load, and a new page root is loaded.
|
|
1203
1256
|
+ If navigation is initiated with any of the following keys pressed: Meta Key, Alt Key, Shift Key, Ctrl Key, navigation is allowed to work the default way - regardless of the first rule above.
|
|
1204
1257
|
+ If navigation is initiated from a link element that has the `target` attribute, or the `download` attribute, navigation is allowed to work the default way - regardless of the first rule above.
|
|
1205
1258
|
+ If navigation is initiated from a form element that has the `target` attribute, navigation is allowed to work the default way - regardless of the first rule above.
|
|
1206
1259
|
|
|
1207
|
-
>
|
|
1260
|
+
<details>
|
|
1261
|
+
<summary>Default Config</summary>
|
|
1262
|
+
|
|
1263
|
+
> File: `.webqit/webflo/client.json` | Command: `webflo config client spa_navigation=TRUE`
|
|
1264
|
+
|
|
1265
|
+
```json
|
|
1266
|
+
{ "spa_navigation": true }
|
|
1267
|
+
```
|
|
1268
|
+
<details>
|
|
1208
1269
|
|
|
1209
1270
|
##### SPA State
|
|
1210
1271
|
|
|
1211
|
-
|
|
1272
|
+
On the client side of a Webflo application, [the idea of state](#the-idea-of-state) also includes the following aspects of the client-side lifecycle that can be used to provide visual cues on the UI.
|
|
1212
1273
|
|
|
1213
1274
|
###### The `document.state.network` Object
|
|
1214
1275
|
|
|
1215
1276
|
This is a *live* object that exposes the network activity and network state of the application.
|
|
1216
1277
|
|
|
1217
1278
|
```js
|
|
1218
|
-
console.log(document.state.network) // { requesting, remote, error, redirecting,
|
|
1279
|
+
console.log(document.state.network) // { requesting, remote, error, redirecting, connectivity, }
|
|
1219
1280
|
```
|
|
1220
1281
|
|
|
1221
1282
|
+ **`network.requesting`: `null|Object`** - This property tells when a request is ongoing, in which case it exposes the `params` object used to initiate the request.
|
|
1283
|
+
|
|
1284
|
+
On the UI, this could be used to hide a menu drawer that may have been open.
|
|
1285
|
+
|
|
1286
|
+
```html
|
|
1287
|
+
<menu-drawer>
|
|
1288
|
+
<script type="subscript">
|
|
1289
|
+
let { network: { requesting } } = document.state;
|
|
1290
|
+
if (requesting) {
|
|
1291
|
+
$(this).attr('open', false);
|
|
1292
|
+
}
|
|
1293
|
+
</script>
|
|
1294
|
+
</menu-drawer>
|
|
1295
|
+
```
|
|
1296
|
+
|
|
1222
1297
|
+ **`network.remote`: `null|String`** - This property tells when a remote request is ongoing - usually the same navigation requests as at `network.requesting`, but when not handled by any client-side route handlers, or when `next()`ed to this point by route handlers. The `remote` property also goes live when a route handler calls the special `fetch()` function that they recieve on their fourth parameter.
|
|
1298
|
+
|
|
1299
|
+
On the UI, this could be used to show/hide a spinner, or progress bar, to provide a visual cue.
|
|
1300
|
+
|
|
1301
|
+
```html
|
|
1302
|
+
<progress-bar>
|
|
1303
|
+
<script type="subscript">
|
|
1304
|
+
let { network: { remote } } = document.state;
|
|
1305
|
+
$(this).attr('hidden', !remote);
|
|
1306
|
+
</script>
|
|
1307
|
+
</progress-bar>
|
|
1308
|
+
```
|
|
1309
|
+
|
|
1223
1310
|
+ **`network.error`: `null|Error`** - This property tells when a request is *errored* in which case it contains an `Error` instance of the error. For requests that can be retried, the `Error` instance also has a custom `retry()` method.
|
|
1311
|
+
|
|
1312
|
+
On the UI, this could be used to show/hide cute error elements.
|
|
1313
|
+
|
|
1314
|
+
```html
|
|
1315
|
+
<nice-error>
|
|
1316
|
+
<script type="subscript">
|
|
1317
|
+
let { network: { error } } = document.state;
|
|
1318
|
+
$(this).attr('hidden', !error);
|
|
1319
|
+
</script>
|
|
1320
|
+
</nice-error>
|
|
1321
|
+
```
|
|
1322
|
+
|
|
1224
1323
|
+ **`network.redirecting`: `null|String`** - This property tells when a client-side redirect is ongoing - see [Scenario 4: Single Page Navigation Requests and Responses](#scenario-4-single-page-navigation-requests-and-responses) - in which case it exposes the destination URL.
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1324
|
+
|
|
1325
|
+
On the UI, this could be used to prevent further interactions with the outgoing page.
|
|
1326
|
+
|
|
1327
|
+
```html
|
|
1328
|
+
<body>
|
|
1329
|
+
<script type="subscript">
|
|
1330
|
+
let { network: { redirecting } } = document.state;
|
|
1331
|
+
$(this).css(redirecting ? { pointerEvents: 'none', filter: 'blur(2)' } : { pointerEvents: 'auto', filter: 'blur(0)' });
|
|
1332
|
+
</script>
|
|
1333
|
+
</body>
|
|
1334
|
+
```
|
|
1335
|
+
|
|
1336
|
+
+ **`network.connectivity`: `String`** - This property tells of [the browser's ability to connect to the network](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine): `online`, `offline`.
|
|
1337
|
+
|
|
1338
|
+
On the UI, this could be used to show/hide a connectivity status.
|
|
1339
|
+
|
|
1340
|
+
```html
|
|
1341
|
+
<body>
|
|
1342
|
+
<script type="subscript">
|
|
1343
|
+
let { network: { connectivity } } = document.state;
|
|
1344
|
+
$(this).attr( 'connectivity', connectivity });
|
|
1345
|
+
</script>
|
|
1346
|
+
</body>
|
|
1347
|
+
```
|
|
1348
|
+
|
|
1349
|
+
Here are some additional examples with the [Observer API](#the-observer-api).
|
|
1228
1350
|
|
|
1229
1351
|
```js
|
|
1230
1352
|
// Visualize the network state
|
|
@@ -1238,12 +1360,12 @@ Observer.observe(document.state.network, onlineVisualizer);
|
|
|
1238
1360
|
```
|
|
1239
1361
|
|
|
1240
1362
|
```js
|
|
1241
|
-
// Visualize the '
|
|
1242
|
-
let
|
|
1243
|
-
console.log('You are ', e.value
|
|
1363
|
+
// Visualize the 'connectivity' property
|
|
1364
|
+
let connectivityVisualizer = e => {
|
|
1365
|
+
console.log('You are ', e.value);
|
|
1244
1366
|
};
|
|
1245
|
-
Observer.observe(document.state.network, '
|
|
1246
|
-
// Or: Observer.observe(document.state, [ ['network', '
|
|
1367
|
+
Observer.observe(document.state.network, 'connectivity', connectivityVisualizer);
|
|
1368
|
+
// Or: Observer.observe(document.state, [ ['network', 'connectivity'] ], connectivityeVisualizer);
|
|
1247
1369
|
```
|
|
1248
1370
|
|
|
1249
1371
|
```js
|
|
@@ -1258,26 +1380,124 @@ Observer.observe(document.state.network, 'error', e => {
|
|
|
1258
1380
|
});
|
|
1259
1381
|
```
|
|
1260
1382
|
|
|
1261
|
-
|
|
1383
|
+
##### Form Actions
|
|
1384
|
+
|
|
1385
|
+
When navigation occurs [via form submissions](#scenario-4-single-page-navigation-requests-and-responses), the form element and the submit button are made to go on the *active* state while the request is being processed. For both of these elements, the Webflo client simply sets the `element.state.active` to `true` on submission, then `false`, on completion.
|
|
1386
|
+
|
|
1387
|
+
```html
|
|
1388
|
+
<form method="post">
|
|
1389
|
+
<input name="username" placeholder="Your username..." />
|
|
1390
|
+
<script>
|
|
1391
|
+
$(this).css(this.state.active ? { pointerEvents: 'none', opacity: 'o.5' } : { pointerEvents: 'auto', opacity: '1' });
|
|
1392
|
+
</script>
|
|
1393
|
+
</form>
|
|
1394
|
+
```
|
|
1395
|
+
|
|
1396
|
+
One more thing: HTML forms can only accept two HTTP methods on their `method` attribute: `GET`, `POST`! The same constraint exists on the equivalent `formmethod` attribue in submit buttons. You are able to overcome this in Webflo by using alternative `data-` attributes: `data-method`, `data-formmethod`, respectively.
|
|
1397
|
+
|
|
1398
|
+
```html
|
|
1399
|
+
<form data-method="patch">
|
|
1400
|
+
<input name="price" placeholder="Enter new price..." />
|
|
1401
|
+
</form>
|
|
1402
|
+
```
|
|
1403
|
+
|
|
1404
|
+
#### Progressive Web Apps
|
|
1262
1405
|
|
|
1263
|
-
|
|
1406
|
+
Webflo client-side applications are intended to provide an app-like-first experience. So unless disabled in config, a [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) is built as part of your application on running the `npm run generate` command. You may define [route handlers in the `/worker` directory](#handler-functions-and-layout) of your application, and these will be built into the service worker to handle Same-Origin requests of the application. Where there are no *worker* handlers, or where these forward incoming requests, requests are fetched, either from the cache, or from the network, depending on the fetching strategy built into the Service Worker.
|
|
1264
1407
|
|
|
1265
|
-
|
|
1408
|
+
<details>
|
|
1409
|
+
<summary>Default Config</summary>
|
|
1266
1410
|
|
|
1267
|
-
|
|
1411
|
+
> File: `.webqit/webflo/client.json` | Command: `webflo config client enable_service_worker=TRUE`
|
|
1268
1412
|
|
|
1269
|
-
|
|
1413
|
+
```json
|
|
1414
|
+
{ "enable_service_worker": true }
|
|
1415
|
+
```
|
|
1416
|
+
<details>
|
|
1417
|
+
|
|
1418
|
+
##### Fetching Strategy
|
|
1419
|
+
|
|
1420
|
+
+ **Network First** - This strategy tells the Service Worker to always attempt fetching from the network first for given resources, before fetching from the cache. On every successful network fetch, a copy of the response is saved to the cache for next time. (This is good for resources that need to be fresh to the user on a "best effort" basis.) Unless changed, this is Webflo's default fetching strategy. When not the default strategy, a list of specific URLs that should be fetched this way can be configured.
|
|
1421
|
+
|
|
1422
|
+
<details>
|
|
1423
|
+
<summary>Default Config</summary>
|
|
1424
|
+
|
|
1425
|
+
> File: `.webqit/webflo/client.json` | Command: `webflo config client default_fetching_strategy=network-first`
|
|
1426
|
+
|
|
1427
|
+
```json
|
|
1428
|
+
{ "default_fetching_strategy": "network-first" }
|
|
1429
|
+
```
|
|
1270
1430
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1431
|
+
*To list specific URLs...*
|
|
1432
|
+
|
|
1433
|
+
```json
|
|
1434
|
+
{ "network_first_urls": [ "/logo.png" ] }
|
|
1435
|
+
```
|
|
1436
|
+
<details>
|
|
1437
|
+
|
|
1438
|
+
+ **Cache First** - This strategy tells the Service Worker to always attempt fetching from the cache first for given resources, before fetching from the network. After serving a cached response, or where not found in cache, a network fetch happens and a copy of the response is saved to the cache for next time. (This is good for resources that do not critially need to be fresh to the user.) When not the default strategy, a list of specific URLs that should be fetched this way can be configured.
|
|
1439
|
+
|
|
1440
|
+
<details>
|
|
1441
|
+
<summary>Default Config</summary>
|
|
1442
|
+
|
|
1443
|
+
> File: `.webqit/webflo/client.json` | Command: `webflo config client default_fetching_strategy=cache-first`
|
|
1444
|
+
|
|
1445
|
+
```json
|
|
1446
|
+
{ "default_fetching_strategy": "cache-first" }
|
|
1447
|
+
```
|
|
1448
|
+
|
|
1449
|
+
*To list specific URLs...*
|
|
1450
|
+
|
|
1451
|
+
```json
|
|
1452
|
+
{ "cache_first_urls": [ "/logo.png" ] }
|
|
1453
|
+
```
|
|
1454
|
+
<details>
|
|
1455
|
+
|
|
1456
|
+
+ **Network Only** - This strategy tells the Service Worker to always fetch given resources from the network only. They are simply not available when offline. (This is good for resources that critially need to be fresh to the user.) When not the default strategy, a list of specific URLs that should be fetched this way can be configured.
|
|
1457
|
+
|
|
1458
|
+
<details>
|
|
1459
|
+
<summary>Default Config</summary>
|
|
1460
|
+
|
|
1461
|
+
> File: `.webqit/webflo/client.json` | Command: `webflo config client default_fetching_strategy=network-only`
|
|
1462
|
+
|
|
1463
|
+
```json
|
|
1464
|
+
{ "default_fetching_strategy": "network-only" }
|
|
1465
|
+
```
|
|
1466
|
+
|
|
1467
|
+
*To list specific URLs...*
|
|
1468
|
+
|
|
1469
|
+
```json
|
|
1470
|
+
{ "network_only_urls": [ "/logo.png" ] }
|
|
1471
|
+
```
|
|
1472
|
+
<details>
|
|
1473
|
+
|
|
1474
|
+
+ **Cache Only** - This strategy tells the Service Worker to always fetch given resources from the cache only. (This is good for resources that do not change often.) When not the default strategy, a list of specific URLs that should be fetched this way can be configured. The listed resources are pre-cached ahead of when they'll be needed - and are served from the cache each time. (Pre-caching happens on the one-time `install` event of the Service Worker.)
|
|
1475
|
+
|
|
1476
|
+
<details>
|
|
1477
|
+
<summary>Default Config</summary>
|
|
1478
|
+
|
|
1479
|
+
> File: `.webqit/webflo/client.json` | Command: `webflo config client default_fetching_strategy=cache-only`
|
|
1480
|
+
|
|
1481
|
+
```json
|
|
1482
|
+
{ "default_fetching_strategy": "cache-only" }
|
|
1483
|
+
```
|
|
1484
|
+
|
|
1485
|
+
*To list specific URLs...*
|
|
1486
|
+
|
|
1487
|
+
```json
|
|
1488
|
+
{ "cache_only_urls": [ "/logo.png" ] }
|
|
1489
|
+
```
|
|
1490
|
+
<details>
|
|
1275
1491
|
|
|
1276
1492
|
In all cases above, the convention for specifying URLs for a strategy accepts [URL patterns](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) - against which URLs can be matched on the fly. For example, to place all files in an `/image` directory (and subdirectories) on the *Cache First* strategy, the pattern `/image/*` can be used. To place all `.svg` files in an `/icons` directory (including subdirectories) on the *Cache Only* strategy, the pattern `/icons/*.svg` can be used. (Specifically for the *Cache Only* strategy, patterns are resolved at Service Worker build-time, and each pattern must match, at least, a file.)
|
|
1277
1493
|
|
|
1278
|
-
|
|
1494
|
+
```json
|
|
1495
|
+
{ "cache_only_urls": [ "/icons/*.svg" ] }
|
|
1496
|
+
```
|
|
1497
|
+
|
|
1498
|
+
##### Cross-Thread Communications
|
|
1279
1499
|
|
|
1280
|
-
A couple APIs exists in browsers for establishing a two-way communication channel between a page and its
|
|
1500
|
+
A couple APIs exists in browsers for establishing a two-way communication channel between a page and its Service Worker, for firing UI Notifications from either ends, and for implementing Push Notifications. Webflo offers to simply this with a unifying set of conventions:
|
|
1281
1501
|
|
|
1282
1502
|
+ The `workport` API - an object with simple methods for working with *cross-thread* messages, UI and Push Notifications.
|
|
1283
1503
|
|
|
@@ -1417,7 +1637,7 @@ export default function(event, context, next) {
|
|
|
1417
1637
|
}
|
|
1418
1638
|
```
|
|
1419
1639
|
|
|
1420
|
-
You are always able to lay out your route handlers in the structure for a
|
|
1640
|
+
You are always able to lay out your route handlers in the structure for a formal REST API.
|
|
1421
1641
|
|
|
1422
1642
|
```shell
|
|
1423
1643
|
server
|
|
@@ -1426,15 +1646,15 @@ server
|
|
|
1426
1646
|
└── api/v1/products/index.js
|
|
1427
1647
|
```
|
|
1428
1648
|
|
|
1429
|
-
And if you will partition your backend for both page routes and a
|
|
1649
|
+
And if you will partition your backend for both page routes and a formal REST API...
|
|
1430
1650
|
|
|
1431
1651
|
```shell
|
|
1432
1652
|
server
|
|
1433
1653
|
├── index.js ──┐
|
|
1434
|
-
├── cart/index.js
|
|
1654
|
+
├── cart/index.js ├─ Page Routes
|
|
1435
1655
|
├── products/index.js ──┘
|
|
1436
1656
|
├── api/v1/index.js ──┐
|
|
1437
|
-
├── api/v1/orders/index.js
|
|
1657
|
+
├── api/v1/orders/index.js ├─ REST API
|
|
1438
1658
|
└── api/v1/products/index.js ──┘
|
|
1439
1659
|
```
|
|
1440
1660
|
|
|
@@ -1485,7 +1705,46 @@ You have a static site!
|
|
|
1485
1705
|
|
|
1486
1706
|
### Webflo Config
|
|
1487
1707
|
|
|
1488
|
-
|
|
1708
|
+
Webflo comes *convention-first*! But it is entirely configurable for when you need it! The easiest way to do this is to run the command `webflo config` and follow the walkthrough. To simply get an overview, use the command `webflo config help`, and all commands and their description are shown.
|
|
1709
|
+
|
|
1710
|
+
## Technology Stack
|
|
1711
|
+
|
|
1712
|
+
Webflo applications are often built on/with the following technologies.
|
|
1713
|
+
|
|
1714
|
+
### OOHTML
|
|
1715
|
+
|
|
1716
|
+
[OOHTML](https://github.com/webqit/oohtml) is a proposed set of new features for HTML that makes it fun to hand-author your UI! Within OOHTML are [HTML Modules](https://github.com/webqit/oohtml#html-modules) and [HTML Imports](https://github.com/webqit/oohtml#html-imports), [Reactive Scripts](https://github.com/webqit/oohtml#subscript) and more!
|
|
1717
|
+
|
|
1718
|
+
Webflo natively supports OOHTML in full! But it is also possible to switch this to none, or to partial support - when specific features aren't needed anywhere in your application. Server-side and client-side support for OOHTML exist independently. This is good when, for example, your application places more importance on SSR, and less on CSR, in which case a reduced support for OOHTML can reduce the overall client JS bundle size.
|
|
1719
|
+
|
|
1720
|
+
<details>
|
|
1721
|
+
<summary>Default Config</summary>
|
|
1722
|
+
|
|
1723
|
+
> File: `.webqit/webflo/client.json` | Command: `webflo config client oohtml_support=full`
|
|
1724
|
+
|
|
1725
|
+
> File: `.webqit/webflo/client.json` | Command: `webflo config server oohtml_support=full`
|
|
1726
|
+
|
|
1727
|
+
```json
|
|
1728
|
+
{ "oohtml_support": "full" }
|
|
1729
|
+
```
|
|
1730
|
+
|
|
1731
|
+
*Values: `full`, `namespacing`, `scripting`, `templating`, `none` - See [details at OOHTML SSR](https://github.com/webqit/oohtml-ssr#options)*
|
|
1732
|
+
<details>
|
|
1733
|
+
|
|
1734
|
+
|
|
1735
|
+
### OOHTML SSR
|
|
1736
|
+
|
|
1737
|
+
[OOHTML SSR](https://github.com/webqit/oohtml-ssr) is a server-side DOM implementation with native support for OOHTML. This is internally used by Webflo as the Server-Side Rendering engine, and it it what gives Webflo its native support for OOHTML.
|
|
1738
|
+
|
|
1739
|
+
### OOHTML CLI
|
|
1740
|
+
|
|
1741
|
+
[OOHTML CLI](https://github.com/webqit/oohtml-cli) is a small Command Line utility that automates certain aspects of hand-authored OOHTML-based documents.
|
|
1742
|
+
|
|
1743
|
+
### The Observer API
|
|
1744
|
+
|
|
1745
|
+
[The Observer API](https://github.com/webqit/observer) is a simple set of functions for intercepting and observing JavaScript objects and arrays. (Reflection, Interception, and Events.)
|
|
1746
|
+
|
|
1747
|
+
This is part of OOHTML's reactivity system, and it is made available on OOHTML-based documents as `window.WebQit.Observer`.
|
|
1489
1748
|
|
|
1490
1749
|
## Getting Started
|
|
1491
1750
|
|
|
@@ -1503,13 +1762,3 @@ All forms of contributions and PR are welcome! To report bugs or request feature
|
|
|
1503
1762
|
## License
|
|
1504
1763
|
|
|
1505
1764
|
MIT.
|
|
1506
|
-
|
|
1507
|
-
...
|
|
1508
|
-
|
|
1509
|
-
## Getting Involved
|
|
1510
|
-
|
|
1511
|
-
All forms of contributions and PR are welcome! To report bugs or request features, please submit an [issue](https://github.com/webqit/webflo/issues).
|
|
1512
|
-
|
|
1513
|
-
## License
|
|
1514
|
-
|
|
1515
|
-
MIT.
|
package/package.json
CHANGED
|
@@ -118,7 +118,7 @@ export default class Runtime {
|
|
|
118
118
|
return params;
|
|
119
119
|
}, {});
|
|
120
120
|
// We support method hacking
|
|
121
|
-
submitParams.method = (submitter && submitter.dataset.
|
|
121
|
+
submitParams.method = (submitter && submitter.dataset.formmethod) || form.dataset.method || submitParams.method;
|
|
122
122
|
submitParams.submitter = submitter;
|
|
123
123
|
// ---------------
|
|
124
124
|
var actionEl = window.document.createElement('a');
|
|
@@ -152,8 +152,8 @@ export default class Runtime {
|
|
|
152
152
|
// -----------------------
|
|
153
153
|
// Initialize network
|
|
154
154
|
Observer.set(this, 'network', {});
|
|
155
|
-
window.addEventListener('online', () => Observer.set(this.network, '
|
|
156
|
-
window.addEventListener('offline', () => Observer.set(this.network, '
|
|
155
|
+
window.addEventListener('online', () => Observer.set(this.network, 'connectivity', 'online'));
|
|
156
|
+
window.addEventListener('offline', () => Observer.set(this.network, 'connectivity', 'offline'));
|
|
157
157
|
|
|
158
158
|
// -----------------------
|
|
159
159
|
// Service Worker && COMM
|