or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

component-system.mddata-binding.mdform-api.mdform-creation.mdform-factory.mdindex.mdrule-system.mdvalidation.md
tile.json

data-binding.mddocs/

Data Binding and Effects

Advanced data binding, global data management, effect system, and reactive data loading capabilities for creating dynamic, data-driven forms.

Capabilities

Global Data Management

System for managing global data sources that can be shared across forms and accessed by all components.

/**
 * Set global data value
 * @param id - Data identifier (dot notation supported for nested access)
 * @param data - Data value to store
 */
setData(id: string, data: any): void;

/**
 * Get global data value
 * @param id - Data identifier (dot notation supported for nested access)  
 * @param defaultValue - Default value if data not found
 * @returns Data value or default if not found
 */
getData(id: string, defaultValue?: any): any;

/**
 * Set data driver function for dynamic data access
 * @param id - Data identifier
 * @param callback - Function that returns data based on key parameter
 */
setDataDriver(id: string, callback: (key: string) => any): void;

/**
 * Remove global data entry
 * @param id - Data identifier to remove
 */
removeData(id: string): void;

/**
 * Refresh specific data and trigger updates
 * @param id - Data identifier to refresh
 */
refreshData(id: string): void;

Usage Examples:

// Set global configuration data
FormCreate.setData("app.config", {
  apiUrl: "https://api.example.com",
  theme: "dark",
  locale: "en-US"
});

// Access nested data
const apiUrl = FormCreate.getData("app.config.apiUrl");

// Set up data driver for user preferences
FormCreate.setDataDriver("userPrefs", (key) => {
  return localStorage.getItem(`pref_${key}`);
});

// Access driver data
const userTheme = FormCreate.getData("userPrefs.theme");

// Remove data when no longer needed
FormCreate.removeData("app.tempData");

Data Binding in Forms

Form-level data binding methods for managing data within specific form instances.

interface BaseApi<OptionAttrs, CreatorAttrs, RuleAttrs, ApiAttrs> {
  /**
   * Get data value within form context
   * @param id - Data identifier with special prefixes
   * @param def - Default value if not found
   * @returns Data value or default
   */
  getData(id: string, def?: any): any;

  /**
   * Set data value within form context
   * @param id - Data identifier
   * @param data - Data value to set
   * @param isGlobal - Whether to set in global scope (default: false)
   */
  setData(id: string, data: any, isGlobal?: boolean): void;

  /**
   * Refresh data and trigger reactive updates
   * @param id - Data identifier to refresh
   */
  refreshData(id: string): void;

  /**
   * Watch data changes and react to updates
   * @param fn - Function called when watched data changes
   * @returns Unwatch function to stop watching
   */
  watchData(fn: (get: (id: string, defaultValue?: any) => any, change: boolean) => void): () => Function;
}

Special Data Prefixes:

  • $form - Current form data
  • $topForm - Top-level form data
  • $scopeForm - Scope form data
  • $options - Form options
  • $globalData - Global data sources
  • $var - Global variables
  • $locale - Current locale
  • $t - Translation data
  • $preview - Preview mode flag

Usage Examples:

// Access different data scopes
const currentFormData = api.getData("$form");
const topLevelData = api.getData("$topForm");
const globalSettings = api.getData("$globalData.settings");
const currentLocale = api.getData("$locale");

// Set form-specific data
api.setData("formState.step", 2);

// Set global data from form
api.setData("user.lastForm", "contactForm", true);

// Watch for data changes
const unwatch = api.watchData((get, changed) => {
  const step = get("formState.step");
  if (changed && step === 3) {
    // React to step change
    api.hidden(false, "finalizeButton");
  }
});

HTTP Request System

Built-in HTTP request capabilities for fetching data, submitting forms, and integrating with APIs.

/**
 * Make HTTP request with form context
 * @param options - Request configuration
 * @returns Promise resolving with response data
 */
fetch(options: FetchOption): Promise<any>;

/**
 * Watch HTTP requests with reactive data loading
 * @param opt - Request options with dynamic data binding
 * @param callback - Success callback function
 * @param error - Error callback function
 * @param beforeFetch - Pre-request hook
 * @returns Unwatch function
 */
watchFetch(
  opt: FetchOption, 
  callback: (res: any, change: boolean) => void, 
  error?: Function, 
  beforeFetch?: (opt: FetchOption, change: boolean) => boolean
): Function;

interface FetchOption {
  /** Request URL */
  action: string;
  /** HTTP method (default: GET) */
  method?: string;
  /** Request body data */
  data?: Object;
  /** URL query parameters */
  query?: Object;
  /** Data type expected (json, text, etc.) */
  dataType?: 'json';
  /** Request headers */
  headers?: Object;
  /** Send credentials with request */
  withCredentials?: boolean;
  /** Success callback */
  onSuccess: (body: any) => void;
  /** Error callback */
  onError?: (e: Error | ProgressEvent) => void;
}

Usage Examples:

// Simple API request
api.fetch({
  action: "/api/users",
  method: "GET",
  onSuccess: (users) => {
    api.setData("users", users);
  },
  onError: (error) => {
    console.error("Failed to load users:", error);
  }
});

// Form submission
api.fetch({
  action: "/api/forms/submit",
  method: "POST", 
  data: api.formData(),
  headers: {
    "Content-Type": "application/json"
  },
  onSuccess: (response) => {
    console.log("Form submitted successfully:", response);
  }
});

