Skip to content

Template Modification System

Basic Concept

Using the API call this.api.getDocumentModifier(), you can access the TemplateModifier object.

The TemplateModifier provides a controlled way to modify templates:

javascript
this.api.getDocumentModifier()
    .modifyHtml(immutableNode)     // Select what to modify
    .setInnerHtml('New content')   // Define the modification
    .apply(description);           // Apply with tracking

Modification Flow

1. Get Modifier          2. Select Node           3. Chain Modifications
      ↓                        ↓                          ↓
getDocumentModifier() → modifyHtml(node) → setAttribute().setStyle()...

                              5. Sync                4. Apply
                                 ↓                        ↓
                        Propagate to Users ← apply(ModificationDescription)

This flow ensures that all modifications are:

  • Tracked - Each change is recorded for version history
  • Atomic - Related changes are applied together
  • Collaborative - Changes are synchronized across all users
  • Reversible - Full undo/redo support is maintained

Modification Types

HTML Modifications

The system supports various HTML modifications:

Content Modifications

javascript
.setInnerHtml(html)     // Replace inner HTML
.append(html)           // Add to end
.prepend(html)          // Add to beginning
.replaceWith(html)      // Replace entire element

Attribute Modifications

javascript
.setAttribute(name, value)    // Set attribute
.removeAttribute(name)        // Remove attribute

Text Modifications

javascript
.setText(text)      // Updates the text content of the HTML text node

Style Modifications

javascript
.setStyle(property, value)    // Set CSS property
.removeStyle(property)        // Remove CSS property

Class Modifications

javascript
.setClass(className)          // Add CSS class
.removeClass(className)       // Remove CSS class

Structural Modifications

javascript
.delete()                     // Remove current node

CSS Modifications

The system also supports CSS modifications:

javascript
this.api.getDocumentModifier()
    .modifyCss(immutableCssNode)
    .setProperty('color', 'blue')        // Set a CSS property
    .setProperty('font-size', '16px')    // Set a CSS property
    .removeProperty('text-decoration')   // Remove a CSS property
    .apply(description);

MultiRowStructureModifier

Overview

The MultiRowStructureModifier is a specialized helper interface for creating and modifying complex email layouts with multiple rows and columns. It provides high-level methods to manage email structure containers, handling the intricate details of email-compatible HTML generation.

When to Use MultiRowStructureModifier

Use the MultiRowStructureModifier when you need to:

  • Create multi-row custom blocks - Build responsive custom blocks with multiple structure rows
  • Modify existing structures - Change the layout while preserving content
  • Manage container distribution - Control how content is distributed across containers within the structure

Why Use MultiRowStructureModifier

Traditional email HTML is complex due to:

  1. Table-based layouts - Email clients require nested tables for consistent rendering
  2. MSO compatibility - Outlook needs special conditional comments and markup
  3. Responsive challenges - Mobile and desktop layouts require different approaches

The MultiRowStructureModifier abstracts these complexities by providing:

  • Automatic table structure generation - No need to manually create complex nested tables
  • Built-in MSO/Outlook compatibility - Handles all necessary conditional comments and markup
  • Responsive design handling - Automatically generates mobile-friendly layouts
  • Smart content distribution - Intelligently places content across containers
  • Image scaling and optimization - Ensures images render correctly across email clients

Access and Usage

Access the MultiRowStructureModifier through the multiRowStructureModifier() method:

javascript
const modifier = this.api.getDocumentModifier()
    .modifyHtml(structureNode)
    .multiRowStructureModifier();

Methods

updateLayoutWithContent

Creates a new structure by replacing the current one with specified containers and content.

javascript
// Create a three-column layout with mixed container types
modifier.multiRowStructureModifier()
    .updateLayoutWithContent(
        [
            {width: '25%', contentType: 'EMPTY'},  // Empty placeholder
            '50%',                                 // Content column 1
            '25%'                                  // Content column 2
        ],
        [
            `<${BlockType.BLOCK_TEXT}>
                <p>Content 1</p>
            </${BlockType.BLOCK_TEXT}>`,
            `<${BlockType.BLOCK_TEXT}>
                <p>Content 2</p>
            </${BlockType.BLOCK_TEXT}>`
        ]
    )
    .apply(new ModificationDescription('Created three-column layout'));

Parameters:

  • layout: Array of StructureLayout defining container widths and types
  • containerContent: Array of HTML strings for content containers

Container Types:

  • Content Container (string) - Width percentage for content-holding containers
  • Empty Container - {width: string, contentType: 'EMPTY'} for placeholders awaiting future content
  • Spacer Container - {width: string, contentType: 'SPACER'} for creating empty space

updateLayout

Modifies the container layout of an existing structure while preserving content.

javascript
// Change from two columns to three columns
modifier.multiRowStructureModifier()
    .updateLayout([
        '33%',     // First column
        '34%',     // Second column
        '33%'      // Third column
    ])
    .apply(new ModificationDescription('Changed to three-column layout'));

Parameters:

  • layout: New container layout configuration

Behavior:

  • Preserves existing content when possible during layout changes
  • Redistributes content intelligently among new containers
  • Manages content overflow by automatically creating additional structure beneath the current one when needed

Best Practices

  1. Plan Your Layout - Design your container structure before implementation
  2. Use Meaningful Widths - Ensure widths add up to 100% for proper rendering
  3. Test Content Distribution - Verify content appears in the correct containers
  4. Leverage Spacers - Use spacer containers for consistent margins and padding

