Appearance
Widget Lifecycle Hooks
Widget lifecycle hooks are methods that are automatically called at different stages of the widget's lifecycle. They allow you to control widget behavior using the interface pattern.
Lifecycle Order
1. Constructor → Widget instance is created
2. OnWidgetInit → Widget initialization operations
3. OnWidgetReady → When template is ready
4. OnWidgetRendered → Rendered to DOM
5. OnWidgetPropertyChanged → Property changes (continuous)
6. OnWidgetDataUpdated → Data updates (continuous)
7. OnWidgetResize → Size changes (continuous)
8. OnWidgetEditModeChanged → Edit mode change (optional)
9. OnWidgetMobileModeChanged → Mobile mode change (optional)
10. OnWidgetDestroy → Widget cleanupBasic Lifecycle Hooks
OnWidgetInit
Called when the widget is being initialized. Used for initial setup operations.
typescript
import { Widget, xproperty } from '@xcons/widget';
import type { OnWidgetInit } from '@xcons/widget';
@Widget({
selector: '.init-widget',
template: `
<div>
<p x:text="message">Loading...</p>
<p>Data Count: <span x:text="dataCount">0</span></p>
</div>
`
})
export class InitWidget implements OnWidgetInit {
@xproperty()
message: string = '';
@xproperty()
data: any[] = [];
@xcomputed({ dependencies: ['data'] })
get dataCount(): number {
return this.data.length;
}
async onWidgetInit(): Promise<void> {
this.safeLog('info', 'Widget is initializing...');
// Initial data loading
await this.loadInitialData();
this.message = 'Widget ready!';
this.safeLog('info', 'Widget initialized');
}
private async loadInitialData(): Promise<void> {
return new Promise(resolve => {
setTimeout(() => {
this.data = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' }
];
resolve();
}, 1000);
});
}
}OnWidgetReady
Called when the template and resources are ready.
typescript
import type { OnWidgetReady } from '@xcons/widget';
@Widget({
selector: '.ready-widget',
template: `
<div>
<h3 x:text="title">Title</h3>
<p x:text="status">Status</p>
</div>
`
})
export class ReadyWidget implements OnWidgetReady {
@xproperty()
title: string = '';
@xproperty()
status: string = '';
onWidgetReady(templateReady: boolean): void {
this.safeLog('info', 'Widget ready', { templateReady });
if (templateReady) {
this.title = 'Widget Loaded';
this.status = 'Template loaded successfully';
// DOM element access is safe
this.setupDOMElements();
}
}
private setupDOMElements(): void {
// DOM element setup operations
this.safeLog('debug', 'DOM elements prepared');
}
}OnWidgetRendered
Called after the widget is rendered to the DOM.
typescript
import type { OnWidgetRendered } from '@xcons/widget';
@Widget({
selector: '.rendered-widget',
template: `
<div>
<p>Container: <span x:text="containerInfo">-</span></p>
<p>Rendered: <span x:text="isRendered ? 'Yes' : 'No'">-</span></p>
</div>
`
})
export class RenderedWidget implements OnWidgetRendered {
@xproperty()
containerInfo: string = '';
@xproperty()
isRendered: boolean = false;
onWidgetRendered(container: HTMLElement): void {
this.safeLog('info', 'Widget rendered');
this.isRendered = true;
this.containerInfo = `${container.tagName}#${container.id || 'no-id'}`;
// Container-specific operations
this.performPostRenderOperations(container);
}
private performPostRenderOperations(container: HTMLElement): void {
this.safeLog('debug', 'Post-render operations are being performed');
// Third-party library integrations, measurements, etc.
}
}OnWidgetDestroy
Called when the widget is being destroyed. Used for cleanup operations.
typescript
import type { OnWidgetDestroy } from '@xcons/widget';
@Widget({
selector: '.destroy-widget',
template: `
<div>
<p>Counter: <span x:text="counter">0</span></p>
</div>
`
})
export class DestroyWidget implements OnWidgetDestroy {
@xproperty()
counter: number = 0;
private intervalId?: number;
onWidgetInit(): void {
// Start timer
this.intervalId = window.setInterval(() => {
this.counter++;
}, 1000);
// Add event listener
document.addEventListener('click', this.handleClick);
}
onWidgetDestroy(): void {
this.safeLog('info', 'Widget is being cleaned up');
// Clear timer
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = undefined;
}
// Clear event listener
document.removeEventListener('click', this.handleClick);
this.safeLog('info', 'Widget cleaned up');
}
private handleClick = (): void => {
this.safeLog('debug', 'Click event');
};
}Event-Based Lifecycle Hooks
OnWidgetPropertyChanged
Called when a property changes.
typescript
import type { OnWidgetPropertyChanged } from '@xcons/widget';
@Widget({
selector: '.property-widget',
template: `
<div>
<p>Name: <span x:text="name">-</span></p>
<p>Age: <span x:text="age">-</span></p>
<p>Status: <span x:text="status">-</span></p>
<button x:on:click="updateInfo">Update Info</button>
</div>
`
})
export class PropertyWidget implements OnWidgetPropertyChanged {
@xproperty()
name: string = '';
@xproperty()
age: number = 0;
@xproperty()
status: string = '';
onWidgetPropertyChanged(propertyKey?: string, oldValue?: any, newValue?: any): void {
this.safeLog('debug', `Property changed: ${propertyKey}`, { oldValue, newValue });
// Specific property check
if (propertyKey === 'age') {
this.status = newValue >= 18 ? 'Adult' : 'Child';
}
if (propertyKey === 'name') {
this.status = newValue ? 'Valid' : 'Invalid';
}
}
updateInfo(): void {
this.name = 'Ahmet Yılmaz';
this.age = 25;
}
}OnWidgetDataUpdated
Called when data is updated.
typescript
import type { OnWidgetDataUpdated } from '@xcons/widget';
@Widget({
selector: '.data-widget',
template: `
<div>
<p>Last Updated: <span x:text="lastUpdated">-</span></p>
<p>Data Count: <span x:text="dataCount">0</span></p>
<button x:on:click="refreshData">Refresh</button>
</div>
`
})
export class DataWidget implements OnWidgetDataUpdated {
@xproperty()
data: any[] = [];
@xproperty()
lastUpdated: string = '';
@xcomputed({ dependencies: ['data'] })
get dataCount(): number {
return this.data.length;
}
onWidgetDataUpdated(): void {
this.safeLog('info', 'Data updated', { count: this.data.length });
this.lastUpdated = new Date().toLocaleString('en-US');
// Data processing
this.processData();
}
private processData(): void {
this.safeLog('debug', 'Data is being processed...');
}
refreshData(): void {
this.data = [
{ id: 1, value: Math.random() },
{ id: 2, value: Math.random() }
];
this.onWidgetDataUpdated();
}
}OnWidgetResize
Called when the widget size changes.
typescript
import type { OnWidgetResize } from '@xcons/widget';
@Widget({
selector: '.resize-widget',
template: `
<div>
<p>Width: <span x:text="currentWidth">-</span>px</p>
<p>Height: <span x:text="currentHeight">-</span>px</p>
<p x:class:compact="isCompact">Layout: <span x:text="layoutMode">-</span></p>
</div>
`
})
export class ResizeWidget implements OnWidgetResize {
@xproperty()
currentWidth: number = 0;
@xproperty()
currentHeight: number = 0;
@xcomputed({ dependencies: ['currentWidth'] })
get isCompact(): boolean {
return this.currentWidth < 400;
}
@xcomputed({ dependencies: ['currentWidth'] })
get layoutMode(): string {
return this.currentWidth < 400 ? 'Compact' : 'Normal';
}
onWidgetResize(): void {
if (this.xctx) {
this.currentWidth = this.xctx.width || 0;
this.currentHeight = this.xctx.height || 0;
this.safeLog('debug', 'Widget size changed', {
width: this.currentWidth,
height: this.currentHeight
});
}
}
}OnWidgetEditModeChanged
Called when edit mode changes.
typescript
import type { OnWidgetEditModeChanged } from '@xcons/widget';
@Widget({
selector: '.edit-mode-widget',
template: `
<div>
<p>Mode: <span x:text="currentMode">-</span></p>
<div x:class:visible="isEditMode" class="edit-controls">
<button>Save</button>
<button>Cancel</button>
</div>
</div>
`
})
export class EditModeWidget implements OnWidgetEditModeChanged {
@xproperty()
currentMode: string = 'View';
@xproperty()
isEditMode: boolean = false;
onWidgetEditModeChanged(): void {
this.isEditMode = this.xctx?.isEdit || false;
this.currentMode = this.isEditMode ? 'Edit' : 'View';
this.safeLog('info', `Edit mode: ${this.currentMode}`);
}
}OnWidgetMobileModeChanged
Called when mobile mode changes.
typescript
import type { OnWidgetMobileModeChanged } from '@xcons/widget';
@Widget({
selector: '.mobile-mode-widget',
template: `
<div x:class:mobile="isMobileMode">
<p>Device: <span x:text="deviceType">-</span></p>
</div>
`
})
export class MobileModeWidget implements OnWidgetMobileModeChanged {
@xproperty()
deviceType: string = 'Desktop';
@xproperty()
isMobileMode: boolean = false;
onWidgetMobileModeChanged(): void {
this.isMobileMode = this.xctx?.isMobile || false;
this.deviceType = this.isMobileMode ? 'Mobile' : 'Desktop';
this.safeLog('info', `Mobile mode: ${this.isMobileMode}`);
}
}Error Handling
OnWidgetInitializationError
Called when an error occurs during initialization.
typescript
import type { OnWidgetInitializationError } from '@xcons/widget';
@Widget({
selector: '.error-widget',
template: `
<div>
<p x:text="errorMessage">Loading...</p>
</div>
`
})
export class ErrorWidget implements OnWidgetInitializationError {
@xproperty()
errorMessage: string = '';
async onWidgetInit(): Promise<void> {
// Error simulation
throw new Error('Initialization failed!');
}
onWidgetInitializationError(error: any): void {
this.safeLog('error', 'Widget could not be initialized', error);
this.errorMessage = `Error: ${error.message}`;
// Recovery attempt
this.attemptRecovery();
}
private attemptRecovery(): void {
setTimeout(() => {
this.errorMessage = 'Recovered from error';
}, 2000);
}
}Complete Lifecycle Example
A comprehensive example using all hooks together:
```Best Practices
1. Hook Ordering
typescript
// Organize according to lifecycle order
export class MyWidget implements
OnWidgetInit,
OnWidgetReady,
OnWidgetRendered,
OnWidgetDestroy {
onWidgetInit() { }
onWidgetReady() { }
onWidgetRendered() { }
onWidgetDestroy() { }
}2. Async Operations
typescript
async onWidgetInit(): Promise<void> {
// Use promises
await this.loadData();
await this.setupConfig();
}3. Cleanup
typescript
onWidgetDestroy(): void {
// Always perform cleanup
if (this.subscription) {
this.subscription.unsubscribe();
}
if (this.timer) {
clearInterval(this.timer);
}
}4. Error Handling
typescript
async onWidgetInit(): Promise<void> {
try {
await this.riskyOperation();
} catch (error) {
this.safeLog('error', 'Init error:', error);
// Fallback logic
}
}Summary
Widget Lifecycle Hooks features:
✅ Lifecycle Control - Manage widget lifecycle
✅ Organized Structure - Organize initialization logic
✅ Resource Management - Manage resources properly
✅ Property Tracking - Track property changes
✅ DOM Optimization - Optimize DOM interactions
✅ Error Handling - Centralize error management
✅ Memory Safety - Prevent memory leaks
The interface pattern makes widget development more organized and maintainable.