Skip to main content

BOX Template Architecture

Introduction​

BOX templates form a crucial abstraction layer in the Ampersand prototype framework, serving as the bridge between domain logic expressed in Ampersand scripts and the user interface components rendered in the browser. Understanding their architecture is essential for framework contributors and advanced users who need to extend the system's presentation capabilities.

Conceptual Foundation​

What are BOX Templates?​

BOX templates are HTML template files that define how interface components are rendered in the frontend. They transform abstract interface specifications from Ampersand models into concrete Angular components with specific visual layouts and interaction patterns.

In Ampersand, a BOX represents a structured interface element that can contain other interface elements. The template system allows these abstract BOX specifications to be rendered as different visual presentations:

  • BOX\<FORM> → Renders as form layout
  • BOX\<TABLE> → Renders as tabular data
  • BOX\<TABS> → Renders as tabbed interface
  • BOX\<FILTEREDDROPDOWN> → Renders as filtered selection component

The Template Variable System​

BOX templates use a template variable substitution system where placeholders like $name$, $label$, and $crud$ are replaced with actual values from the Ampersand model during compilation. This creates a parameterized template system that maintains separation between:

  • Structure (defined in the template)
  • Content (provided by the Ampersand model)
  • Behavior (implemented in Angular components)

Architectural Principles​

1. Separation of Concerns​

BOX templates embody clear separation between different layers of the application:

  • Domain Logic: Expressed in Ampersand (.adl) files
  • Presentation Structure: Defined in HTML templates
  • Interactive Behavior: Implemented in Angular TypeScript components
  • Visual Styling: Handled by CSS/SCSS

2. Reusability and Composability​

Templates are designed to be:

  • Reusable across different interfaces and contexts
  • Composable with other templates and components
  • Parameterizable through the template variable system

3. Framework Integration​

BOX templates integrate seamlessly with:

  • Ampersand Compiler: Templates are processed during compilation
  • Angular Framework: Generated output works with Angular's component system
  • PrimeNG Components: Templates can leverage the UI component library

Purpose and Problem Domain​

The Interface Generation Challenge​

Ampersand scripts describe business logic and data relationships but remain agnostic about presentation. The challenge is transforming these abstract specifications into usable interfaces while:

  • Maintaining type safety between backend and frontend
  • Preserving business rules and constraints
  • Allowing customization of presentation
  • Supporting different interaction patterns

Why BOX Templates Exist​

BOX templates solve several critical problems:

  1. Presentation Flexibility: Different business contexts require different interface patterns
  2. Maintainability: Changes to interface structure shouldn't require recompiling business logic
  3. Customization: Organizations need to adapt interfaces to their specific workflows
  4. Evolution: New interaction patterns can be added without framework modifications

Template Categories​

Core Structural Templates​

  • BOX\<FORM>: For data entry and editing workflows
  • BOX\<TABLE>: For displaying and managing collections
  • BOX\<TABS>: For organizing complex information hierarchies
  • BOX\<RAW>: For minimal, unstyled presentations

Specialized Interaction Templates​

  • BOX\<FILTEREDDROPDOWN>: For constrained selection with business rule filtering
  • Custom templates: Organization-specific interaction patterns

Component Architecture Deep-Dive​

The Inheritance Hierarchy​

BOX templates leverage a sophisticated object-oriented component architecture in the Angular frontend:

BaseAtomicComponent
├── BaseBoxComponent (for structural components)
│ ├── BoxFormComponent
│ ├── BoxTableComponent
│ ├── BoxTabsComponent
│ └── BoxRawComponent
└── AtomicComponents (for data input/display)
├── AtomicObjectComponent
├── AtomicAlphanumericComponent
├── AtomicBooleanComponent
└── AtomicDateComponent

Shared Base Functionality​

All atomic components inherit from BaseAtomicComponent, which provides:

  • CRUD rights management: Create, Read, Update, Delete permissions
  • Constraint handling: isUni (univalent) and isTot (total) relation support
  • Common operations: addItem(), removeItem() for non-univalent components
  • Data binding: Resource binding and property management
  • Labels and identifiers: Consistent labeling and identification

Template-to-Component Mapping​

BOX templates use Angular component selectors to map HTML elements to TypeScript components:

<!-- In your BOX template -->
<app-atomic-object></app-atomic-object>
// In the Angular component
@Component({
selector: 'app-atomic-object', // This creates the mapping
templateUrl: './atomic-object.component.html'
})
export class AtomicObjectComponent extends BaseAtomicComponent {
// Component implementation
}

Advanced Implementation: SelectOptions Pattern​

The transcript revealed how filtering mechanisms work in practice. Here's the technical implementation:

// In AtomicObjectComponent
@Input() selectOptions?: any[]; // Optional filtered options

// In the component logic
if (this.selectOptions) {
// Use filtered set when selectOptions provided
this.availableOptions = this.selectOptions;
} else {
// Use complete set when no filtering
this.availableOptions = this.allOptions;
}

This pattern allows templates like FILTEREDDROPDOWN to provide pre-filtered data while maintaining compatibility with standard object selection.

Design Considerations​

When to Create Custom Templates​

Consider creating a custom BOX template when:

  • Existing templates don't match business workflow requirements
  • Specific user interaction patterns are needed repeatedly
  • Performance optimization requires specialized rendering
  • Integration with external systems demands custom interfaces

When to Use Existing Templates​

Prefer existing templates when:

  • Standard interaction patterns meet requirements
  • Maintenance overhead should be minimized
  • Consistency across the application is prioritized
  • Development speed is more important than customization

Leveraging Component Inheritance​

When creating custom templates, consider:

  • Extending existing components: Inherit from BaseAtomicComponent or BaseBoxComponent
  • Reusing selector patterns: Use established component selectors in your templates
  • Maintaining shared interfaces: Preserve common input/output patterns
  • Following naming conventions: Stick to app-{component-type} selector naming

Template Variable Architecture​

Core Variables​

All BOX templates have access to fundamental variables:

  • $name$: Property identifier from the Ampersand model
  • $label$: Human-readable display text
  • $crud$: Create, Read, Update, Delete permissions
  • $target$: Target concept for relation-based properties

Conditional Variables​

Templates support conditional logic:

  • $if(exprIsUni)$isUni$endif$: For univalent relations
  • $if(exprIsTot)$isTot$endif$: For total relations

BOX-header annotations​

Every annotation a scripter writes in a BOX header — for example BOX<TABLE noHeader title="Catalog"> — is available to the matching template as a StringTemplate attribute. This is generic: the compiler does not know or special-case individual annotation names.

The mechanism lives in renderTemplate (Ampersand, src/Ampersand/Prototype/ProtoUtil.hs):

  1. The BOX header carries a list of key/value pairs (btKeys header). When a box template is rendered (GenAngularFrontend.hs), it is rendered with these pairs: renderTemplate (Just (btKeys header)) template.
  2. setUserAtts turns each pair into a StringTemplate attribute — a bare flag (noHeader) becomes True, a key="value" (title="Catalog") becomes the string value.
  3. fillInTheBlanks then sets every attribute the template references but the scripter did not supply to False.

The practical consequences for template authors:

  • To support a new annotation, you only edit the template (and, if it needs runtime behaviour, the Angular component). No compiler change is required.
  • Reference a flag with $if(myFlag)$ … $endif$ and a string with $if(myTitle)$title="$myTitle$"$endif$. An unreferenced annotation is simply ignored; a referenced-but-absent one is False, so existing scripts keep working.

Exception — component.html. The per-interface wrapper template (component.html, rendered by genComponentFileFromTemplate) is rendered with renderTemplate Nothing, i.e. without the box header's key/values, and it errors on any attribute it references but is not given. Annotations that must affect the interface heading (such as noRootTitle) therefore need a compiler change, not just a template edit.

Resource Binding​

Templates bind to data through:

  • [resource]="resource": The data context
  • [property]="resource.$name$": Specific property binding
  • [selectOptions]="resource.$name$": For filtered options

Integration with Angular Architecture​

Component Hierarchy​

BOX templates generate Angular components that follow a clear hierarchy:

BaseBoxComponent
├── BoxFormComponent
├── BoxTableComponent
├── BoxTabsComponent
└── CustomBoxComponents

Atomic Component Integration​

BOX templates commonly delegate to atomic components:

  • atomic-object: For entity relationships
  • atomic-alphanumeric: For text input
  • atomic-boolean: For binary choices
  • atomic-date: For temporal data

This delegation maintains consistency while allowing BOX-level customization.

Framework Evolution Considerations​

Backwards Compatibility​

New BOX templates should:

  • Not break existing Ampersand scripts
  • Maintain consistent variable naming conventions
  • Follow established Angular component patterns

Extension Points​

The template system provides extension points for:

  • Custom business logic: Through Angular component inheritance
  • Styling customization: Through CSS/SCSS overrides
  • Third-party integration: Through component composition

Conclusion​

BOX templates represent a sophisticated solution to the challenge of generating flexible, maintainable user interfaces from abstract business models. Their architecture balances the need for consistency with the requirement for customization, enabling the Ampersand framework to adapt to diverse organizational needs while maintaining type safety and business rule enforcement.

Understanding this architecture is crucial for anyone extending the framework or creating custom interface patterns that go beyond the standard templates.