Utility functions for event binding, option merging, method normalization, and global configuration of DOM APIs and template renderers.
Functions for binding and unbinding events from entities (models, collections, other event emitters).
/**
* Bind events from an entity to methods on the target object
* @param entity - Object that triggers events (model, collection, etc.)
* @param bindings - Hash of event names to method names or functions
*/
function bindEvents(entity: EventsEntity, bindings: EventsHash): void;
/**
* Unbind events from an entity
* @param entity - Object that triggers events
* @param bindings - Hash of event names to method names or functions
*/
function unbindEvents(entity: EventsEntity, bindings: EventsHash): void;Usage Examples:
import { bindEvents, unbindEvents, View } from "backbone.marionette";
class MyView extends View {
initialize() {
// Bind model events manually
bindEvents(this.model, {
'change': this.render,
'change:name': 'onNameChange',
'destroy': function() {
console.log('Model destroyed');
}
});
}
onNameChange(model, name) {
console.log('Name changed to:', name);
}
onDestroy() {
// Unbind events on cleanup
unbindEvents(this.model, {
'change': this.render,
'change:name': 'onNameChange'
});
}
}
// Can also be used with any event emitter
const eventEmitter = new Backbone.Events();
const handler = {
onCustomEvent() {
console.log('Custom event fired');
}
};
bindEvents(eventEmitter, {
'custom:event': handler.onCustomEvent
});
eventEmitter.trigger('custom:event'); // Handler called
unbindEvents(eventEmitter, { 'custom:event': handler.onCustomEvent });Functions for binding and unbinding Backbone.Radio requests to methods.
/**
* Bind Radio channel requests to methods on the target object
* @param channel - Backbone.Radio channel
* @param bindings - Hash of request names to method names or functions
*/
function bindRequests(channel: RadioChannel, bindings: RequestsHash): void;
/**
* Unbind Radio channel requests
* @param channel - Backbone.Radio channel
* @param bindings - Hash of request names to method names or functions
*/
function unbindRequests(channel: RadioChannel, bindings: RequestsHash): void;Usage Examples:
import { bindRequests, unbindRequests } from "backbone.marionette";
import Radio from "backbone.radio";
class UserService {
constructor() {
this.currentUser = null;
this.channel = Radio.channel('users');
// Bind requests to methods
bindRequests(this.channel, {
'current': 'getCurrentUser',
'login': this.loginUser,
'logout': () => {
this.currentUser = null;
return true;
}
});
}
getCurrentUser() {
return this.currentUser;
}
loginUser(credentials) {
// Login logic
this.currentUser = { id: 1, name: credentials.username };
return this.currentUser;
}
destroy() {
// Cleanup requests
unbindRequests(this.channel, {
'current': 'getCurrentUser',
'login': this.loginUser
});
}
}
// Usage
const userService = new UserService();
const channel = Radio.channel('users');
const user = channel.request('current'); // null
const loggedInUser = channel.request('login', { username: 'john' });
console.log(loggedInUser); // { id: 1, name: 'john' }Functions for managing options and configuration in Marionette objects.
/**
* Merge specific keys from options onto the target object
* @param options - Options object to merge from
* @param keys - Array of key names to merge
*/
function mergeOptions(options: object, keys: string[]): void;
/**
* Get an option value from the object's options or as a property
* @param optionName - Name of the option to retrieve
* @returns The option value or undefined
*/
function getOption(optionName: string): any;Usage Examples:
import { mergeOptions, getOption, View } from "backbone.marionette";
class ConfigurableView extends View {
constructor(options = {}) {
super(options);
// Merge specific options onto this object
mergeOptions(options, ['apiUrl', 'timeout', 'debugMode']);
// Now this.apiUrl, this.timeout, this.debugMode are set
console.log('API URL:', this.apiUrl);
console.log('Timeout:', this.timeout);
console.log('Debug mode:', this.debugMode);
}
makeRequest() {
// Use getOption to get values from options or properties
const url = getOption.call(this, 'apiUrl') || '/api/default';
const timeout = getOption.call(this, 'timeout') || 5000;
// Make request with configured values
console.log(`Making request to ${url} with timeout ${timeout}`);
}
}
// Usage
const view = new ConfigurableView({
apiUrl: 'https://api.example.com',
timeout: 10000,
debugMode: true
});
view.makeRequest(); // Uses merged optionsFunctions for converting method name strings to actual function references.
/**
* Convert method name strings to actual function references
* @param hash - Hash object with method names as values
* @returns Hash with method names converted to functions
*/
function normalizeMethods(hash: MethodsHash): NormalizedMethodsHash;Usage Examples:
import { normalizeMethods, View } from "backbone.marionette";
class MyView extends View {
initialize() {
// Method names as strings
const eventHandlers = {
'click .button': 'onButtonClick',
'submit form': 'onFormSubmit',
'change input': this.onInputChange // Already a function
};
// Normalize to actual functions
const normalizedHandlers = normalizeMethods.call(this, eventHandlers);
console.log(typeof normalizedHandlers['click .button']); // 'function'
console.log(typeof normalizedHandlers['submit form']); // 'function'
console.log(typeof normalizedHandlers['change input']); // 'function'
}
onButtonClick() {
console.log('Button clicked');
}
onFormSubmit() {
console.log('Form submitted');
}
onInputChange() {
console.log('Input changed');
}
}Function for triggering both events and corresponding onMethod handlers.
/**
* Trigger both an event and its corresponding onMethod handler
* @param event - Event name to trigger
* @param args - Arguments to pass to handlers
* @returns Result from onMethod if it exists
*/
function triggerMethod(event: string, ...args: any[]): any;Usage Examples:
import { triggerMethod, View } from "backbone.marionette";
class MyView extends View {
initialize() {
// Listen to the event
this.on('custom:event', (data) => {
console.log('Event listener:', data);
});
}
doSomething() {
// Trigger both event and onMethod
triggerMethod.call(this, 'custom:event', { message: 'Hello' });
// This calls both the event listener above AND onCustomEvent method if it exists
}
onCustomEvent(data) {
console.log('onMethod handler:', data);
return 'handled';
}
}
// Usage
const view = new MyView();
const result = view.doSomething();
// Logs:
// "Event listener: { message: 'Hello' }"
// "onMethod handler: { message: 'Hello' }"
console.log('Result:', result); // "handled"Functions for configuring global behavior of Marionette components.
/**
* Set custom DOM API for Views, CollectionViews, and Regions
* @param mixin - DOM API mixin object with custom methods
*/
function setDomApi(mixin: DomApiMixin): void;
/**
* Set custom template renderer for Views and CollectionViews
* @param renderer - Template rendering function
*/
function setRenderer(renderer: RendererFunction): void;Usage Examples:
import { setDomApi, setRenderer } from "backbone.marionette";
// Custom DOM API using native DOM methods
const customDomApi = {
getEl(selector) {
if (typeof selector === 'string') {
return document.querySelectorAll(selector);
}
return [selector];
},
findEl(el, selector) {
return el.querySelectorAll(selector);
},
setContents(el, html) {
el.innerHTML = html;
},
appendContents(el, contents) {
if (typeof contents === 'string') {
el.insertAdjacentHTML('beforeend', contents);
} else {
el.appendChild(contents);
}
}
};
// Set custom DOM API globally
setDomApi(customDomApi);
// Custom template renderer using Handlebars
const handlebarsRenderer = (template, data) => {
if (typeof template === 'string') {
const compiled = Handlebars.compile(template);
return compiled(data);
} else if (typeof template === 'function') {
return template(data);
}
return '';
};
// Set custom renderer globally
setRenderer(handlebarsRenderer);
// Now all views will use the custom DOM API and Handlebars renderer
class MyView extends View {
template: '<h1>{{title}}</h1><p>{{content}}</p>' // Handlebars template
}Functions for managing feature flags in Marionette.
/**
* Check if a feature flag is enabled
* @param name - Feature flag name
* @returns True if feature is enabled
*/
function isEnabled(name: string): boolean;
/**
* Set a feature flag state
* @param name - Feature flag name
* @param state - Whether the feature should be enabled
*/
function setEnabled(name: string, state: boolean): void;Usage Examples:
import { isEnabled, setEnabled } from "backbone.marionette";
// Enable a feature flag
setEnabled('newUI', true);
setEnabled('experimentalFeature', false);
// Check feature flags
if (isEnabled('newUI')) {
console.log('Using new UI');
// Load new UI components
} else {
console.log('Using legacy UI');
// Load legacy UI components
}
// In a view
class FeatureAwareView extends View {
template() {
if (isEnabled('experimentalFeature')) {
return '<div class="experimental">Experimental content</div>';
}
return '<div class="standard">Standard content</div>';
}
behaviors() {
const behaviors = {
standard: StandardBehavior
};
if (isEnabled('experimentalFeature')) {
behaviors.experimental = ExperimentalBehavior;
}
return behaviors;
}
}Function for monitoring view state and propagating events to child views.
/**
* Monitor view state and propagate attach/detach events to children
* @param view - View to monitor
*/
function monitorViewEvents(view: View): void;Usage Examples:
import { monitorViewEvents, View } from "backbone.marionette";
class ParentView extends View {
initialize() {
// Monitor this view for attach/detach events
monitorViewEvents(this);
// Listen for attach/detach events
this.on('attach', this.onAttach);
this.on('detach', this.onDetach);
}
onAttach() {
console.log('Parent view attached to DOM');
// Start animations, timers, etc.
}
onDetach() {
console.log('Parent view detached from DOM');
// Clean up animations, timers, etc.
}
}
// Child views will automatically receive attach/detach events
class ChildView extends View {
onAttach() {
console.log('Child view attached to DOM');
}
onDetach() {
console.log('Child view detached from DOM');
}
}interface EventsEntity {
on(event: string, callback: Function): void;
off(event?: string, callback?: Function): void;
trigger(event: string, ...args: any[]): void;
}
interface EventsHash {
[eventName: string]: string | Function;
}
interface RequestsHash {
[requestName: string]: string | Function;
}
interface MethodsHash {
[key: string]: string | Function;
}
interface NormalizedMethodsHash {
[key: string]: Function;
}
interface RadioChannel {
request(requestName: string, ...args: any[]): any;
trigger(eventName: string, ...args: any[]): void;
reply(requestName: string, handler: Function): void;
replyOnce(requestName: string, handler: Function): void;
stopReplying(requestName?: string): void;
}
interface DomApiMixin {
createBuffer?(): DocumentFragment;
getDocumentEl?(el: Element): Document;
getEl?(selector: string | Element): Element | Element[];
findEl?(el: Element, selector: string): Element[];
hasEl?(el: Element, childEl: Element): boolean;
detachEl?(el: Element): void;
replaceEl?(newEl: Element, oldEl: Element): void;
swapEl?(el1: Element, el2: Element): void;
setContents?(el: Element, html: string): void;
appendContents?(el: Element, contents: Element | DocumentFragment | string, options?: object): void;
hasContents?(el: Element): boolean;
detachContents?(el: Element): DocumentFragment;
}
type RendererFunction = (template: string | Function, data: object) => string;// Create reusable utility mixins
const ApiMixin = {
makeRequest(url, options = {}) {
const apiUrl = getOption.call(this, 'apiUrl') || '';
const timeout = getOption.call(this, 'timeout') || 5000;
return fetch(apiUrl + url, {
timeout,
...options
});
}
};
// Apply to views
class ApiEnabledView extends View {
initialize(options) {
mergeOptions(options, ['apiUrl', 'timeout']);
_.extend(this, ApiMixin);
}
loadData() {
return this.makeRequest('/data');
}
}// Set up global event system using utilities
const GlobalEvents = _.extend({}, Backbone.Events);
// Service that uses global events
class NotificationService {
constructor() {
bindEvents(GlobalEvents, {
'user:login': 'onUserLogin',
'user:logout': 'onUserLogout',
'error': this.showError
});
}
onUserLogin(user) {
this.showNotification(`Welcome back, ${user.name}!`);
}
onUserLogout() {
this.showNotification('You have been logged out.');
}
showError(error) {
this.showNotification(`Error: ${error.message}`, 'error');
}
showNotification(message, type = 'info') {
console.log(`[${type.toUpperCase()}] ${message}`);
}
}
// Trigger global events from anywhere
GlobalEvents.trigger('user:login', { name: 'John Doe' });Additional utilities provided by Marionette for framework extension and development.
/**
* Backbone.extend utility for creating classes with inheritance
* @param protoProps - Prototype properties to add to the class
* @param staticProps - Static properties to add to the class constructor
* @returns Extended class constructor
*/
function extend(protoProps: object, staticProps?: object): Function;
/**
* Backbone.Events mixin for adding event capabilities to objects
*/
const Events: {
on(eventName: string, callback: Function): this;
off(eventName?: string, callback?: Function): this;
trigger(eventName: string, ...args: any[]): this;
once(eventName: string, callback: Function): this;
listenTo(other: any, eventName: string, callback: Function): this;
stopListening(other?: any, eventName?: string, callback?: Function): this;
};Usage Examples:
import { extend, Events } from "backbone.marionette";
// Create a custom class using extend
const MyClass = extend({
initialize() {
console.log('MyClass initialized');
},
doSomething() {
console.log('Doing something...');
}
}, {
// Static properties
VERSION: '1.0.0'
});
// Add events to any object
const eventedObject = _.extend({}, Events);
eventedObject.on('custom:event', () => {
console.log('Custom event triggered');
});
eventedObject.trigger('custom:event');