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/faad-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 constraintsRangedProperty<T>- Read-only numbers with constraints
Supported Types:
string- Text valuesnumber- Numeric values (prefer RangedCommandedProperty if constrained)boolean- True/false flagsEnumType- 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.faad.runner/simulations//server/com.faad.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 Phoenix 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
Scan to join WeChat group