Skip to content

@xproperty Decorator

The @xproperty decorator is used to make properties in widget classes reactive. When properties marked with this decorator change, the DOM is automatically updated and template bindings are refreshed.

Basic Usage

typescript
import { Widget, xproperty } from '@xcons/widget';

@Widget({
  selector: '.my-widget',
  template: '<span x:text="message">Default message</span>'
})
export class MyWidget {
  @xproperty()
  message: string = 'Hello World!';
}

Syntax

Simple Property

typescript
@xproperty()
propertyName: type = defaultValue;

Configured Property

typescript
@xproperty({
  reflect: true,
  attribute: 'custom-attribute',
  hasChanged: (newVal, oldVal) => newVal !== oldVal
})
propertyName: type = defaultValue;

Property Options

typescript
interface PropertyOptions {
  reflect?: boolean;           // Reflect to HTML attribute
  attribute?: string;          // Custom attribute name
  hasChanged?: (value: any, oldValue: any) => boolean; // Change detection
}

Usage Examples

Simple Reactive Properties

Different Data Types

Property Options Usage

Usage with @xcomputed

Property Change Tracking

onWidgetPropertyChanged Lifecycle Hook

typescript
@Widget({
  selector: '.tracked-widget',
  template: `
    <div>
      <input type="text" x:attr:value="message">
      <p x:text="message">-</p>
    </div>
  `
})
export class TrackedWidget {
  @xproperty()
  message: string = 'Hello';
  
  onWidgetPropertyChanged(propertyKey?: string, oldValue?: any, newValue?: any): void {
    this.safeLog('info', `Property changed: ${propertyKey}`, {
      from: oldValue,
      to: newValue
    });
    
    // Custom operations
    if (propertyKey === 'message' && newValue.length > 50) {
      this.safeLog('warn', 'Message too long!');
    }
  }
}

Manual Update Triggering

typescript
import { triggerUpdate } from '@xcons/widget';

@Widget({
  selector: '.manual-update-widget',
  template: '<p x:text="data">-</p>'
})
export class ManualUpdateWidget {
  @xproperty()
  data: any[] = [];
  
  addItem(item: any): void {
    // Update data
    this.data.push(item);
    
    // Manually trigger reactive update
    triggerUpdate(this);
  }
}

Performance Tips

Effective Change Detection

typescript
// ✅ Good - Reference comparison for simple values
@xproperty({ 
  hasChanged: (newVal, oldVal) => newVal !== oldVal
})
simpleValue: string = '';

// ✅ Good - Deep comparison for complex objects
@xproperty({ 
  hasChanged: (newVal, oldVal) => 
    JSON.stringify(newVal) !== JSON.stringify(oldVal)
})
complexObject: any = {};

// ✅ Good - Threshold value check
@xproperty({ 
  hasChanged: (newVal: number, oldVal: number) => 
    Math.abs(newVal - oldVal) >= 0.01
})
preciseNumber: number = 0;

Batch Updates

Multiple property changes result in a single DOM update:

typescript
updateMultiple(): void {
  // All changes are made at once
  this.prop1 = 'value1';
  this.prop2 = 'value2';
  this.prop3 = 'value3';
  // DOM is updated only once
}

Nested Reactive Objects

Important Notes

✅ Do's

  • Use @xproperty to automatically reflect property changes to the DOM
  • Add @xproperty for all properties used in the template
  • Use @xcomputed for computed values
  • Use custom hasChanged function for performance

❌ Don'ts

  • Don't directly change properties and call triggerUpdate (unless necessary)
  • Don't write overly complex hasChanged functions
  • Don't make unnecessary properties reactive

Summary

With the @xproperty decorator:

  • ✅ Automatic reactivity - Automatic DOM updates on property changes
  • ✅ Template integration - Seamless work with template bindings
  • ✅ HTML attribute reflection - With reflect option
  • ✅ Customizable change detection - hasChanged function
  • ✅ Performance optimization - Batch updates and smart change detection
  • ✅ Type safety - TypeScript support

This decorator automates your widget's state management and UI updates.