Claude Code Plugins

Community-maintained marketplace

Feedback

Create Entity ViewModel

@biggs3d/Tools
0
0

Create a new entity ViewModel with property ViewModels following Phoenix MVVM patterns. Use when adding new data models, creating entity wrappers, or scaffolding ViewModels for entities. Handles property VM creation, label converters, base class selection, and test generation.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name Create Entity ViewModel
description Create a new entity ViewModel with property ViewModels following Phoenix MVVM patterns. Use when adding new data models, creating entity wrappers, or scaffolding ViewModels for entities. Handles property VM creation, label converters, base class selection, and test generation.
allowed-tools Read, Write, Edit, Grep, Glob

Create Entity ViewModel

This skill scaffolds a complete entity ViewModel following Phoenix framework patterns.

When to Use

  • Creating a new entity ViewModel for a data model
  • Wrapping an existing model with UI logic
  • Adding property ViewModels for entity properties
  • Setting up label converters for enum types

Prerequisites

  • Data model must exist in /model/*.model/src/ and be generated
  • Run npm run generate-model if model was just created
  • Entity should be defined with proper property types (ValueProperty, CommandedProperty, etc.)

Process

Step 1: Understand the Data Model

Read the source model to understand:

  • Property types (ValueProperty, CommandedProperty, RangedCommandedProperty)
  • Enum types that need label converters
  • Geographic properties (lat/lon/alt)
  • Parent class (Entity, GeoEntity, Platform, etc.)

Reference: ENTITY_ARCHITECTURE.md

Step 2: Choose the Right Base Class

Determine appropriate base class:

  • EntityViewModel - Standard entities (settings, configs)
  • GeoEntityBaseVM - Entities with location data (platforms, vehicles)
  • GeoPointBaseVM - Simple geographic points (waypoints, markers)
  • Custom abstract base - Multiple related entities share functionality

Reference: ENTITY_ARCHITECTURE.md - Best Practices

Step 3: Create Property ViewModels

For each property on the model, create appropriate property VM:

Property Type Mapping:

  • ValueProperty<string>StringViewModel
  • CommandedProperty<string>CommandedStringViewModel
  • ValueProperty<number>NumberViewModel
  • RangedCommandedProperty<number>RangedCommandedNumberViewModel
  • CommandedProperty<boolean>CommandedBooleanViewModel
  • CommandedProperty<EnumType>CommandedEnumViewModel<EnumType>
  • ValueProperty<string[]>ArrayViewModel<string>
  • CommandedProperty<string[]>CommandedArrayViewModel<string>

CRITICAL Patterns:

  • Use CommandedArrayViewModel, NOT ArrayViewModel for commanded arrays
  • Use RangedCommandedNumberViewModel for numbers with min/max, NOT CommandedNumberViewModel
  • Always add type hints for labelConverter: (value: number | null | undefined) => string
  • Return '---' for null/undefined values in labelConverter

@computed Decorator Usage:

Use @computed when getter:

  • Includes configuration (labelConverter, defaultValue, etc.) - prevents re-running configure
  • Derives/computes values from observables
  • Filters or transforms data
// ✅ With configuration - use @computed
@computed
get modeVM(): ICommandedVM<ModeType, IEnumFormatOptions<ModeType>> {
    const vm = this.createPropertyVM('mode', CommandedEnumViewModel<ModeType>);
    vm.configure({
        labelConverter: ModeTypeLabel,
        defaultValue: ModeType.DEFAULT
    });
    return vm;
}

// ✅ Simple forwarding - @computed optional (micro-optimization)
get nameVM(): IPropertyVM<string, IStringFormatOptions> {
    return this.createPropertyVM('name', CommandedStringViewModel);
}

// ✅ Derived values - @computed required
@computed
get activeItems(): EntityViewModel[] {
    return this.items.filter(item => item.isActive);
}

Reference: ENTITY_ARCHITECTURE.md - Property ViewModel Patterns

Step 4: Create Label Converters for Enums

For each enum type, create a label converter in adapters/types.ts:

export const YourEnumTypeLabel: Record<YourEnumType, string> = {
    [YourEnumType.VALUE1]: 'Display Label 1',
    [YourEnumType.VALUE2]: 'Display Label 2',
};

Reference: ENTITY_ARCHITECTURE.md - Label Converter Creation

Step 5: Implement Required Methods

All entity ViewModels must implement:

  • getEntityClassName() - Returns ModelClass.class
  • getEntityCtr() - Returns the model constructor

Reference: COOKBOOK_PATTERNS_ENHANCED.md - Complete Entity ViewModel

Standard Import Pattern:

// Framework interfaces and types
import { IEntityConstructor, IFrameworkServices, IPropertyVM, ICommandedVM, IEnumFormatOptions, IStringFormatOptions, INumberFormatOptions } from '@tektonux/framework-api';

// Entity ViewModel base and property VMs (from phoenix-core)
import { EntityViewModel, CommandedStringViewModel, CommandedEnumViewModel, RangedCommandedNumberViewModel, CommandedArrayViewModel } from '@tektonux/phoenix-core';

// MobX decorators
import { computed, makeObservable } from 'mobx';

// Model and types
import { MyEntity } from '@tektonux/your-model-package';
import { MyEnumType } from '@tektonux/your-model-package';

// Label converters (from adapters)
import { MyEnumTypeLabel } from '../adapters/types';

Step 6: Add MobX Support

CRITICAL: Call makeObservable(this) in constructor!

constructor(services: IFrameworkServices) {
    super(services);
    makeObservable(this);  // REQUIRED for reactivity
}

Reference: MOBX_ESSENTIALS.md - Constructor Pattern

Step 7: Update Barrel Exports

Add to {lib}.core/src/index.ts:

export * from './lib/viewModels/yourEntityViewModel';

Add label converters to {lib}.core/src/lib/adapters/index.ts:

export * from './types';

Step 8: Create Tests

Generate unit tests following patterns:

  • Mock IFrameworkServices
  • Test property VM creation
  • Test getEntityClassName/getEntityCtr
  • Test label converters

Reference: TESTING_GUIDE.md

Common Pitfalls to Avoid

Reference: COMMON_PITFALLS.md

  1. ❌ Don't use BaseEntityViewModel - Use EntityViewModel from phoenix-core
  2. ❌ Don't use ArrayViewModel for commanded arrays - Use CommandedArrayViewModel
  3. ❌ Don't forget makeObservable(this) in constructor
  4. ❌ Don't use CommandedNumberViewModel for constrained numbers - Use RangedCommandedNumberViewModel
  5. ❌ Don't hardcode labels - Create label converters in adapters
  6. ❌ Don't forget type hints on labelConverter functions
  7. ❌ Don't return 'N/A' for null - Use '---'
  8. ❌ Don't forget @computed on property VMs with configuration or derived values

Complete Template

Reference: COOKBOOK_PATTERNS_ENHANCED.md - Complete Entity ViewModel

File Locations

  • Entity ViewModels: {lib}.core/src/lib/viewModels/
  • Label Converters: {lib}.core/src/lib/adapters/types.ts
  • Tests: {lib}.core/src/lib/viewModels/__tests__/

Verification Steps

After creation:

  1. Run ./tools/build-helpers/count-client-errors.sh - Should be 0
  2. Run npm test - New tests should pass
  3. Verify exports in index.ts
  4. Check label converters work in UI components

Ask User If Unclear

  • Which library to create the VM in (faad.core, alpha.core, etc.)
  • Whether this is a geographic entity (needs GeoEntityBaseVM)
  • Default values for enum properties
  • Whether to create abstract base class (if multiple similar entities)