Add Model Property
This skill walks through the complete workflow of adding a property to a data model, including generation and propagation to client/server.
When to Use
- •Adding a new field to an existing entity
- •Extending a model with additional properties
- •Adding enum types to models
- •Modifying entity data structures
CRITICAL Warning
NEVER edit generated model files! Only edit source models in /model/*.model/src/
Generated files (DO NOT EDIT):
- •
/client/libs/*/model/❌ - •
/server/com.*.model/❌
Source files (EDIT HERE):
- •
/model/*.model/src/✅
Reference: MODEL_GENERATION_GUIDE.md - Critical Concepts
Process
Step 1: Locate the Source Model
Find the model file in /model/*/src/:
# Example: Find InitConfig model
grep -r "export class InitConfig" /workspaces/{{projectName}}-hmi/model/*/src/
Step 2: Choose Property Type
Determine the appropriate property type:
Property Types:
- •
ValueProperty<T>- Read-only from server (timestamps, computed values) - •
CommandedProperty<T>- User can request changes (settings, modes) - •
RangedCommandedProperty<T>- Numbers with min/max constraints - •
RangedProperty<T>- Read-only numbers with constraints
Supported Types:
- •
string- Text values - •
number- Numeric values (prefer RangedCommandedProperty if constrained) - •
boolean- True/false flags - •
EnumType- Custom enum types (must be defined separately) - •
string[]- Arrays of entity IDs (ONLY string arrays supported!) - •
Date- Timestamps (rare, usually use number for epoch)
Array Limitations (CRITICAL):
- •✅
string[]- Supported (for entity ID lists) - •❌
number[]- NOT supported by generator - •❌
boolean[]- NOT supported by generator - •❌
EnumType[]- NOT supported by generator
Workaround for unsupported arrays: Create wrapper entities
// Instead of: values?: CommandedProperty<number[]>
// Create wrapper entity:
export class NumberValue extends Entity {
value?: CommandedProperty<number>;
}
// Then use: valueIds?: CommandedProperty<string[]>
Reference: MODEL_GENERATION_GUIDE.md - Common Property Types
Step 3: Choose Property Name (CRITICAL)
Include units in property names! Comments don't transfer during generation.
Good Names:
- •
contourLineWidthPixels(notcontourLineWidth) - •
targetAltitudeMeters(nottargetAltitude) - •
speedKnots(notspeed) - •
brightnessPercentFraction(notbrightness) - •
intervalSeconds(notinterval)
Common Unit Suffixes:
- •Distance:
Meters,Kilometers,Feet,Miles - •Screen:
Pixels,Rem - •Angles:
Degrees,Radians - •Time:
Seconds,Milliseconds,Minutes - •Speed:
Knots,Mph,Kph,MetersPerSecond - •Fractions:
PercentFraction(0.0-1.0) - •Multipliers:
Multiplier(e.g.,terrainExaggerationMultiplier)
Reference: MODEL_GENERATION_GUIDE.md - Best Practices
Step 4: Edit Source Model
- •Add property to class
- •Add property name to
"members"array in@descriptioncomment - •Import any new types
Example:
/**
* @description {
* "clazz" : {
* "extends": ["Entity"],
* "members": [
* "class",
* "existingProp1",
* "existingProp2",
* "newPropertyName" // ← Add to members array!
* ]
* }
* }
*/
export class MyModel extends Entity {
/**
* @default quicktype.projectname.MyModel
*/
public static class: string = "quicktype.MyModel";
existingProp1?: ValueProperty<string>;
existingProp2?: CommandedProperty<number>;
newPropertyName?: CommandedProperty<EnumType>; // ← New property
constructor(id: string) {
super(id);
this.className = MyModel.class;
}
}
CRITICAL Class Name Pattern:
/** * @default quicktype.projectname.ClassName // ✅ Include package */ public static class: string = "quicktype.ClassName"; // ❌ NO package
Reference: MODEL_GENERATION_GUIDE.md - Class/Entity Types
Step 5: Create Enum Type (If Needed)
If adding an enum property, create the enum file:
File: /model/projectname.model/src/yourEnumType.ts
/**
* @description {
* "clazz" : {
* "enum": "VALUE1,VALUE2,VALUE3",
* "title":"YourEnumType"
* }
* }
* @type integer
*/
export enum YourEnumType {
VALUE1 = 0,
VALUE2 = 1,
VALUE3 = 2
}
CRITICAL: Enum values must be globally unique across ALL enums!
- •❌ WRONG: Multiple enums with
UNKNOWN(generator collision!) - •✅ CORRECT: Use prefixes like
UNKNOWN_KIND,UNKNOWN_SOURCE
Reference: MODEL_GENERATION_GUIDE.md - Enum Types
Step 6: Run Model Generation
npm run generate-model
This generates:
- •TypeScript models in
/client/libs/*/model/ - •C# models in
/server/com.*.model/
Check for errors - Common issues:
- •Missing property in
membersarray - •Enum not defined
- •Invalid property type
- •Circular dependencies
Reference: MODEL_GENERATION_GUIDE.md - Model Generation Workflow
Step 7: Update Entity ViewModel
IMPORTANT: Property changes flow through both client and server. See full data flow in CLIENT_SERVER_BRIDGE.md.
Add property ViewModel getter:
@computed
get newPropertyNameVM(): ICommandedVM<EnumType, IEnumFormatOptions<EnumType>> {
const vm = this.createPropertyVM('newPropertyName', CommandedEnumViewModel<EnumType>);
vm.configure({
labelConverter: YourEnumTypeLabel,
defaultValue: YourEnumType.VALUE1
});
return vm;
}
@computed Usage: Use @computed when getter includes configuration or derives values.
Create label converter in adapters/types.ts:
export const YourEnumTypeLabel: Record<YourEnumType, string> = {
[YourEnumType.VALUE1]: 'Display Label 1',
[YourEnumType.VALUE2]: 'Display Label 2',
};
References:
- •ENTITY_ARCHITECTURE.md - Property ViewModel Patterns
- •CLIENT_SERVER_BRIDGE.md - Cross-stack property lifecycle
Step 8: Update Server-Side Code (C#)
If this entity has server-side simulation/builder code:
- •Find builder class (usually
*Builder.cs) - •Add property initialization
- •Update any simulation logic that uses the entity
Example locations:
- •
/server/com.{{projectName}}.runner/simulations/ - •
/server/com.{{projectName}}.runner/builders/
Reference: SERVER.md
Step 9: Verify Build
# Check client-side errors ./tools/build-helpers/count-client-errors.sh ./tools/build-helpers/show-client-errors.sh 10 # Check server-side errors ./tools/build-helpers/count-server-errors.sh ./tools/build-helpers/show-server-errors.sh 10
All errors should be resolved.
Step 10: Update UI Components
If the property should be visible/editable in UI:
- •Find the view component
- •Add input component bound to property VM
- •Use appropriate {{sharedLib}} component (Select, TextInput, Checkbox, etc.)
Reference: UI_COMPONENT_GUIDELINES.md
Common Pitfalls
Reference: COMMON_PITFALLS.md
- •❌ Editing generated files instead of source
- •❌ Forgetting to add property to
membersarray - •❌ Using unsupported array types (
number[],boolean[]) - •❌ Not including units in property names
- •❌ Duplicate enum values across different enums
- •❌ Forgetting to run
npm run generate-model - •❌ Missing label converter for enum types
Verification Checklist
- • Source model edited in
/model/*/src/ - • Property added to
membersarray - • Enum created (if applicable) with unique values
- •
npm run generate-modelran successfully - • Entity ViewModel updated with property VM
- • Label converter created (for enums)
- • Server builder updated (if applicable)
- • Build passes (0 client/server errors)
- • UI component updated (if user-facing)
- • Tests updated
Ask User If Unclear
- •Property type (Value, Commanded, Ranged)
- •Default value for the property
- •Whether property should be visible/editable in UI
- •Units for numeric properties
- •Display labels for enum values