Dynamic Elements
Data Binding

Data Binding

Data binding is the process of connecting your template elements to data sources in Canvelete (opens in a new tab), enabling automatic updates when data changes. Canvelete provides powerful data binding capabilities that make it easy to create responsive, data-driven designs.

Understanding Data Binding

Data binding creates a live connection between your template elements and data sources. When the underlying data changes, the visual elements automatically update to reflect those changes.

Types of Data Binding

One-Way Binding

Data flows from the source to the template element:

// Simple one-way binding
"{{user.name}}" // Displays the user's name
 
// With default values
"{{user.name || 'Guest User'}}" // Shows 'Guest User' if name is empty

Two-Way Binding

Data flows in both directions (for interactive elements):

// Input field with two-way binding
{
  "type": "input",
  "value": "{{user.email}}",
  "onChange": "updateUser.email"
}

Computed Binding

Derived values calculated from other data:

// Computed full name
"{{user.firstName + ' ' + user.lastName}}"
 
// Computed discount percentage
"{{((originalPrice - salePrice) / originalPrice * 100).toFixed(1)}}%"

Data Source Configuration

JSON Data Sources

Connect to static or dynamic JSON data:

// Static JSON data
{
  "dataSource": {
    "type": "json",
    "data": {
      "products": [
        {"id": 1, "name": "Product A", "price": 29.99},
        {"id": 2, "name": "Product B", "price": 39.99}
      ]
    }
  }
}

API Data Sources

Connect to REST APIs for live data:

// API configuration
{
  "dataSource": {
    "type": "api",
    "url": "https://api.example.com/products",
    "method": "GET",
    "headers": {
      "Authorization": "Bearer {{apiToken}}",
      "Content-Type": "application/json"
    },
    "refreshInterval": 300000 // 5 minutes
  }
}

Database Connections

Connect directly to databases:

// Database configuration
{
  "dataSource": {
    "type": "database",
    "connection": "postgresql://user:pass@host:5432/db",
    "query": "SELECT * FROM products WHERE category = $1",
    "parameters": ["{{selectedCategory}}"]
  }
}

CSV/Spreadsheet Data

Import data from CSV files or spreadsheets:

// CSV data source
{
  "dataSource": {
    "type": "csv",
    "url": "https://example.com/data.csv",
    "delimiter": ",",
    "headers": true,
    "mapping": {
      "productName": "Product Name",
      "productPrice": "Price (USD)"
    }
  }
}

Binding Syntax

Basic Variable Binding

// Simple property access
"{{user.name}}"
"{{product.price}}"
"{{order.items[0].quantity}}"
 
// Nested object access
"{{user.profile.address.city}}"
"{{company.departments[0].manager.name}}"

Array and Object Iteration

// Iterate through arrays
"{{#each products}}
  <div class='product'>
    <h3>{{name}}</h3>
    <p>${{price}}</p>
  </div>
{{/each}}"
 
// Iterate with index
"{{#each users as |user index|}}
  {{index + 1}}. {{user.name}}
{{/each}}"
 
// Iterate through object properties
"{{#each user}}
  {{@key}}: {{this}}
{{/each}}"

Conditional Binding

// Simple conditions
"{{#if user.isActive}}
  Active User
{{else}}
  Inactive User
{{/if}}"
 
// Multiple conditions
"{{#if user.role === 'admin'}}
  Administrator
{{else if user.role === 'moderator'}}
  Moderator
{{else}}
  Regular User
{{/if}}"
 
// Inline conditions
"{{user.isActive ? 'Active' : 'Inactive'}}"

Data Filters and Transformations

Text Filters

// Case transformations
"{{user.name | uppercase}}" // JOHN DOE
"{{user.name | lowercase}}" // john doe
"{{user.name | titlecase}}" // John Doe
"{{user.name | camelcase}}" // johnDoe
 
// Text manipulation
"{{description | truncate:100}}" // Limit to 100 characters
"{{content | stripHtml}}" // Remove HTML tags
"{{text | slugify}}" // Convert to URL-friendly slug

Number Filters

// Formatting
"{{price | currency:'USD'}}" // $29.99
"{{percentage | percent:2}}" // 15.25%
"{{fileSize | bytes}}" // 1.2 MB
 
// Mathematical operations
"{{value | round:2}}" // Round to 2 decimal places
"{{number | abs}}" // Absolute value
"{{price | multiply:1.08}}" // Add 8% tax

Date Filters

// Date formatting
"{{createdAt | date:'YYYY-MM-DD'}}" // 2024-01-15
"{{updatedAt | date:'MMM DD, YYYY'}}" // Jan 15, 2024
"{{timestamp | time:'HH:mm'}}" // 14:30
 
