Dot Notation with Models
Working with nested data in Domma Models using dot notation for forms

Using Dot Notation in Forms

While Domma Models store flat key-value pairs, you can work with nested data structures by combining Models with utility functions. This enables dot notation in forms through _.get() and _.set() utilities.

Key Concept: Store nested objects as model values, then use utility functions and custom formatters/parsers to handle dot notation paths when binding to forms.

Example 1: Basic Nested Data Access

Storing and Accessing Nested Data

// Create model with nested object values
const userModel = M.create({
    profile: { type: 'object', default: {} },
    address: { type: 'object', default: {} }
});

// Set nested data
userModel.set('profile', {
    firstName: 'John',
    lastName: 'Doe',
    bio: 'Software developer'
});

userModel.set('address', {
    street: '123 Main St',
    city: 'New York',
    zip: '10001'
});

// Access nested properties using utilities
const city = _.get(userModel.get('address'), 'city');
console.log('City:', city);  // 'New York'

// Update nested property
const address = userModel.get('address');
_.set(address, 'city', 'Boston');
userModel.set('address', address);  // Trigger change events

Try It:

Model State:

Click "Initialize Model" to start

Example 2: Form Binding with Dot Notation

Helper Function for Nested Binding

// Helper function for binding nested properties
function bindNested(model, field, path, selector, options = {}) {
    M.bind(model, field, selector, {
        ...options,
        format: (data) => _.get(data, path, ''),
        parse: (value) => {
            const data = _.cloneDeep(model.get(field) || {});
            _.set(data, path, value);
            return data;
        }
    });
}

// Create form model
const formModel = M.create({
    userData: { type: 'object', default: {} }
});

// Bind form inputs with dot notation paths
bindNested(formModel, 'userData', 'user.name', '#user-name', { twoWay: true });
bindNested(formModel, 'userData', 'user.email', '#user-email', { twoWay: true });
bindNested(formModel, 'userData', 'address.city', '#address-city', { twoWay: true });

Form Inputs:

Live Model Data:

{}

Example 3: Complete Registration Form

Full Form with Validation

Serialized Form Data (with dot notation):

Submit the form to see the data structure

Example 4: Form Auto-Serialization

// Utility to serialize form with dot notation support
function serializeFormWithDotNotation(formSelector) {
    const data = {};

    $(formSelector).find('input, select, textarea').each(function() {
        const element = $(this);
        const name = element.attr('name');
        if (!name) return;

        let value;
        if (element.attr('type') === 'checkbox') {
            value = element.prop('checked');
        } else if (element.attr('type') === 'radio') {
            if (element.prop('checked')) {
                value = element.val();
            } else {
                return; // Skip unchecked radio
            }
        } else {
            value = element.val();
        }

        // Use _.set to handle dot notation
        _.set(data, name, value);
    });

    return data;
}

// Usage
const formData = serializeFormWithDotNotation('#my-form');
// Returns nested object: { user: { name: '...', email: '...' }, address: { ... } }
Pro Tip: You can use HTML5 data attributes or name attributes with dot notation to automatically structure your form data. The serialization function will parse these paths and create the appropriate nested object structure.

Best Practices

  • Use consistent naming: Stick to a naming convention like section.field throughout your forms
  • Validate nested data: Use custom validation functions that can check nested properties with _.get()
  • Create reusable helpers: Build utility functions like bindNested() for common patterns
  • Leverage persistence: Models with nested data can still use the persist option for localStorage
  • Handle undefined paths: Always provide default values when using _.get() to avoid errors
  • Trigger change events: After modifying nested data, always set() the parent object to trigger model change events