Skip to main content

Overview

The DataSet interface is a generic key-value structure used for passing data to and from form controls. It maps control IDs (strings) to their corresponding values. Source: projects/mat-dynamic-form/src/lib/models/DataSet.ts:1

Interface Definition

interface DataSet<T> {
  [key: string]: T;
}

Type Parameter

T
generic
required
The type of values stored in the dataset. Can be any type - string, number, boolean, object, etc.

Usage

DataSet is primarily used with FormStructure’s value management methods:

Usage Examples

Basic String Values

import { FormStructure, DataSet } from 'mat-dynamic-form';

const structure = new FormStructure(/* ... */);

// Patch values - all strings
const userData: DataSet<string> = {
  firstName: 'John',
  lastName: 'Doe',
  email: '[email protected]',
  country: 'us'
};

structure.patchValue(userData);

Mixed Value Types

import { DataSet } from 'mat-dynamic-form';

// Dataset with various types
const formData: DataSet<any> = {
  username: 'johndoe',              // string
  age: 30,                          // number
  subscribed: true,                 // boolean
  birthdate: new Date('1993-05-15'),// Date
  preferences: ['email', 'sms'],    // array
  address: {                        // object
    street: '123 Main St',
    city: 'New York',
    zip: '10001'
  }
};

structure.patchValue(formData);

Type-Safe Form Data

import { FormStructure, DataSet, Input, Checkbox, Dropdown } from 'mat-dynamic-form';

// Define your form data interface
interface UserRegistration {
  username: string;
  email: string;
  password: string;
  country: string;
  agreeToTerms: boolean;
  age: number;
}

// Create form structure
const structure = new FormStructure(
  'User Registration',
  [
    new Input('username', 'Username'),
    new Input('email', 'Email'),
    new InputPassword('password', 'Password'),
    new Dropdown('country', 'Country', countryOptions),
    new Checkbox('agreeToTerms', 'I agree to terms'),
    new InputNumber('age', 'Age')
  ]
);

// Patch with type safety
const initialData: DataSet<UserRegistration[keyof UserRegistration]> = {
  username: 'johndoe',
  email: '[email protected]',
  country: 'us',
  agreeToTerms: false,
  age: 25
};

structure.patchValue(initialData);

// Get values with type safety
const formValues = structure.getValue<UserRegistration>();
console.log(formValues.username); // TypeScript knows this is a string
console.log(formValues.agreeToTerms); // TypeScript knows this is a boolean

Partial Updates

import { DataSet } from 'mat-dynamic-form';

// Update only specific fields
const partialUpdate: DataSet<string> = {
  email: '[email protected]',
  phone: '+1-555-0123'
};

structure.patchValue(partialUpdate);
// Other fields remain unchanged

Loading Data from API

import { FormStructure, DataSet } from 'mat-dynamic-form';
import { HttpClient } from '@angular/common/http';

interface UserDTO {
  id: string;
  firstName: string;
  lastName: string;
  email: string;
  country: string;
}

@Component({
  selector: 'app-user-form',
  template: `<mat-dynamic-form [structure]="structure"></mat-dynamic-form>`
})
export class UserFormComponent implements OnInit {
  structure: FormStructure;
  
  constructor(private http: HttpClient) {
    this.structure = new FormStructure(/* ... */);
  }
  
  ngOnInit() {
    // Load user data
    this.http.get<UserDTO>('/api/users/123').subscribe(user => {
      // Convert API response to DataSet
      const formData: DataSet<string> = {
        firstName: user.firstName,
        lastName: user.lastName,
        email: user.email,
        country: user.country
      };
      
      this.structure.patchValue(formData);
    });
  }
}

Transforming Values

import { DataSet } from 'mat-dynamic-form';

// API returns different format than form needs
interface ApiResponse {
  user_name: string;
  user_email: string;
  is_active: boolean;
}

function transformToFormData(apiData: ApiResponse): DataSet<any> {
  return {
    username: apiData.user_name,
    email: apiData.user_email,
    active: apiData.is_active
  };
}

const apiResponse: ApiResponse = {
  user_name: 'johndoe',
  user_email: '[email protected]',
  is_active: true
};

const formData = transformToFormData(apiResponse);
structure.patchValue(formData);

Form Submission

import { FormStructure, DataSet, Button } from 'mat-dynamic-form';

const submitButton = new Button(
  'submit',
  'Submit',
  {
    type: 'click',
    style: 'primary',
    onEvent: ({ structure }) => {
      if (structure.isValid()) {
        // Get form values as DataSet
        const formData: DataSet<any> = structure.getValue();
        
        // Submit to API
        fetch('/api/submit', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(formData)
        }).then(response => {
          if (response.ok) {
            console.log('Form submitted successfully');
          }
        });
      }
    }
  },
  false,
  'send',
  false,
  true
);

Conditional Field Updates

import { DataSet, ActionEvent } from 'mat-dynamic-form';

const accountTypeDropdown = new Dropdown(
  'accountType',
  'Account Type',
  [
    new OptionChild('Personal', 'personal'),
    new OptionChild('Business', 'business')
  ],
  'personal',
  false,
  false,
  null,
  null,
  false,
  null,
  null,
  {
    type: 'valueChange',
    onEvent: ({ event, structure }: ActionEvent) => {
      if (event === 'business') {
        // Pre-fill business-specific fields
        const businessDefaults: DataSet<string> = {
          taxId: '',
          companyName: '',
          businessType: 'llc'
        };
        structure.patchValue(businessDefaults);
      } else {
        // Clear business fields for personal accounts
        const clearBusiness: DataSet<string> = {
          taxId: '',
          companyName: '',
          businessType: ''
        };
        structure.patchValue(clearBusiness);
      }
    }
  }
);