// Relative dates
"{{createdAt | timeAgo}}" // 2 hours ago
"{{dueDate | timeUntil}}" // in 3 days

Array Filters

// Array operations
"{{products | length}}" // Number of items
"{{prices | sum}}" // Total of all prices
"{{ratings | average}}" // Average rating
"{{products | first}}" // First item
"{{products | last}}" // Last item
 
// Filtering arrays
"{{products | where:'category','Electronics'}}"
"{{users | where:'isActive',true}}"
"{{items | sortBy:'price'}}"

Advanced Binding Techniques

Custom Filters

Create reusable custom filters:

// Register custom filter
Canvelete.registerFilter('formatPhone', function(phone) {
  return phone.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
});
 
// Use custom filter
"{{user.phone | formatPhone}}" // (555) 123-4567

Computed Properties

Define calculated values:

// Computed properties configuration
{
  "computed": {
    "fullName": "{{user.firstName}} {{user.lastName}}",
    "totalPrice": "{{quantity * unitPrice}}",
    "discountAmount": "{{originalPrice - salePrice}}",
    "isEligible": "{{user.age >= 18 && user.verified}}"
  }
}
 
// Use computed properties
"Welcome, {{fullName}}!"
"Total: ${{totalPrice}}"

Dynamic Data Loading

Load data based on user interactions:

// Dynamic data loading
{
  "triggers": {
    "onCategoryChange": {
      "action": "loadData",
      "dataSource": "products",
      "parameters": {
        "category": "{{selectedCategory}}"
      }
    }
  }
}

Real-Time Data Binding

WebSocket Connections

Connect to real-time data streams:

// WebSocket configuration
{
  "dataSource": {
    "type": "websocket",
    "url": "wss://api.example.com/live-data",
    "events": {
      "priceUpdate": "updateProductPrice",
      "stockChange": "updateInventory"
    }
  }
}

Auto-Refresh

Automatically refresh data at intervals:

// Auto-refresh configuration
{
  "dataSource": {
    "type": "api",
    "url": "https://api.example.com/dashboard",
    "autoRefresh": {
      "enabled": true,
      "interval": 30000, // 30 seconds
      "onError": "exponentialBackoff"
    }
  }
}

Error Handling and Fallbacks

Graceful Degradation

Handle missing or invalid data:

// Default values
"{{user.name || 'Anonymous'}}"
"{{product.image || '/images/placeholder.jpg'}}"
 
// Type checking
"{{typeof price === 'number' ? price : 0}}"
 
// Safe navigation
"{{user?.profile?.bio || 'No bio available'}}"

Error Boundaries

Define error handling strategies:

// Error handling configuration
{
  "errorHandling": {
    "onDataError": "showFallback",
    "onBindingError": "logAndContinue",
    "fallbackData": {
      "user": {"name": "Guest User"},
      "products": []
    }
  }
}

Performance Optimization

Lazy Loading

Load data only when needed:

// Lazy loading configuration
{
  "lazyLoad": {
    "enabled": true,
    "threshold": "50px", // Load when 50px from viewport
    "placeholder": "Loading..."
  }
}

Data Caching

Cache frequently accessed data:

// Caching configuration
{
  "cache": {
    "enabled": true,
    "ttl": 300000, // 5 minutes
    "strategy": "lru", // Least Recently Used
    "maxSize": 100 // Maximum cached items
  }
}

Batch Updates

Group multiple data updates:

// Batch update configuration
{
  "batchUpdates": {
    "enabled": true,
    "debounce": 100, // Wait 100ms before applying updates
    "maxBatchSize": 50
  }
}

Testing Data Binding

Mock Data

Use mock data for testing:

// Mock data configuration
{
  "mockData": {
    "enabled": true,
    "scenarios": {
      "emptyState": {"products": []},
      "loadingState": {"loading": true},
      "errorState": {"error": "Failed to load data"}
    }
  }
}

Validation

Validate data binding setup:

// Validation rules
{
  "validation": {
    "required": ["user.name", "product.price"],
    "types": {
      "user.age": "number",
      "user.email": "email",
      "product.price": "currency"
    }
  }
}

Best Practices

  1. Keep bindings simple: Avoid complex logic in templates
  2. Use computed properties: For calculated values
  3. Handle errors gracefully: Always provide fallbacks
  4. Optimize performance: Use caching and lazy loading
  5. Test thoroughly: With various data scenarios
  6. Document data structure: Make it clear what data is expected

Next Steps