Base object classes and application management for organizing global application state, Radio communication, and top-level view management.
The base object class that provides common functionality including lifecycle management, event handling, and Backbone.Radio integration.
/**
* Base object class with common functionality and Radio integration
* @param options - Configuration options for the object
*/
const MnObject: {
new (options?: MnObjectOptions): MnObject;
extend(properties: object, classProperties?: object): typeof MnObject;
};
interface MnObject {
/** Initialize the object (noop method for overriding) */
initialize(): void;
/** Check if the object has been destroyed */
isDestroyed(): boolean;
/** Destroy the object and clean up resources */
destroy(options?: DestroyOptions): this;
/** Get the Radio channel for this object */
getChannel(): RadioChannel;
/** Unique identifier for the object */
cid: string;
}
interface MnObjectOptions {
/** Channel name for Backbone.Radio */
channelName?: string;
/** Hash of Radio event handlers */
radioEvents?: RadioEventsHash;
/** Hash of Radio request handlers */
radioRequests?: RadioRequestsHash;
}Usage Examples:
import { MnObject } from "backbone.marionette";
// Basic object
class MyService extends MnObject {
initialize() {
console.log('Service initialized');
}
doSomething() {
console.log('Doing something...');
}
onDestroy() {
console.log('Service destroyed');
}
}
// Object with Radio integration
class DataService extends MnObject {
channelName: 'data'
radioRequests() {
return {
'fetch:user': 'fetchUser',
'save:user': 'saveUser'
};
}
radioEvents() {
return {
'user:updated': 'onUserUpdated'
};
}
fetchUser(id) {
// Fetch user logic
return { id, name: 'John Doe' };
}
saveUser(userData) {
// Save user logic
console.log('Saving user:', userData);
}
onUserUpdated(user) {
console.log('User updated:', user);
}
}
// Usage
const service = new DataService();
const channel = service.getChannel();
// Make requests
const user = channel.request('fetch:user', 123);
channel.request('save:user', { id: 123, name: 'Jane Doe' });
// Trigger events
channel.trigger('user:updated', user);The main application class that extends MnObject and provides global application management, including a root region for displaying views.
/**
* Main application class for global state and view management
* @param options - Configuration options for the application
*/
class Application extends MnObject {
constructor(options?: ApplicationOptions): Application;
/** Start the application with options */
start(options?: object): this;
/** Get the application's root region */
getRegion(): Region;
/** Show a view in the application's root region */
showView(view: View, ...args: any[]): View;
/** Get the current view from the application's root region */
getView(): View | undefined;
}
interface ApplicationOptions extends MnObjectOptions {
/** Region definition for the application's root region */
region?: string | RegionDefinition;
/** Region class to use for the root region */
regionClass?: typeof Region;
}Usage Examples:
import { Application, View } from "backbone.marionette";
// Basic application
class MyApplication extends Application {
region: '#app'
onStart(options) {
console.log('Application started with options:', options);
// Show initial view
const homeView = new HomeView();
this.showView(homeView);
}
}
// Application with Radio integration
class ChatApplication extends Application {
region: '#chat-app'
channelName: 'chat'
radioRequests() {
return {
'show:conversation': 'showConversation',
'show:contacts': 'showContacts'
};
}
radioEvents() {
return {
'user:login': 'onUserLogin',
'user:logout': 'onUserLogout'
};
}
onStart() {
// Initialize application
this.showView(new LoginView());
}
showConversation(conversationId) {
const conversationView = new ConversationView({
model: new Conversation({ id: conversationId })
});
this.showView(conversationView);
}
showContacts() {
const contactsView = new ContactsView();
this.showView(contactsView);
}
onUserLogin(user) {
console.log('User logged in:', user);
this.showView(new DashboardView({ model: user }));
}
onUserLogout() {
console.log('User logged out');
this.showView(new LoginView());
}
}
// Start the application
const app = new ChatApplication();
app.start({ environment: 'production' });
// Navigate using Radio
const chatChannel = app.getChannel();
chatChannel.request('show:conversation', 'conv-123');Methods for managing object lifecycle and state.
/**
* Initialize the object (called automatically by constructor)
* Override this method to add custom initialization logic
*/
initialize(): void;
/**
* Check if the object has been destroyed
* @returns True if object is destroyed
*/
isDestroyed(): boolean;
/**
* Destroy the object and clean up all resources
* @param options - Destroy options
* @returns The object instance for chaining
*/
destroy(options?: DestroyOptions): this;Usage Examples:
// Custom initialization
class ConfiguredService extends MnObject {
initialize(options = {}) {
this.config = _.defaults(options.config || {}, {
timeout: 5000,
retries: 3
});
this.setupEventHandlers();
}
setupEventHandlers() {
// Setup custom event handlers
}
onDestroy() {
// Custom cleanup
this.config = null;
}
}
// Usage
const service = new ConfiguredService({
config: { timeout: 10000 }
});
// Later...
service.destroy(); // Triggers cleanup
console.log(service.isDestroyed()); // trueMethods for integrating with Backbone.Radio for decoupled communication.
/**
* Get the Radio channel for this object
* @returns Backbone.Radio channel instance
*/
getChannel(): RadioChannel;Usage Examples:
// Service communication
class UserService extends MnObject {
channelName: 'users'
radioRequests() {
return {
'current:user': 'getCurrentUser',
'login': 'loginUser',
'logout': 'logoutUser'
};
}
getCurrentUser() {
return this.currentUser;
}
loginUser(credentials) {
// Login logic
this.currentUser = { id: 1, name: 'John' };
this.getChannel().trigger('user:logged:in', this.currentUser);
return this.currentUser;
}
logoutUser() {
const user = this.currentUser;
this.currentUser = null;
this.getChannel().trigger('user:logged:out', user);
}
}
// Another service listening to user events
class NotificationService extends MnObject {
channelName: 'users'
radioEvents() {
return {
'user:logged:in': 'onUserLogin',
'user:logged:out': 'onUserLogout'
};
}
onUserLogin(user) {
this.showWelcomeMessage(user);
}
onUserLogout(user) {
this.showGoodbyeMessage(user);
}
}
// Usage across the application
const userChannel = Radio.channel('users');
const user = userChannel.request('current:user');
userChannel.request('login', { username: 'john', password: '123' });/** Unique identifier prefix for objects */
cidPrefix: 'mno'; // for MnObject
/** Unique identifier prefix for applications */
cidPrefix: 'mna'; // for Application
/** Default region class for applications */
regionClass: typeof Region; // Application only
/** Unique identifier for the object instance */
cid: string;Objects trigger events during their lifecycle:
// Destroy lifecycle
object.on('before:destroy', (object, options) => { /* ... */ });
object.on('destroy', (object, options) => { /* ... */ });
// Application start lifecycle
app.on('before:start', (app, options) => { /* ... */ });
app.on('start', (app, options) => { /* ... */ });Usage Examples:
// Global lifecycle monitoring
const app = new MyApplication();
app.on('before:start', (app, options) => {
console.log('Application starting...');
// Setup analytics, logging, etc.
});
app.on('start', (app, options) => {
console.log('Application started!');
// Application is ready
});
// Object cleanup monitoring
const service = new MyService();
service.on('destroy', (service) => {
console.log('Service destroyed');
// Notify other parts of the system
});// Base service class
class BaseService extends MnObject {
channelName: 'services'
radioRequests() {
return {
[`${this.serviceName}:start`]: 'start',
[`${this.serviceName}:stop`]: 'stop',
[`${this.serviceName}:status`]: 'getStatus'
};
}
start() {
this.isRunning = true;
this.getChannel().trigger(`${this.serviceName}:started`, this);
}
stop() {
this.isRunning = false;
this.getChannel().trigger(`${this.serviceName}:stopped`, this);
}
getStatus() {
return {
name: this.serviceName,
running: this.isRunning
};
}
}
// Specific services
class EmailService extends BaseService {
serviceName: 'email'
sendEmail(to, subject, body) {
// Email sending logic
}
}
class LoggingService extends BaseService {
serviceName: 'logging'
log(level, message) {
// Logging logic
}
}class ServiceManagedApplication extends Application {
initialize() {
this.services = new Map();
this.initializeServices();
}
initializeServices() {
// Register services
this.registerService('email', new EmailService());
this.registerService('logging', new LoggingService());
}
registerService(name, service) {
this.services.set(name, service);
// Start service
const channel = Radio.channel('services');
channel.request(`${name}:start`);
}
onStart() {
// All services started, show main view
this.showView(new MainView());
}
onDestroy() {
// Stop all services
for (const [name, service] of this.services) {
service.destroy();
}
this.services.clear();
}
}interface RadioEventsHash {
[eventName: string]: string | ((...args: any[]) => any);
}
interface RadioRequestsHash {
[requestName: string]: string | ((...args: any[]) => any);
}
interface RadioChannel {
request(requestName: string, ...args: any[]): any;
trigger(eventName: string, ...args: any[]): void;
on(eventName: string, callback: (...args: any[]) => any): void;
off(eventName?: string, callback?: (...args: any[]) => any): void;
stopListening(): void;
}
interface RegionDefinition {
el: string;
regionClass?: typeof Region;
replaceElement?: boolean;
}