Default Values

import { FormStructure, DataSet, Input, Dropdown } from 'mat-dynamic-form';

// Define default values
const defaultValues: DataSet<any> = {
  country: 'us',
  language: 'en',
  currency: 'usd',
  notifications: true,
  theme: 'light'
};

const structure = new FormStructure(
  'Settings',
  [
    new Dropdown('country', 'Country', countryOptions),
    new Dropdown('language', 'Language', languageOptions),
    new Dropdown('currency', 'Currency', currencyOptions),
    new Checkbox('notifications', 'Enable notifications'),
    new Dropdown('theme', 'Theme', themeOptions)
  ]
);

// Apply defaults after form initialization
setTimeout(() => {
  structure.patchValue(defaultValues);
}, 0);

Getting Values with Type Inference

import { FormStructure, DataSet } from 'mat-dynamic-form';

interface ContactForm {
  name: string;
  email: string;
  subject: string;
  message: string;
  priority: 'low' | 'medium' | 'high';
}

const structure = new FormStructure(/* ... */);

// TypeScript infers the return type
const values = structure.getValue<ContactForm>();

// Now you have type-safe access
const name: string = values.name;
const priority: 'low' | 'medium' | 'high' = values.priority;

// Send to API with proper typing
function submitContact(data: ContactForm) {
  return fetch('/api/contact', {
    method: 'POST',
    body: JSON.stringify(data)
  });
}

if (structure.isValid()) {
  submitContact(values);
}

Comparing Form States

import { DataSet } from 'mat-dynamic-form';

class FormComponent {
  originalValues: DataSet<any>;
  
  ngOnInit() {
    // Save original values
    this.originalValues = this.structure.getValue();
  }
  
  hasChanges(): boolean {
    const currentValues = this.structure.getValue();
    
    // Compare datasets
    return JSON.stringify(this.originalValues) !== JSON.stringify(currentValues);
  }
  
  getChangedFields(): DataSet<any> {
    const currentValues = this.structure.getValue();
    const changes: DataSet<any> = {};
    
    Object.keys(currentValues).forEach(key => {
      if (currentValues[key] !== this.originalValues[key]) {
        changes[key] = currentValues[key];
      }
    });
    
    return changes;
  }
}

Batch Operations

import { DataSet } from 'mat-dynamic-form';

// Clear all text fields
function clearTextFields(structure: FormStructure) {
  const clearData: DataSet<string> = {};
  
  structure.nodes.forEach(node => {
    if (node instanceof Input || node instanceof TextArea) {
      clearData[node.id] = '';
    }
  });
  
  structure.patchValue(clearData);
}

// Disable all fields
function disableAllFields(structure: FormStructure) {
  structure.nodes.forEach(node => {
    const control = structure.getControlById(node.id);
    control?.disable();
  });
}

// Reset to defaults
function resetToDefaults(structure: FormStructure, defaults: DataSet<any>) {
  structure.reset();
  structure.patchValue(defaults);
}

Error Handling

import { DataSet } from 'mat-dynamic-form';
import { ReferenceException } from 'mat-dynamic-form';

try {
  const data: DataSet<string> = {
    nonExistentField: 'value',  // This field doesn't exist in the form
    existingField: 'value'
  };
  
  structure.patchValue(data);
} catch (error) {
  if (error instanceof ReferenceException) {
    console.error('Field not found:', error.message);
  }
}

Best Practices

1. Use Type Safety

// Define explicit interfaces for your form data
interface RegistrationData {
  username: string;
  email: string;
  age: number;
}

// Use the interface with DataSet
const data: DataSet<RegistrationData[keyof RegistrationData]> = {
  username: 'johndoe',
  email: '[email protected]',
  age: 25
};

2. Validate Before Patching

function safePatchValue(structure: FormStructure, data: DataSet<any>) {
  // Only patch fields that exist
  const safeData: DataSet<any> = {};
  
  Object.keys(data).forEach(key => {
    if (structure.getControlById(key)) {
      safeData[key] = data[key];
    } else {
      console.warn(`Field '${key}' not found in form`);
    }
  });
  
  structure.patchValue(safeData);
}

3. Handle Null/Undefined Values

function patchWithDefaults(
  structure: FormStructure, 
  data: DataSet<any>,
  defaults: DataSet<any>
) {
  const mergedData: DataSet<any> = { ...defaults };
  
  Object.keys(data).forEach(key => {
    if (data[key] !== null && data[key] !== undefined) {
      mergedData[key] = data[key];
    }
  });
  
  structure.patchValue(mergedData);
}
  • FormStructure - Uses DataSet for value management
  • Node - Form fields that hold values

TypeScript Tips

// Generic helper for creating typed DataSets
function createDataSet<T>(): DataSet<T> {
  return {};
}

// Usage
const stringData = createDataSet<string>();
stringData.field1 = 'value';

const numberData = createDataSet<number>();
numberData.age = 25;

// Utility type for partial form data
type PartialFormData<T> = Partial<DataSet<T>>;

// Use with form updates
function updateForm(partialData: PartialFormData<string>) {
  structure.patchValue(partialData);
}

Build docs developers (and LLMs) love