Muban provides a couple of util methods that are exposed. This file will describe all the methods that are exposed by the Muban-core. The utils are divided up into sub-sections to keep track on what part they apply to.
Init components for the passed container and all children in the DOM.
initComponents(rootElement: HTMLElement): void
- rootElement - The container to make 'interactive'. This container and all elements in it will
be searched for Muban components (using the
data-component
attribute), and a new class instance will be created for every component. All HTML outside of this component will be ignored.
Cleans all component classes previously created by calling initComponents on the same container.
cleanElement(rootElement: HTMLElement): void
- rootElement - The container to 'clean'. This container and all elements in it will be searched
for Muban components (using the
data-component
attribute) that have an instance registered. All HTML outside of this component will be ignored.
You will only need this method when removing HTML from the DOM (or replacing it with something else), to make sure there won't be any active code around linked to elements that don't exist anymore.
Updates an element with new HTML, makes use of cleanElement
and initComponents
.
updateElement(element: HTMLElement, html: string): void
- element - The element to clean and insert the new HTML in.
- html - The new HTML to add to the element, it will be made interactive afterwards.
First calls cleanElement
to clean up the current HTML in the element, then adds the provided HTML
in the DOM and calls initComponents
.
Mostly useful when receiving rendered HTML from an API call that should be inserted in the DOM.
Returns a class instance for DOM node.
getComponentForElement(element: HTMLElement): CoreComponent
- element - A HTML element with a
data-component
attribute, where a previous call toinitComponents
created a class instance. - returns CoreComponent - The created class instance for the provided element.
This can be useful for inter-component communication. From component A
you can do a
querySelector
to find the DOM node of another component, and then call getComponentForElement
to
retrieve the instance. From there, you can read properties, call methods or add events.
Muban creates child components first, so in the constructor
of any components, all children
classes already have been instantiated. If you want to have access to class instances of parent DOM
elements, you should call this method from the adopted()
lifecycle method.
Starts up Muban and makes sure all components get initialised.
bootstrap(
appRoot: HTMLElement,
options: {
indexTemplate: (data: any) => string;
appTemplate: (data: any) => string;
dataContext: any;
partialsContext: any;
Handlebars: any;
onBeforeInit?: () => void;
onInit?: () => void;
onUpdate?: () => void;
onData: (data: object, pageName: string) => object;
registerPartialMap?: Array<(path: string) => string | null>;
pageName?: string;
} = {}): {
updateData: (changedContext) => void,
updatePartials: (changedContext) => void,
update: (updatedIndexTemplate, updatedAppTemplate) => void,
}
- appRoot - The container where the Muban application lives, most likely a div in the
<body>
. - indexTemplate - The hbs template to render the Muban index page.
- appTemplate - The hbs template to render the application shell, includes logic to render all the blocks.
- dataContext - A webpack context with all data (yaml/json) files.
- partialsContext - A webpack context with component hbs files.
- Handlebars - The Handlebars instance to register templates to.
- onBeforeInit - Optional callback that gets called right before the application becomes fully interactive.
- onInit - Optional callback that gets called after the application is fully interactive.
- onUpdate - Optional callback that gets called after hot reloading did an update.
- onData - Optional callback that gets called before rendering the page, and gives you the opportunity to modify the data before rendering.
- registerPartialMap - A map with functions to define if and how partials should be registered.
- pageName - Override a pageName to render, when you don't want to make use of the default url parsing logic of Muban.
- returns App - An object with 3 functions that can be called when hot reloading triggers. The
app will do the appropriate re-rendering and trigger the
onUpdate
callback afterwards.
Both indexTemplate
and appTemplate
are passed from the outside, so you have full control in the
project about where they are located. Together with the dataContext
and partialsContext
they are
kept outside so we can apply hot reloading logic. When any of those 4 things change, we update the
returned app
object.
The registerPartialsMap
has a default value:
[path => (path.includes('/block/') ? /\/([^/]+)\.hbs/gi.exec(path)[1] : null)];
The above will first check if there is /block/
in the path, and if so it will return the basename.
So a path of component/block/paragraph/paragraph.hbs
will return paragraph
. This means that the
partial is registered as paragraph
, and is used in the data.yaml
with that block name.
You can register multiple functions, but the first one that returns a non-null value will short-circuit the map.
When providing a custom map, make sure to also include the default value above if you want to keep it, since it will overwrite the complete map.
This option is most useful if you want to render non-block components in a dynamic way in
Handlebars. By default, the handlebars-loader will auto-require all static partial includes, but
dynamic includes (e.g. using the lookup
helper) will need to be registered manually upfront.
Renders an item in the DOM using a handlebars template and some data.
renderItem(
container: HTMLElement,
template: (data?: any) => string,
data: any,
append: boolean = false,
): void
- container - The container to render the item in.
- template - A precompiled Handlebars template, where you can pass in data and get the rendered HTML as a result.
- data - The data object being passed to the Handlebars template.
- append - When false (default), it cleans the container, so only the rendered HTML will end up in the container. When true, it will append the rendered item at the end of the container.
The rendered HTML will be made interactive by calling initComponents
on the newly added
components.
This is useful when you don't have an HTML node available in the DOM to clone, or if the template contains a lot of rendering logic that you don't want to repeat in JavaScript. The downside of using this is that eventually the HTML will live in two places; in the imported .hbs template in the JS bundle, and in the rendered HTML from the server.
An example:
import { renderItem } from 'muban-core/lib/utils/dataUtils';
import buttonTemplate from '../../general/button/button.hbs?include';
renderItem(container, buttonTemplate, { text: 'button text' });
Renders a list of items in the DOM using a handlebars template and an Array of data.
renderItems(
container: HTMLElement,
template: (data?: any) => string,
data: Array<any>,
append: boolean = false,
): void
- container - The container to render the item in.
- template - A precompiled Handlebars template, where you can pass in data and get the rendered HTML as a result. The template will be called for each item in the data Array.
- data - An Array where each item is being passed to the Handlebars template.
- append - When false (default), it cleans the container, so only the rendered HTML will end up in the container. When true, it will append the rendered items at the end of the container.
This function is similar to renderItem
, excepts it renders a list. See the documentation on
renderItem
for more information.
Sets up a binding to the element, and sets the element's content as initial value.
initTextBinding(
element: HTMLElement,
html: boolean = false,
): KnockoutObservable<string>
- element - The element to apply the binding to, and where to extract the content from.
- html - When true, extracts the content as HTML, and sets the HTML binding. When false, extract the text contents, and sets the text binding.
- returns KnockoutObservable<string> - The observable that is linked to the element. When updating this observable, the content in the element will update.
Please have a look at the knockout documentation for more information about the
text
and
html
bindings.
An example:
// init the binding
const label = initTextBinding(this.getElement('.label'));
// would return the original label text value
console.log(label());
// set the new label value, updates the HTML
label('new label text');
Sets up a foreach template binding to a container, and can optionally extract the old data.
initListBinding<T>(
container: HTMLElement,
templateName: string,
configOrData: Array<T> | any,
additionalData?: any,
): KnockoutObservable<Array<T>>
- container - The container to apply the binding to, and where to extract the content from.
- templateName - The Knockout template to render for each
- configOrData - When passing an Array, it will be used as initial data to render. When passing
an Object, it will be used as configuration for
html-extract-data
. The extracted data from the HTML will be used to render the same content again using the Knockout template. - additionalData - Useful when extracting data from HTML, it is passed as third parameter to
html-extract-data
, and merged with each item extracted. This could be used to setup bindings that don't deal with data (e.g. click bindings). - returns KnockoutObservable<Array> - The observable Array that is linked to the rendered items, filled with either the passed data Array or the extracted data from the DOM. When updating this observable, the list in the DOM will also update. Note, the fields of individual items are not observable, only the list itself.
This function can be used in two ways:
- setting up a normal Knockout
foreach
binding by passing data to render, or - by extracting the rendered items from the DOM as a starting point using the
html-extract-data
module. This option is mostly useful when you already have rendered items in the DOM, and you want to append some more at a later time. Or when you want to make the current items interactive with additional Knockout bindings.
Please have a look at the knockout documentation how to define templates, and how foreach bindings and observableArrays work.
And when extracting data, check the documentation from html-extract-data.
A data example:
const items = initListBinding(this.getElement('.items'), 'list-template', [
{ name: 'Bob', age: 21 },
{ name: 'Alice', age: 19 },
]);
// add a new item, will be added to the DOM
items.push({ name: 'Joe', age: 24 });
An extract example:
const items = initListBinding(
this.getElement('.items'),
'list-template',
{
query: '.grid-item',
data: {
name: '.name',
age: { query: '.age', convert: parseInt },
},
},
{
onClick(vm) {
console.log('clicked', vm);
},
},
);
// add a new item, will be added to the DOM
items.push({ name: 'Joe', age: 24 });