Expression Properties
Expression properties are template-based computed fields that combine multiple properties into dynamic, formatted strings. Perfect for display names, labels, descriptions, and any scenario where you need to compose values dynamically.
What are Expression Properties?
An expression property uses a template string with variable interpolation to create computed values:
{
key: 'displayName',
type: 'string',
expression: '${firstName} ${lastName}',
}How it works:
Input: { firstName: 'Alice', lastName: 'Johnson' }
Output: { displayName: 'Alice Johnson' }Key features:
- Variable interpolation with
${variable} - String concatenation
- Access to all properties in the same model
- Access to referenced model properties
- Automatic updates when source values change
Basic Expression
Simple Variable Interpolation
import { Dcupl } from '@dcupl/core';
import type { ModelDefinition } from '@dcupl/common';
const dcupl = new Dcupl();
const customerModel: ModelDefinition = {
key: 'Customer',
properties: [
{ key: 'firstName', type: 'string' },
{ key: 'lastName', type: 'string' },
// Expression property
{
key: 'fullName',
type: 'string',
expression: '${firstName} ${lastName}',
},
],
data: [
{ key: 'c1', firstName: 'Alice', lastName: 'Johnson' },
{ key: 'c2', firstName: 'Bob', lastName: 'Smith' },
],
};
dcupl.models.set(customerModel);
await dcupl.init();
const customerList = dcupl.lists.create({ modelKey: 'Customer' });
const customers = customerList.catalog.query.items();
console.log(customers[0].fullName); // 'Alice Johnson'
console.log(customers[1].fullName); // 'Bob Smith'
// Filter on expression property
customerList.catalog.query.addCondition({
attribute: 'fullName',
operator: 'find',
value: 'Alice',
});String Concatenation
With Static Text
const productModel: ModelDefinition = {
key: 'Product',
properties: [
{ key: 'name', type: 'string' },
{ key: 'sku', type: 'string' },
{ key: 'price', type: 'float' },
// Product title with SKU
{
key: 'displayTitle',
type: 'string',
expression: '${name} (${sku})',
},
// Price label
{
key: 'priceLabel',
type: 'string',
expression: 'Price: ${price}',
},
],
data: [
{ key: 'p1', name: 'Laptop', sku: 'LPT-001', price: 999 },
{ key: 'p2', name: 'Mouse', sku: 'MSE-002', price: 29 },
],
};
dcupl.models.set(productModel);
await dcupl.init();
const productList = dcupl.lists.create({ modelKey: 'Product' });
const products = productList.catalog.query.items();
console.log(products[0].displayTitle); // 'Laptop (LPT-001)'
console.log(products[0].priceLabel); // 'Price: $999'Multi-Line Templates
const addressModel: ModelDefinition = {
key: 'Address',
properties: [
{ key: 'street', type: 'string' },
{ key: 'city', type: 'string' },
{ key: 'state', type: 'string' },
{ key: 'zip', type: 'string' },
// Full address
{
key: 'fullAddress',
type: 'string',
expression: '${street}, ${city}, ${state} ${zip}',
},
],
data: [
{
key: 'a1',
street: '123 Main St',
city: 'San Francisco',
state: 'CA',
zip: '94102',
},
],
};
dcupl.models.set(addressModel);
await dcupl.init();
const addressList = dcupl.lists.create({ modelKey: 'Address' });
const addresses = addressList.catalog.query.items();
console.log(addresses[0].fullAddress);
// '123 Main St, San Francisco, CA 94102'Complex Expressions
Multiple Variables
const employeeModel: ModelDefinition = {
key: 'Employee',
properties: [
{ key: 'firstName', type: 'string' },
{ key: 'lastName', type: 'string' },
{ key: 'title', type: 'string' },
{ key: 'department', type: 'string' },
{ key: 'email', type: 'string' },
// Display name with title
{
key: 'displayName',
type: 'string',
expression: '${firstName} ${lastName}, ${title}',
},
// Email signature
{
key: 'emailSignature',
type: 'string',
expression: '${firstName} ${lastName}\n${title}\n${department}\n${email}',
},
],
data: [
{
key: 'e1',
firstName: 'Alice',
lastName: 'Johnson',
title: 'Senior Engineer',
department: 'Engineering',
email: 'alice@example.com',
},
],
};
dcupl.models.set(employeeModel);
await dcupl.init();
const employeeList = dcupl.lists.create({ modelKey: 'Employee' });
const employees = employeeList.catalog.query.items();
console.log(employees[0].displayName);
// 'Alice Johnson, Senior Engineer'Using Reference Properties
Expression properties can access properties from referenced models:
const brandModel: ModelDefinition = {
key: 'Brand',
properties: [
{ key: 'name', type: 'string' },
{ key: 'tagline', type: 'string' },
],
data: [{ key: 'b1', name: 'Apple', tagline: 'Think Different' }],
};
const productModel: ModelDefinition = {
key: 'Product',
properties: [
{ key: 'name', type: 'string' },
{ key: 'model', type: 'string' },
// Expression using reference property
{
key: 'fullTitle',
type: 'string',
expression: '${brand.name} ${name} - ${model}',
},
],
references: [{ key: 'brand', type: 'singleValued', model: 'Brand' }],
data: [{ key: 'p1', name: 'iPhone', model: '14 Pro', brand: 'b1' }],
};
dcupl.models.set(brandModel);
dcupl.models.set(productModel);
await dcupl.init();
const productList = dcupl.lists.create({ modelKey: 'Product' });
const products = productList.catalog.query.items();
console.log(products[0].fullTitle); // 'Apple iPhone - 14 Pro'Use Cases
Use Case 1: Product Display Names
const productModel: ModelDefinition = {
key: 'Product',
properties: [
{ key: 'brand', type: 'string' },
{ key: 'name', type: 'string' },
{ key: 'variant', type: 'string' },
{ key: 'color', type: 'string' },
{ key: 'size', type: 'string' },
// Product display title
{
key: 'displayTitle',
type: 'string',
expression: '${brand} ${name} - ${variant} (${color}, ${size})',
},
],
data: [
{
key: 'p1',
brand: 'Nike',
name: 'Air Max',
variant: '270',
color: 'Black',
size: '10',
},
],
};
dcupl.models.set(productModel);
await dcupl.init();
const productList = dcupl.lists.create({ modelKey: 'Product' });
const products = productList.catalog.query.items();
console.log(products[0].displayTitle);
// 'Nike Air Max - 270 (Black, 10)'Use Case 2: Contact Information
const contactModel: ModelDefinition = {
key: 'Contact',
properties: [
{ key: 'firstName', type: 'string' },
{ key: 'lastName', type: 'string' },
{ key: 'company', type: 'string' },
{ key: 'phone', type: 'string' },
{ key: 'email', type: 'string' },
// Full name
{
key: 'fullName',
type: 'string',
expression: '${firstName} ${lastName}',
},
// Contact card
{
key: 'contactCard',
type: 'string',
expression: '${fullName} | ${company} | ${phone} | ${email}',
},
],
data: [
{
key: 'c1',
firstName: 'Alice',
lastName: 'Johnson',
company: 'Acme Corp',
phone: '555-1234',
email: 'alice@acme.com',
},
],
};
dcupl.models.set(contactModel);
await dcupl.init();
const contactList = dcupl.lists.create({ modelKey: 'Contact' });
const contacts = contactList.catalog.query.items();
console.log(contacts[0].contactCard);
// 'Alice Johnson | Acme Corp | 555-1234 | alice@acme.com'Use Case 3: Order Summary
const orderModel: ModelDefinition = {
key: 'Order',
properties: [
{ key: 'orderNumber', type: 'string' },
{ key: 'customerName', type: 'string' },
{ key: 'itemCount', type: 'int' },
{ key: 'total', type: 'float' },
{ key: 'status', type: 'string' },
// Order summary line
{
key: 'summary',
type: 'string',
expression:
'Order #${orderNumber} - ${customerName} (${itemCount} items, ${total}) - ${status}',
},
],
data: [
{
key: 'o1',
orderNumber: 'ORD-1001',
customerName: 'Alice Johnson',
itemCount: 3,
total: 299.99,
status: 'Shipped',
},
],
};
dcupl.models.set(orderModel);
await dcupl.init();
const orderList = dcupl.lists.create({ modelKey: 'Order' });
const orders = orderList.catalog.query.items();
console.log(orders[0].summary);
// 'Order #ORD-1001 - Alice Johnson (3 items, $299.99) - Shipped'Use Case 4: File Paths
const fileModel: ModelDefinition = {
key: 'File',
properties: [
{ key: 'directory', type: 'string' },
{ key: 'filename', type: 'string' },
{ key: 'extension', type: 'string' },
// Full file path
{
key: 'fullPath',
type: 'string',
expression: '${directory}/${filename}.${extension}',
},
],
data: [{ key: 'f1', directory: '/documents/reports', filename: 'sales-2025', extension: 'pdf' }],
};
dcupl.models.set(fileModel);
await dcupl.init();
const fileList = dcupl.lists.create({ modelKey: 'File' });
const files = fileList.catalog.query.items();
console.log(files[0].fullPath);
// '/documents/reports/sales-2025.pdf'Use Case 5: URL Slugs
const articleModel: ModelDefinition = {
key: 'Article',
properties: [
{ key: 'year', type: 'int' },
{ key: 'month', type: 'int' },
{ key: 'slug', type: 'string' },
// URL path
{
key: 'urlPath',
type: 'string',
expression: '/blog/${year}/${month}/${slug}',
},
],
data: [{ key: 'a1', year: 2025, month: 12, slug: 'getting-started-with-dcupl' }],
};
dcupl.models.set(articleModel);
await dcupl.init();
const articleList = dcupl.lists.create({ modelKey: 'Article' });
const articles = articleList.catalog.query.items();
console.log(articles[0].urlPath);
// '/blog/2025/12/getting-started-with-dcupl'Use Case 6: Badge Labels
const userModel: ModelDefinition = {
key: 'User',
properties: [
{ key: 'username', type: 'string' },
{ key: 'level', type: 'int' },
{ key: 'rank', type: 'string' },
{ key: 'points', type: 'int' },
// User badge
{
key: 'badge',
type: 'string',
expression: '${username} - Level ${level} ${rank} (${points} pts)',
},
],
data: [{ key: 'u1', username: 'alice_dev', level: 42, rank: 'Expert', points: 8500 }],
};
dcupl.models.set(userModel);
await dcupl.init();
const userList = dcupl.lists.create({ modelKey: 'User' });
const users = userList.catalog.query.items();
console.log(users[0].badge);
// 'alice_dev - Level 42 Expert (8500 pts)'Use Case 7: Event Descriptions
const eventModel: ModelDefinition = {
key: 'Event',
properties: [
{ key: 'title', type: 'string' },
{ key: 'date', type: 'string' },
{ key: 'location', type: 'string' },
{ key: 'attendees', type: 'int' },
// Event description
{
key: 'description',
type: 'string',
expression: '${title} on ${date} at ${location} (${attendees} attendees)',
},
],
data: [
{
key: 'e1',
title: 'Tech Conference 2025',
date: '2025-06-15',
location: 'San Francisco',
attendees: 500,
},
],
};
dcupl.models.set(eventModel);
await dcupl.init();
const eventList = dcupl.lists.create({ modelKey: 'Event' });
const events = eventList.catalog.query.items();
console.log(events[0].description);
// 'Tech Conference 2025 on 2025-06-15 at San Francisco (500 attendees)'Use Case 8: Inventory Labels
const inventoryModel: ModelDefinition = {
key: 'Inventory',
properties: [
{ key: 'productName', type: 'string' },
{ key: 'warehouse', type: 'string' },
{ key: 'location', type: 'string' },
{ key: 'quantity', type: 'int' },
// Inventory label
{
key: 'label',
type: 'string',
expression: '${productName} | WH: ${warehouse} | LOC: ${location} | QTY: ${quantity}',
},
],
data: [
{
key: 'i1',
productName: 'Widget Pro',
warehouse: 'WH-01',
location: 'A-12-3',
quantity: 150,
},
],
};
dcupl.models.set(inventoryModel);
await dcupl.init();
const inventoryList = dcupl.lists.create({ modelKey: 'Inventory' });
const inventory = inventoryList.catalog.query.items();
console.log(inventory[0].label);
// 'Widget Pro | WH: WH-01 | LOC: A-12-3 | QTY: 150'Advanced Examples
Example 9: Currency Formatting
const priceModel: ModelDefinition = {
key: 'Product',
properties: [
{ key: 'name', type: 'string' },
{ key: 'price', type: 'float' },
{ key: 'currency', type: 'string' },
// Price with currency
{
key: 'formattedPrice',
type: 'string',
expression: '${price} ${currency}',
},
],
data: [
{ key: 'p1', name: 'Laptop', price: 999.99, currency: 'USD' },
{ key: 'p2', name: 'Mouse', price: 29.99, currency: 'EUR' },
],
};
dcupl.models.set(priceModel);
await dcupl.init();
const productList = dcupl.lists.create({ modelKey: 'Product' });
const products = productList.catalog.query.items();
console.log(products[0].formattedPrice); // '999.99 USD'
console.log(products[1].formattedPrice); // '29.99 EUR'Example 10: Coordinates
const locationModel: ModelDefinition = {
key: 'Location',
properties: [
{ key: 'name', type: 'string' },
{ key: 'latitude', type: 'float' },
{ key: 'longitude', type: 'float' },
// Coordinates string
{
key: 'coordinates',
type: 'string',
expression: '${latitude}, ${longitude}',
},
// Google Maps URL
{
key: 'mapsUrl',
type: 'string',
expression: 'https://maps.google.com/?q=${latitude},${longitude}',
},
],
data: [{ key: 'l1', name: 'Office', latitude: 37.7749, longitude: -122.4194 }],
};
dcupl.models.set(locationModel);
await dcupl.init();
const locationList = dcupl.lists.create({ modelKey: 'Location' });
const locations = locationList.catalog.query.items();
console.log(locations[0].coordinates); // '37.7749, -122.4194'
console.log(locations[0].mapsUrl);
// 'https://maps.google.com/?q=37.7749,-122.4194'Example 11: Version Numbers
const softwareModel: ModelDefinition = {
key: 'Software',
properties: [
{ key: 'name', type: 'string' },
{ key: 'major', type: 'int' },
{ key: 'minor', type: 'int' },
{ key: 'patch', type: 'int' },
// Semantic version
{
key: 'version',
type: 'string',
expression: '${major}.${minor}.${patch}',
},
// Full software name
{
key: 'fullName',
type: 'string',
expression: '${name} v${version}',
},
],
data: [{ key: 's1', name: 'dcupl', major: 2, minor: 5, patch: 3 }],
};
dcupl.models.set(softwareModel);
await dcupl.init();
const softwareList = dcupl.lists.create({ modelKey: 'Software' });
const software = softwareList.catalog.query.items();
console.log(software[0].version); // '2.5.3'
console.log(software[0].fullName); // 'dcupl v2.5.3'Example 12: Date Ranges
const projectModel: ModelDefinition = {
key: 'Project',
properties: [
{ key: 'name', type: 'string' },
{ key: 'startDate', type: 'string' },
{ key: 'endDate', type: 'string' },
// Date range
{
key: 'dateRange',
type: 'string',
expression: '${startDate} to ${endDate}',
},
],
data: [{ key: 'p1', name: 'Website Redesign', startDate: '2025-01-15', endDate: '2025-03-30' }],
};
dcupl.models.set(projectModel);
await dcupl.init();
const projectList = dcupl.lists.create({ modelKey: 'Project' });
const projects = projectList.catalog.query.items();
console.log(projects[0].dateRange); // '2025-01-15 to 2025-03-30'Example 13: Dimensions
const packageModel: ModelDefinition = {
key: 'Package',
properties: [
{ key: 'length', type: 'float' },
{ key: 'width', type: 'float' },
{ key: 'height', type: 'float' },
{ key: 'unit', type: 'string' },
// Dimensions string
{
key: 'dimensions',
type: 'string',
expression: '${length} x ${width} x ${height} ${unit}',
},
],
data: [{ key: 'pkg1', length: 12.5, width: 8.0, height: 4.5, unit: 'inches' }],
};
dcupl.models.set(packageModel);
await dcupl.init();
const packageList = dcupl.lists.create({ modelKey: 'Package' });
const packages = packageList.catalog.query.items();
console.log(packages[0].dimensions); // '12.5 x 8.0 x 4.5 inches'Example 14: Social Media Handles
const userModel: ModelDefinition = {
key: 'User',
properties: [
{ key: 'username', type: 'string' },
{ key: 'twitter', type: 'string' },
{ key: 'github', type: 'string' },
// Twitter URL
{
key: 'twitterUrl',
type: 'string',
expression: 'https://twitter.com/${twitter}',
},
// GitHub URL
{
key: 'githubUrl',
type: 'string',
expression: 'https://github.com/${github}',
},
// Social links
{
key: 'socialLinks',
type: 'string',
expression: 'Twitter: @${twitter} | GitHub: @${github}',
},
],
data: [{ key: 'u1', username: 'alice', twitter: 'alice_dev', github: 'alicedev' }],
};
dcupl.models.set(userModel);
await dcupl.init();
const userList = dcupl.lists.create({ modelKey: 'User' });
const users = userList.catalog.query.items();
console.log(users[0].twitterUrl); // 'https://twitter.com/alice_dev'
console.log(users[0].githubUrl); // 'https://github.com/alicedev'
console.log(users[0].socialLinks); // 'Twitter: @alice_dev | GitHub: @alicedev'Example 15: Recipe Titles
const recipeModel: ModelDefinition = {
key: 'Recipe',
properties: [
{ key: 'name', type: 'string' },
{ key: 'cuisine', type: 'string' },
{ key: 'prepTime', type: 'int' },
{ key: 'servings', type: 'int' },
// Recipe display title
{
key: 'displayTitle',
type: 'string',
expression: '${name} (${cuisine}) - ${prepTime} min, serves ${servings}',
},
],
data: [{ key: 'r1', name: 'Pasta Carbonara', cuisine: 'Italian', prepTime: 30, servings: 4 }],
};
dcupl.models.set(recipeModel);
await dcupl.init();
const recipeList = dcupl.lists.create({ modelKey: 'Recipe' });
const recipes = recipeList.catalog.query.items();
console.log(recipes[0].displayTitle);
// 'Pasta Carbonara (Italian) - 30 min, serves 4'Example 16: Academic Titles
const facultyModel: ModelDefinition = {
key: 'Faculty',
properties: [
{ key: 'title', type: 'string' },
{ key: 'firstName', type: 'string' },
{ key: 'lastName', type: 'string' },
{ key: 'degree', type: 'string' },
{ key: 'department', type: 'string' },
// Formal name
{
key: 'formalName',
type: 'string',
expression: '${title} ${firstName} ${lastName}, ${degree}',
},
// Full title
{
key: 'fullTitle',
type: 'string',
expression: '${formalName}, ${department}',
},
],
data: [
{
key: 'f1',
title: 'Dr.',
firstName: 'Alice',
lastName: 'Johnson',
degree: 'Ph.D.',
department: 'Computer Science',
},
],
};
dcupl.models.set(facultyModel);
await dcupl.init();
const facultyList = dcupl.lists.create({ modelKey: 'Faculty' });
const faculty = facultyList.catalog.query.items();
console.log(faculty[0].formalName); // 'Dr. Alice Johnson, Ph.D.'
console.log(faculty[0].fullTitle);
// 'Dr. Alice Johnson, Ph.D., Computer Science'Example 17: SKU Generation
const productModel: ModelDefinition = {
key: 'Product',
properties: [
{ key: 'categoryCode', type: 'string' },
{ key: 'brandCode', type: 'string' },
{ key: 'productNumber', type: 'string' },
{ key: 'colorCode', type: 'string' },
// Auto-generated SKU
{
key: 'sku',
type: 'string',
index: true,
expression: '${categoryCode}-${brandCode}-${productNumber}-${colorCode}',
},
],
data: [
{ key: 'p1', categoryCode: 'ELC', brandCode: 'APL', productNumber: '001', colorCode: 'BLK' },
],
};
dcupl.models.set(productModel);
await dcupl.init();
const productList = dcupl.lists.create({ modelKey: 'Product' });
const products = productList.catalog.query.items();
console.log(products[0].sku); // 'ELC-APL-001-BLK'Example 18: Email Subjects
const emailModel: ModelDefinition = {
key: 'Email',
properties: [
{ key: 'recipientName', type: 'string' },
{ key: 'orderNumber', type: 'string' },
{ key: 'status', type: 'string' },
// Email subject line
{
key: 'subject',
type: 'string',
expression: 'Order ${orderNumber} ${status} - ${recipientName}',
},
],
data: [{ key: 'e1', recipientName: 'Alice Johnson', orderNumber: 'ORD-1001', status: 'Shipped' }],
};
dcupl.models.set(emailModel);
await dcupl.init();
const emailList = dcupl.lists.create({ modelKey: 'Email' });
const emails = emailList.catalog.query.items();
console.log(emails[0].subject);
// 'Order ORD-1001 Shipped - Alice Johnson'Example 19: Citation Format
const publicationModel: ModelDefinition = {
key: 'Publication',
properties: [
{ key: 'author', type: 'string' },
{ key: 'year', type: 'int' },
{ key: 'title', type: 'string' },
{ key: 'journal', type: 'string' },
// APA citation
{
key: 'citation',
type: 'string',
expression: '${author} (${year}). ${title}. ${journal}.',
},
],
data: [
{
key: 'pub1',
author: 'Smith, J.',
year: 2025,
title: 'Advances in Data Processing',
journal: 'Journal of Computer Science',
},
],
};
dcupl.models.set(publicationModel);
await dcupl.init();
const publicationList = dcupl.lists.create({ modelKey: 'Publication' });
const publications = publicationList.catalog.query.items();
console.log(publications[0].citation);
// 'Smith, J. (2025). Advances in Data Processing. Journal of Computer Science.'Example 20: Vehicle Identification
const vehicleModel: ModelDefinition = {
key: 'Vehicle',
properties: [
{ key: 'year', type: 'int' },
{ key: 'make', type: 'string' },
{ key: 'model', type: 'string' },
{ key: 'trim', type: 'string' },
{ key: 'color', type: 'string' },
// Vehicle description
{
key: 'description',
type: 'string',
expression: '${year} ${make} ${model} ${trim} - ${color}',
},
],
data: [
{ key: 'v1', year: 2025, make: 'Tesla', model: 'Model 3', trim: 'Long Range', color: 'Blue' },
],
};
dcupl.models.set(vehicleModel);
await dcupl.init();
const vehicleList = dcupl.lists.create({ modelKey: 'Vehicle' });
const vehicles = vehicleList.catalog.query.items();
console.log(vehicles[0].description);
// '2025 Tesla Model 3 Long Range - Blue'Example 21: Medical Records
const patientModel: ModelDefinition = {
key: 'Patient',
properties: [
{ key: 'lastName', type: 'string' },
{ key: 'firstName', type: 'string' },
{ key: 'dob', type: 'string' },
{ key: 'mrn', type: 'string' },
// Patient identifier
{
key: 'patientId',
type: 'string',
expression: '${lastName}, ${firstName} (DOB: ${dob}, MRN: ${mrn})',
},
],
data: [
{ key: 'pt1', lastName: 'Johnson', firstName: 'Alice', dob: '1985-06-15', mrn: 'MRN-12345' },
],
};
dcupl.models.set(patientModel);
await dcupl.init();
const patientList = dcupl.lists.create({ modelKey: 'Patient' });
const patients = patientList.catalog.query.items();
console.log(patients[0].patientId);
// 'Johnson, Alice (DOB: 1985-06-15, MRN: MRN-12345)'Example 22: Real Estate Listings
const propertyModel: ModelDefinition = {
key: 'Property',
properties: [
{ key: 'address', type: 'string' },
{ key: 'beds', type: 'int' },
{ key: 'baths', type: 'float' },
{ key: 'sqft', type: 'int' },
{ key: 'price', type: 'int' },
// Property listing
{
key: 'listing',
type: 'string',
expression: '${address} - ${beds}bd/${baths}ba, ${sqft} sqft - ${price}',
},
],
data: [{ key: 'prop1', address: '123 Main St', beds: 3, baths: 2.5, sqft: 2200, price: 750000 }],
};
dcupl.models.set(propertyModel);
await dcupl.init();
const propertyList = dcupl.lists.create({ modelKey: 'Property' });
const properties = propertyList.catalog.query.items();
console.log(properties[0].listing);
// '123 Main St - 3bd/2.5ba, 2200 sqft - $750000'Example 23: Course Titles
const courseModel: ModelDefinition = {
key: 'Course',
properties: [
{ key: 'code', type: 'string' },
{ key: 'number', type: 'int' },
{ key: 'title', type: 'string' },
{ key: 'credits', type: 'int' },
// Course identifier
{
key: 'courseId',
type: 'string',
expression: '${code} ${number}',
},
// Full course name
{
key: 'fullName',
type: 'string',
expression: '${courseId}: ${title} (${credits} credits)',
},
],
data: [{ key: 'c1', code: 'CS', number: 101, title: 'Introduction to Programming', credits: 4 }],
};
dcupl.models.set(courseModel);
await dcupl.init();
const courseList = dcupl.lists.create({ modelKey: 'Course' });
const courses = courseList.catalog.query.items();
console.log(courses[0].courseId); // 'CS 101'
console.log(courses[0].fullName);
// 'CS 101: Introduction to Programming (4 credits)'Example 24: Flight Information
const flightModel: ModelDefinition = {
key: 'Flight',
properties: [
{ key: 'airline', type: 'string' },
{ key: 'flightNumber', type: 'string' },
{ key: 'origin', type: 'string' },
{ key: 'destination', type: 'string' },
{ key: 'departureTime', type: 'string' },
// Flight code
{
key: 'flightCode',
type: 'string',
expression: '${airline} ${flightNumber}',
},
// Flight info
{
key: 'flightInfo',
type: 'string',
expression: '${flightCode}: ${origin} -> ${destination} (${departureTime})',
},
],
data: [
{
key: 'f1',
airline: 'UA',
flightNumber: '123',
origin: 'SFO',
destination: 'JFK',
departureTime: '10:30 AM',
},
],
};
dcupl.models.set(flightModel);
await dcupl.init();
const flightList = dcupl.lists.create({ modelKey: 'Flight' });
const flights = flightList.catalog.query.items();
console.log(flights[0].flightInfo);
// 'UA 123: SFO → JFK (10:30 AM)'Example 25: Issue Tracking
const issueModel: ModelDefinition = {
key: 'Issue',
properties: [
{ key: 'projectKey', type: 'string' },
{ key: 'issueNumber', type: 'int' },
{ key: 'title', type: 'string' },
{ key: 'priority', type: 'string' },
{ key: 'assignee', type: 'string' },
// Issue key
{
key: 'issueKey',
type: 'string',
index: true,
expression: '${projectKey}-${issueNumber}',
},
// Issue summary
{
key: 'summary',
type: 'string',
expression: '[${issueKey}] ${title} (${priority}) - ${assignee}',
},
],
data: [
{
key: 'i1',
projectKey: 'PROJ',
issueNumber: 123,
title: 'Fix login bug',
priority: 'High',
assignee: 'Alice',
},
],
};
dcupl.models.set(issueModel);
await dcupl.init();
const issueList = dcupl.lists.create({ modelKey: 'Issue' });
const issues = issueList.catalog.query.items();
console.log(issues[0].issueKey); // 'PROJ-123'
console.log(issues[0].summary);
// '[PROJ-123] Fix login bug (High) - Alice'Best Practices
1. Enable Filtering on Expression Properties
// ✅ Good: Make expression properties filterable
{
key: 'fullName',
type: 'string',
expression: '${firstName} ${lastName}',
}
// ❌ Bad: Missing filter flag
{
key: 'fullName',
type: 'string',
expression: '${firstName} ${lastName}',
}2. Use Clear Variable Names
// ✅ Good: Clear template
expression: '${firstName} ${lastName}';
// ❌ Bad: Unclear variables
expression: '${a} ${b}';3. Keep Templates Simple
// ✅ Good: Simple, readable
expression: '${brand} ${name} - ${model}';
// ⚠️ OK but complex
expression: '${category.type} > ${category.name} > ${brand.name} ${name} (${sku})';4. Consider Performance
Expression properties are computed at init and during updates. Complex expressions with many variables have minimal performance impact.
5. Use for Display, Not Logic
// ✅ Good: Display purposes
{
key: 'displayName',
type: 'string',
expression: '${firstName} ${lastName}',
}
// ❌ Bad: Don't use for calculations
{
key: 'total',
type: 'string',
expression: '${price} * ${quantity}', // Won't calculate!
}Troubleshooting
Expression Not Evaluating
Problem: Expression shows literal template
console.log(product.displayName); // '${brand} ${name}'Solution: Call dcupl.init():
await dcupl.init(); // Must init before accessing expressionsUndefined in Expression
Problem: Expression shows undefined values
console.log(product.fullTitle); // 'Apple undefined'Solution: Check source properties exist:
console.log(product.name); // Verify source propertyReference Property Not Accessible
Problem: Can't access reference properties
expression: '${brand.name} ${name}'; // brand.name is undefinedSolution: Ensure reference is resolved:
await dcupl.init(); // Resolves references firstExpression Too Complex
Problem: Very long expression is hard to maintain
Solution: Break into multiple expressions:
// Instead of one huge expression
{
key: 'description',
expression: '${a} ${b} ${c} ${d} ${e} ${f} ${g}',
}
// Create intermediate expressions
{
key: 'partOne',
expression: '${a} ${b} ${c}',
},
{
key: 'partTwo',
expression: '${d} ${e}',
},
{
key: 'description',
expression: '${partOne} - ${partTwo} ${f} ${g}',
}What's Next?
- Derived Properties - Compute values from references
- Models - Properties and references configuration
- Quality & Validation - Validate expressions
- Performance - Optimization strategies