// Reactive data loading
const unwatchUsers = api.watchFetch({
  action: "/api/users",
  query: { 
    department: "{{department}}", // Dynamic from form data
    active: true 
  }
}, (users, changed) => {
  // Update options when data changes
  api.updateRule("assignedUser", {
    options: users.map(u => ({ label: u.name, value: u.id }))
  });
});

Effect System for Data

Advanced effect system for reactive data loading, transformation, and binding.

interface FetchEffectOption {
  /** URL or function returning Promise */
  action: string | ((rule: object, api: object) => Promise<any>);
  /** Target field for data binding */
  to?: string;
  /** Data parsing function or expression */
  parse?: string | ((body: any, rule: Object, api: Object) => any);
  /** HTTP method */
  method?: string;
  /** Request data */
  data?: Object;
  /** Query parameters */
  query?: Object;
  /** Data type */
  dataType?: 'json';
  /** Request headers */
  headers?: Object;
  /** Send credentials */
  withCredentials?: boolean;
  /** Error handler */
  onError?: (e: Error | ProgressEvent, rule: Object, api: Object) => void;
  /** Watch for changes */
  watch?: boolean;
  /** Debounce delay in milliseconds */
  wait?: number;
}

interface LoadDataEffectOption {
  /** Target attribute to set */
  attr?: string;
  /** Data template expression */
  template?: string;
  /** Data handler function */
  handler?: (get: (id: string, defaultValue?: any) => any, rule: Object, api: Object) => any;
  /** Target field for binding */
  to?: string;
  /** Copy data instead of reference */
  copy?: boolean;
  /** Watch for changes */
  watch?: boolean;
  /** Modify existing data */
  modify?: boolean;
  /** Debounce delay */
  wait?: number;
}

Usage Examples:

// Dynamic options loading with effect
const categoryRule = {
  type: "select",
  field: "category",
  title: "Category",
  effect: {
    fetch: {
      action: "/api/categories",
      to: "options",
      parse: (data) => data.map(cat => ({ label: cat.name, value: cat.id })),
      watch: true
    }
  }
};

// Load data based on other fields
const subcategoryRule = {
  type: "select", 
  field: "subcategory",
  title: "Subcategory",
  effect: {
    fetch: {
      action: "/api/subcategories",
      query: {
        categoryId: "{{category}}" // Dynamic from category field
      },
      to: "options",
      parse: "data.items",
      wait: 300 // Debounce requests
    }
  }
};

// Load data with custom handler
const userInfoRule = {
  type: "input",
  field: "userId",
  title: "User ID",
  effect: {
    loadData: {
      handler: (get, rule, api) => {
        const userId = get("$form.userId");
        if (userId) {
          return get(`$globalData.users.${userId}`);
        }
        return null;
      },
      to: "userDetails",
      watch: true
    }
  }
};

Global Data Sources Configuration

Configuration system for global data sources including static data and remote data fetching.

interface StaticDataItem {
  /** Data name/label */
  label: string;
  /** Data type identifier */
  type: 'static';
  /** Static data result */
  result: any;
}

interface FetchDataItem {
  /** Data name/label */
  label: string;
  /** Data type identifier */
  type: 'fetch';
  /** Request URL */
  action: string;
  /** HTTP method */
  method: 'GET' | 'POST';
  /** Request headers */
  headers?: Object;
  /** Request data */
  data?: Object;
  /** Response parser */
  parse?: string | ((res: any) => any);
  /** Error handler */
  onError?: string | ((e) => void);
}

interface GlobalData {
  [id: string]: StaticDataItem | FetchDataItem;
}

Usage Example:

// Configure global data sources
const formOptions = {
  globalData: {
    countries: {
      label: "Countries List",
      type: "static",
      result: [
        { label: "United States", value: "US" },
        { label: "Canada", value: "CA" },
        { label: "United Kingdom", value: "UK" }
      ]
    },
    departments: {
      label: "Company Departments", 
      type: "fetch",
      action: "/api/departments",
      method: "GET",
      parse: "data.departments"
    }
  }
};

// Access in rules
const countryRule = {
  type: "select",
  field: "country",
  title: "Country",
  options: "{{$globalData.countries}}"
};

Reactive Data Binding Utilities

Utility methods for working with reactive data and managing data synchronization.

/**
 * Set effect data for specific rule
 * @param id - Rule identifier
 * @param attr - Effect attribute name
 * @param value - Value to set
 */
setEffect(id: string, attr: string, value: any): void;

/**
 * Clear effect data for rule
 * @param id - Rule identifier  
 * @param attr - Specific attribute to clear (optional)
 */
clearEffectData(id: string, attr?: string): void;

/**
 * Execute function with deferred synchronization
 * @param fn - Function to execute
 * @param sync - Whether to sync immediately after
 */
deferSyncValue(fn: Function, sync?: boolean): void;

/**
 * Get translation text
 * @param id - Translation key
 * @param params - Translation parameters
 * @returns Translated text
 */
t(id: string, params?: Object): string | undefined;

/**
 * Get current locale
 * @returns Locale string (e.g., 'en-US')
 */
getLocale(): string;

This comprehensive data binding system enables creating highly dynamic, data-driven forms with real-time updates, API integration, and complex data relationships.