Model

Model - A flexible data model with schema validation, computed properties, and event system

Features:

  • Schema-based validation with type coercion and defaults
  • Nested property access using dotted paths (e.g., 'user.profile.name')
  • Computed properties that update automatically
  • Event system for change notifications
  • Deep merging and batch operations
  • JSON serialization support

Constructor

new Model()

Examples
// Basic usage
const user = new Model({ name: 'John', age: 30 });
user.name = 'Jane';
console.log(user.getValues()); // { name: 'Jane', age: 30 }
// With schema validation
class UserModel extends Model {
  static schema = {
    name: { type: 'string', required: true },
    age: { type: 'number', default: 18 },
    email: { type: 'string', transform: v => v?.toLowerCase() },
    status: { type: 'string', enum: ['active', 'inactive'], default: 'active' }
  };
}

const user = new UserModel({ name: 'John', email: 'JOHN@TEST.COM' });
console.log(user.email); // 'john@test.com' (transformed)
console.log(user.age);   // 18 (default value)
// Nested properties with dotted paths
class ProfileModel extends Model {
  static schema = {
    'user.name': { type: 'string', required: true },
    'user.email': { type: 'string', default: 'noemail@test.com' },
    'settings.theme': { type: 'string', default: 'light' }
  };
}

const profile = new ProfileModel({ 'user.name': 'Alice' });
console.log(profile.get('user.name'));      // 'Alice'
console.log(profile.get('settings.theme')); // 'light'
// Computed properties
class PersonModel extends Model {
  static computed = {
    fullName: (model) => `${model.firstName || ''} ${model.lastName || ''}`.trim(),
    isAdult: (model) => (model.age || 0) >= 18
  };
}

const person = new PersonModel({ firstName: 'John', lastName: 'Doe', age: 25 });
console.log(person.fullName); // 'John Doe'
console.log(person.isAdult);  // true
// Event handling
const model = new Model({ count: 0 });
model.on('change:count', ({ from, to }) => {
  console.log(`Count changed from ${from} to ${to}`);
});
model.count = 5; // Triggers event
// Batch operations and merging
const model = new Model({ user: { name: 'John', age: 30 } });

// Deep merge - preserves existing nested properties
model.mergeValues({ user: { email: 'john@test.com' } });
console.log(model.get('user.name')); // 'John' (preserved)
console.log(model.get('user.email')); // 'john@test.com' (added)

// Batch replace
model.setValues({ user: { name: 'Jane' } }); // Replaces entire user object
console.log(model.get('user.age')); // undefined (lost)
// Factory method
const model = Model.fromJSON({ name: 'John', nested: { value: 42 } });
const json = model.toJSON(); // Deep cloned plain object

Methods

mergeValues()

Deep-merge values into model (object nodes are merged, not replaced). Arrays: configurable strategy: 'replace' (default) | 'concat' | 'byIndex'

setValues()

Batch update values (overwrites object nodes).