Now.js Framework Documentation
Feature bundles — parts of Now.js loaded when a page uses them
Feature bundles — parts of Now.js loaded when a page uses them
Overview
now.core.min.js leaves out the parts most pages never use. They are separate files in Now/dist
(now.<name>.min.js, and now.<name>.min.css when they have styles), and the core loads one the
first time a page needs it.
| Name | Files | Contains | Loaded when |
|---|---|---|---|
code |
now.code.min.js + .css |
CodeEditor, CodeElementFactory |
a data-element="code" field |
richtext |
now.richtext.min.js |
RichTextElementFactory |
a data-element="richtext" field (the editor itself is still richtext-editor.min.js, loaded by the app) |
sync |
now.sync.min.js |
SyncManager |
Now.init({sync: {enabled: true}}) |
editinplace |
now.editinplace.min.js |
EditInPlaceManager |
data-component="editinplace" |
taskmonitor |
now.taskmonitor.min.js + .css |
TaskMonitorComponent |
data-component="task-monitor" |
securityconfig |
now.securityconfig.min.js |
SecurityConfig |
Now.require('SecurityConfig') only |
calendar |
now.calendar.min.js |
CalendarManager |
[data-calendar] |
gallery |
now.gallery.min.js + .css |
ImageGallery |
data-component="image-gallery" |
iconpicker |
now.iconpicker.min.js |
IconPicker |
data-component="icon-picker" |
tabs |
now.tabs.min.js |
TabsComponent |
data-component="tabs" |
lineitems |
now.lineitems.min.js |
LineItemsManager |
[data-line-items] |
sortable |
now.sortable.min.js |
Sortable |
data-component="sortable", row-sortable tables (data-row-sortable), sortable file lists |
calendar.css and tabs.css stay in now.core.min.css: the core's date field uses the calendar's
styles, and tabs.css keeps the unselected tab panels hidden before the tabs bundle has loaded.
Pages that use markup do nothing
Markup that uses one of these parts (the "Loaded when" column) loads its bundle, whether it is on the
page at start or added later (a page the router loads, an ApiComponent render, a modal). The core's
own code that uses them loads them too: data-row-sortable tables, line items bound with
data-attr="data:...", sortable file fields.
ApiComponent starts loading the bundles its markup uses together with its data and renders once they are
there, so api:content-rendered finds the line items' rows in place even when the bundle arrives after the data.
data-on-load handlers run once the bundles their markup uses are loaded, so the handler of a
form with [data-line-items] can call LineItemsManager.getInstance(table) at once, as when
LineItemsManager was part of the core. (When a bundle cannot be loaded, the handler still runs,
without that part.)
App code that uses them directly: Now.require()
Code that uses one of their globals by itself, not through markup, waits for it first:
await Now.require('Sortable');
new Sortable(list, {draggable: 'tr'});
await Now.require('SyncManager');
await SyncManager.enable();
await Now.require('CodeEditor', 'IconPicker'); // several at once- Takes the names in the table (
sortable) or the globals a bundle defines (Sortable,SyncManager,CodeEditor). - Returns a Promise that resolves once they are ready. A bundle is loaded once; every call gets the same promise.
- When the globals are already there (a page that loads the bundle with its own
<script>), it resolves without loading anything. - A bundle that fails to load (missing file, offline) rejects, and the next call tries again.
- An unknown name rejects.
if (window.Sortable) or typeof SyncManager !== 'undefined' alone is no longer enough: before the
bundle is loaded it is false and the work is silently skipped. And Chrome has a native
window.SyncManager (the Background Sync API) on every https page, so typeof SyncManager is
'function' even when Now.js's SyncManager is not loaded.
A manager in a bundle (CalendarManager, EditInPlaceManager, SyncManager) gets its section of
Now.init() as the core's managers do: after Now.require('SyncManager'),
SyncManager.config.syncInterval is the value passed in Now.init({sync: {syncInterval}}).
Where the files come from
Bundles are loaded from the folder now.core.min.js came from, with the same query: a page that
loads Now/dist/now.core.min.js?v=1.2 loads Now/dist/now.tabs.min.js?v=1.2 (a CDN works the same
way). Deploy the whole Now/dist folder, not only the core.
When the bundles live elsewhere, set Now.init({paths: {dist: '/assets/now/'}}). The scripts the
core adds carry the nonce of the core's own <script> (for a nonce-based CSP).
Offline
ServiceWorkerManager appends the bundle URLs that are not cached yet to the precache list it sends
to the service worker, so the first page opened after install keeps every bundle, and bundles already
kept are not fetched again on every page. Turn it off with
ServiceWorkerManager.init({precacheFeatures: false}).
API
| Method | Returns | What for |
|---|---|---|
Now.require(...names) |
Promise<void> |
Load bundles and wait until they are ready |
Now.hasFeature(name) |
boolean |
Whether a bundle is loaded (a browser's own global of the same name does not count) |
Now.featureUrls() |
string[] |
The URL of every file of every bundle (what ServiceWorkerManager precaches) |
Now.featuresUsedBy(root) |
string[] |
The bundles the markup inside root uses that are not loaded yet |
Now.features |
Object |
The table above: {js, css, globals, selector, elements, config} for each name |
Build
npm run build builds every bundle (it ends with build:features) from Now/features/<name>.js.
The target is feature-<name>, e.g. cross-env BUILD_TARGET=feature-tabs vite build. Never run
build:core alone: the core empties Now/dist before it builds.
To add a bundle: create Now/features/<name>.js importing its files, remove those files from
Now/core-entry.js, and add the name to features in vite.config.js, to Now.features in
Now/Now.js and to build:features in package.json.