View Inheritance
View inheritance allows modules to extend existing views without modifying the original files.
How It Works
Section titled “How It Works”- Create a new view with
inherited_viewpointing to the parent - Define
operationsto add, modify, or remove elements - Use semantic targeting to find elements by their attributes
Basic Example
Section titled “Basic Example”Extend an existing form view:
{ "data_type": "UiView", "identifier": "contact_twitter_extension", "type": "Form", "model": "Contact", "inherited_view": "contact_form_view", "arch": { "operations": [ { "action": "add", "target": {"field": "email"}, "position": "after", "value": { "type": "field", "name": "twitter_handle", "properties": {"widget": "TextInput"} } } ] }}Semantic Targeting
Section titled “Semantic Targeting”Two patterns to remember:
1. Shortcuts (for common elements)
Section titled “1. Shortcuts (for common elements)”Use element-specific keys that match by the element’s natural identifier:
| Target | Element Type | Matches by |
|---|---|---|
{"field": "email"} | field | name attribute |
{"tab": "Details"} | tab | title attribute |
{"accordion": "Shipping"} | accordion | title attribute |
{"fieldset": "Address"} | fieldset | title attribute |
{"paper": "Customer Info"} | paper | title attribute |
{"button": "confirm"} | actionButton | properties.method |
{"statusbar": "state"} | statusbar | name attribute |
{"linkButton": "view_all"} | linkButton | name attribute |
2. Generic (for any element)
Section titled “2. Generic (for any element)”Use type-based targeting when shortcuts aren’t available:
| Target | Description |
|---|---|
{"type": "pageTabs"} | First element of that type |
{"type": "tab", "name": "crm"} | Element matching type AND all other attributes |
{"anchor": "my_section"} | Any element with that anchor attribute |
Examples
Section titled “Examples”Target a field:
{"target": {"field": "email"}}Target a tab:
{"target": {"tab": "Contact Info"}}Target an accordion:
{"target": {"accordion": "Advanced Settings"}}Target a fieldset:
{"target": {"fieldset": "Billing Address"}}Target a paper section:
{"target": {"paper": "Customer Details"}}Target by anchor (for elements without natural identifiers):
// In parent view{ "type": "row", "anchor": "customer_section", "content": [...]}
// In child view{"target": {"anchor": "customer_section"}}Target by type + attributes:
{"target": {"type": "tab", "name": "settings"}}String Shorthand
Section titled “String Shorthand”For convenience, you can use colon-separated strings:
"target": "field:email""target": "tab:Details""target": "accordion:Shipping""target": "fieldset:Address""target": "anchor:customer_section"Operations
Section titled “Operations”Insert a new element.
{ "action": "add", "target": {"field": "email"}, "position": "after", "value": { "type": "field", "name": "phone", "properties": {"widget": "TextInput"} }}Position Options
Section titled “Position Options”| Position | Description |
|---|---|
after | Insert after target (default) |
before | Insert before target |
inside | Append to target’s content |
first | Insert as first child of target |
modify
Section titled “modify”Update element properties.
{ "action": "modify", "target": {"field": "description"}, "value": { "properties": { "widget": "RichTextEditor", "required": true } }}replace
Section titled “replace”Replace an element entirely.
{ "action": "replace", "target": {"field": "old_field"}, "value": { "type": "field", "name": "new_field", "properties": {"widget": "TextInput"} }}remove
Section titled “remove”Delete an element.
{ "action": "remove", "target": {"field": "deprecated_field"}}Real Examples
Section titled “Real Examples”Adding Google Integration Fields
Section titled “Adding Google Integration Fields”{ "data_type": "UiView", "identifier": "google_integration_form_view", "type": "Form", "model": "Integration", "inherited_view": "integration_form_view", "arch": { "operations": [ { "action": "add", "target": {"field": "image"}, "position": "after", "value": { "type": "row", "content": [ { "type": "column", "span": 6, "content": [ { "type": "field", "name": "google_client_id", "properties": { "widget": "TextInput", "label": "Google Client ID" } }, { "type": "field", "name": "google_client_secret", "properties": { "widget": "TextInput", "label": "Google Client Secret" } } ] } ] } } ] }}Adding Action Button
Section titled “Adding Action Button”{ "data_type": "UiView", "identifier": "meeting_sync_extension", "type": "Calendar", "model": "Meeting", "inherited_view": "meeting_calendar_view", "arch": { "operations": [ { "action": "add", "target": {"field": "name"}, "position": "before", "value": { "type": "actionButton", "properties": { "label": "Sync Calendar", "icon": "RefreshCw", "method": "sync_calendar" } } } ] }}Adding Tab Content
Section titled “Adding Tab Content”{ "arch": { "operations": [ { "action": "add", "target": {"tab": "Details"}, "position": "inside", "value": { "type": "field", "name": "custom_notes", "properties": {"widget": "TextArea"} } } ] }}Index-Based Targeting
Section titled “Index-Based Targeting”Use when semantic targeting isn’t possible (e.g., targeting a specific row or column):
{ "action": "add", "target": "arch[1].content[0].content[5]", "value": {...}}Trade-offs:
- ✅ Can target any element, even without identifiers
- ⚠️ Fragile - breaks if parent view structure changes
- ⚠️ Harder to read and maintain
Tip: Prefer semantic targeting when possible. Use index paths for structural elements like row or column that lack natural identifiers, or add an anchor attribute to the parent view for stable targeting.
Multiple Operations
Section titled “Multiple Operations”Apply multiple changes:
{ "arch": { "operations": [ { "action": "add", "target": {"field": "email"}, "position": "after", "value": {"type": "field", "name": "linkedin_url", ...} }, { "action": "add", "target": {"field": "linkedin_url"}, "position": "after", "value": {"type": "field", "name": "twitter_handle", ...} }, { "action": "modify", "target": {"field": "phone"}, "value": {"properties": {"required": true}} } ] }}Best Practices
Section titled “Best Practices”- Always use semantic targeting - Never use index-based paths for new code
- Add anchors to parent views - For stable targeting of complex sections
- Keep operations focused - Each inherited view should have a specific purpose
- Test after parent changes - Verify inherited views still work
- Use descriptive identifiers -
contact_google_extensionnotcontact_ext_1
Next Steps
Section titled “Next Steps”- Form Views - Creating form views
- Creating Modules - Module development