Modification Descriptions

Purpose

Every modification requires a description that:

  • Documents the change for comprehensive version history
  • Provides context for other users in collaborative editing
  • Enables meaningful labels for undo/redo operations
  • Supports internationalization for multi-language environments

Basic Usage

javascript
.apply(new ModificationDescription('Changed text color'));

With Parameters

javascript
.apply(new ModificationDescription('Changed color to {color}')
    .withParams({ color: '#ff0000' }));

Internationalization

javascript
.apply(new ModificationDescription('color_changed')
    .withParams({ color: '#ff0000' }));
// Uses translation key 'color_changed'

Transaction Model

Atomic Operations

All modifications in a single chain are atomic:

javascript
this.api.getDocumentModifier()
    .modifyHtml(container)
    .setStyle('background', 'blue')          // All three
    .setClass('highlighted')                 // happen
    .setAttribute('data-modified', 'true')   // together
    .apply(description);

Benefits of Transactions

  1. Consistency - All related changes succeed together or fail as a unit
  2. Performance - Batch processing reduces overhead and improves responsiveness
  3. History - Single undo/redo operation for logically related changes
  4. Synchronization - Fewer network requests improve collaborative editing performance

Collaborative Editing Support

How It Works

When a modification is applied:

  1. Local Application: Changes apply immediately locally
  2. Serialization: Modifications are serialized to operations
  3. Transmission: Operations sent to collaboration server
  4. Transformation: Server resolves conflicts if needed
  5. Broadcast: Changes sent to all other users
  6. Application: Remote changes applied to all clients

Conflict Resolution

The system automatically handles conflicts:

User A: Changes heading color to red
User B: Changes heading color to blue (simultaneously)

Result: Last write wins (User B's change)
Both users see: Heading is blue
History shows: Both changes with timestamps

Complex conflicts are resolved using operational transformation:

User A: Inserts text at position 10
User B: Deletes text at position 5-8

System transforms User A's operation:
Original: Insert at position 10
Transformed: Insert at position 7 (adjusted for deletion)

Version History Integration

Automatic Tracking

Every modification is automatically tracked:

javascript
// This single modification creates a history entry
this.api.getDocumentModifier()
    .modifyHtml(element)
    .setInnerHtml('Updated content')
    .apply(new ModificationDescription('Updated welcome message'));

// Version history shows:
// - Timestamp
// - "Updated welcome message"
// - Username
// - Restore option

Undo/Redo Support

The modification system provides built-in undo/redo:

javascript
// User performs modification
modifier.apply(description);

// User clicks undo
// System automatically reverses the modification

// User clicks redo
// System reapplies the modification

Performance Considerations

Batching Strategies

Batch related modifications for better performance:

javascript
// Good - Single transaction
const modifier = this.api.getDocumentModifier();
elements.forEach(el => {
    modifier.modifyHtml(el).setStyle('color', 'blue');
});
modifier.apply(description);

// Avoid - Multiple transactions
elements.forEach(el => {
    this.api.getDocumentModifier()
        .modifyHtml(el)
        .setStyle('color', 'blue')
        .apply(description);
});

Debouncing

For high-frequency updates (like color pickers):

javascript
let modificationTimeout;

onColorChange(color) {
    clearTimeout(modificationTimeout);
    modificationTimeout = setTimeout(() => {
        this.applyColorChange(color);
    }, 100);
}

Best Practices

1. Use Descriptive Messages

javascript
// Good - specific and informative
.apply(new ModificationDescription('Changed button color to match brand'));

// Avoid - too vague
.apply(new ModificationDescription('Updated'));
javascript
// Good - related changes in a single transaction
modifier
    .modifyHtml(button)
    .setStyle('background', color)
    .setStyle('border-color', darkenColor(color))
    .apply(description);

// Avoid - unrelated changes in the same transaction
modifier
    .modifyHtml(button)
    .setStyle('background', color)
    .modifyHtml(heading)  // Different element - should be separate
    .setInnerHtml(title)
    .apply(description);

3. Minimize Unnecessary Modifications

javascript
// Good - only modify when values actually change
if (newValue !== oldValue) {
    modifier.modifyHtml(element)
        .setAttribute('data-value', newValue)
        .apply(description);
}

// Avoid - modifying even when values haven't changed
modifier.modifyHtml(element)
    .setAttribute('data-value', newValue)  // Wasteful if unchanged
    .apply(description);

4. Use Appropriate Methods for Each Task

javascript
// Good - use specialized methods for their intended purpose
modifier.modifyHtml(element)
    .setDisplayCondition(condition)     // For visibility control
    .setNodeConfig(widgetConfig)        // For custom data storage
    .apply(description);

// Avoid - misusing generic attributes for specialized functionality
modifier.modifyHtml(element)
    .setAttribute('data-config', JSON.stringify(config))  // Use setNodeConfig instead
    .apply(description);

Summary

The template modification system provides:

  • Safety - Immutable nodes prevent direct manipulation and ensure data integrity
  • Tracking - All changes are recorded and attributed for complete audit trails
  • Collaboration - Automatic conflict resolution and real-time synchronization
  • History - Built-in undo/redo functionality with comprehensive version tracking
  • Performance - Batched operations and optimized synchronization for scalability
  • Advanced Features - Display conditions and node configurations for complex use cases
  • Layout Management - MultiRowStructureModifier for sophisticated email structures

This system is fundamental to creating reliable, collaborative extensions that maintain template integrity while providing powerful modification capabilities.

See Also