@ea-lab/reactive-json-docs
Version:
Complete documentation for Reactive-JSON - Components, examples and LLM-parsable guides
472 lines (412 loc) • 18.8 kB
YAML
renderView:
- type: Markdown
content: |
# ReactiveJsonSubroot
The `ReactiveJsonSubroot` component allows you to render a new Reactive-JSON root inside an existing application. It is useful for embedding a sub-application, isolating a part of the data tree, or rendering a separate rjbuild with its own options.
With the `sharedUpdates` feature, the component can also propagate data changes back to the parent, enabling seamless communication between parent and subroot when working with shared data references.
## Properties
- type: TabbedSerializer
yamlSerializedContent: |
# Complete rjOptions structure.
- type: ReactiveJsonSubroot
# Component-specific properties.
sharedUpdates: true
dataOverrideEvaluationDepth: 10
# Subroot configuration.
rjOptions:
# Loading from URL.
rjBuildUrl: "/path/to/rjbuild.yaml"
rjBuildFetchMethod: "GET" # or "POST".
headersForRjBuild:
Authorization: "Bearer token"
Content-Type: "application/json"
# OR inline definition.
maybeRawAppRjBuild:
renderView:
- type: div
content: "Subroot content."
templates:
myTemplate:
type: span
content: ~.value
data:
initialValue: "Hello World"
# Data replacement.
dataOverride: ~~.userData
# Debug mode.
debugMode: true
# Standard actions.
actions:
- what: hide
when: ~.condition
- type: DefinitionList
content:
- term:
code: rjOptions
after: " (object, required)"
details:
- type: Markdown
content: "Options to pass to the subroot - accepts **all** `ReactiveJsonRoot` properties including:"
- type: DefinitionList
content:
- term:
code: rjOptions.rjBuildUrl
details: "URL to load the RjBuild from."
- term:
code: rjOptions.rjBuildFetchMethod
details: "HTTP method (\"GET\" or \"POST\")."
- term:
code: rjOptions.headersForRjBuild
details: "Headers for the request."
- term:
code: rjOptions.dataOverride
details: "Override data for the loaded/defined RjBuild."
- term:
code: rjOptions.maybeRawAppRjBuild
details:
- type: Markdown
content: "Inline RjBuild content (string or object) containing:"
- type: DefinitionList
content:
- term:
code: rjOptions.maybeRawAppRjBuild.renderView
details: "View definition."
- term:
code: rjOptions.maybeRawAppRjBuild.templates
details: "Template definitions."
- term:
code: rjOptions.maybeRawAppRjBuild.data
details: "Initial data."
- term:
code: rjOptions.debugMode
details: "Enable debug mode and related wrapper components."
- term:
code: sharedUpdates
after: " (boolean, optional)"
details: "Enable upstream data propagation to parent (default: false)."
- term:
code: dataOverrideEvaluationDepth
after: " (number, optional)"
details: "Special evaluation depth for dataOverride property (default: 10)."
- type: Markdown
content: |
### Standard properties
- type: DefinitionList
content:
- term:
code: actions
after: " (array, optional)"
details: "Actions to attach to the subroot."
- type: Markdown
content: |
## Behavior
- Renders a new `ReactiveJsonRoot` with the provided options.
- The subroot is typically isolated from the parent for data, templates, and rendering¹.
- Plugins from the parent are automatically reused in the subroot.
- All properties in `rjOptions` are evaluated with template values.
- If `rjOptions` is not a valid object, nothing is rendered.
¹Note: Data isolation is bypassed when `sharedUpdates: true` is enabled, allowing the subroot to propagate data changes back to the parent. Templates and rendering contexts remain isolated.
## Shared Updates Feature
When `sharedUpdates: true` is enabled, the component automatically detects template references in `dataOverride` and creates update callbacks to propagate changes back to the parent.
### How it works
1. **Automatic analysis**: The system analyzes `dataOverride` to detect references (`~~.`, `~.`, `~>`) to parent data
2. **Callback creation**: For each detected reference, an update callback is created
3. **Update interception**: When the subroot modifies data, the system checks if it corresponds to a parent reference
4. **Propagation**: If so, the update is propagated to the parent instead of being applied locally
### Advantages
1. **Automatic synchronization**: Data remains consistent between parent and subroot
2. **Simplicity**: No need to manually manage update callbacks
3. **Performance**: Avoids unnecessary re-renders by propagating directly to the right level
4. **Flexibility**: Supports different referencing patterns (~~., ~., ~>) and arrays containing references
### Supported vs Unsupported Cases
#### Supported cases
##### Direct reference
```yaml
dataOverride: ~~.user
# Modifications in the subroot propagate to "user" in the parent
```
##### Object mapping with references
```yaml
dataOverride:
userInfo: ~~.user
settings: ~~.config
# Modifications to "userInfo" propagate to "user" in the parent
# Modifications to "settings" propagate to "config" in the parent
```
##### Local references
```yaml
dataOverride: ~.localData
# Modifications propagate to the local template context
```
##### Hierarchical references
```yaml
dataOverride: ~>key.someData
# Modifications propagate up the template hierarchy
```
##### Arrays in dataOverride
```yaml
dataOverride:
- ~~.firstItem
- ~~.secondItem
# Array items with template references are properly handled
```
#### Unsupported cases
##### Nested references in data
```yaml
dataOverride:
someProperty: ~~.user
data:
someProperty: ~~.anotherProperty # Not supported
```
- type: RjBuildDescriber
title: Example
description: This example demonstrates how to embed a separate Reactive-JSON application using ReactiveJsonSubroot. The component loads and renders an independent rjbuild file, creating an isolated sub-application within the current view.
toDescribe:
renderView:
- type: ReactiveJsonSubroot
rjOptions:
rjBuildUrl: "/rjbuild/component/message.yaml"
- type: RjBuildDescriber
title: Example with inline JSON data
description: This example demonstrates two approaches for using ReactiveJsonSubroot with inline data. The first shows passing JSON data directly as a string via maybeRawAppRjBuild. The second shows loading subroot options dynamically from the main RjBuild's data using template interpolation. Both approaches are useful for embedding sub-applications without external files.
toDescribe:
renderView:
- type: ReactiveJsonSubroot
rjOptions:
maybeRawAppRjBuild: '{"data":{"userName":"Alice","messageCount":3},"renderView":[{"type":"div","attributes":{"style":{"padding":"1rem","border":"1px solid #ddd","borderRadius":"8px","backgroundColor":"#f9f9f9"}},"content":[{"type":"h3","content":"~~.userName"},{"type":"p","content":["You have ","~~.messageCount"," new messages."]},{"type":"button","content":"Mark as read","attributes":{"style":{"backgroundColor":"#007bff","color":"white","border":"none","padding":"0.5rem 1rem","borderRadius":"4px"}}}]}]}'
- type: hr
- type: ReactiveJsonSubroot
rjOptions: ~~.subrootToLoad
data:
subrootToLoad:
maybeRawAppRjBuild:
renderView:
- type: div
content: This second subroot is loaded from the "data" key of the main root.
- type: RjBuildDescriber
title: Example with Shared Updates
description: This example demonstrates the `sharedUpdates` feature where changes in the subroot automatically propagate back to the parent data. Notice how modifying the text field in the subroot will update the display in the parent above it.
toDescribe:
data:
user:
name: "John Doe"
email: "john@example.com"
renderView:
- type: div
attributes:
style:
padding: "1rem"
border: "2px solid #007bff"
borderRadius: "8px"
marginBottom: "1rem"
content:
- type: h4
content: "Parent Data Display"
- type: p
content: ["Name: ", ~~.user.name]
- type: p
content: ["Email: ", ~~.user.email]
- type: ReactiveJsonSubroot
sharedUpdates: true
rjOptions:
dataOverride: ~~.user
maybeRawAppRjBuild:
renderView:
- type: div
attributes:
style:
padding: "1rem"
border: "2px solid #28a745"
borderRadius: "8px"
content:
- type: h4
content: "Subroot Editor (with sharedUpdates)"
- type: TextField
label: "Name"
dataLocation: ~.name
- type: TextField
label: "Email"
dataLocation: ~.email
- type: p
attributes:
style:
fontSize: "0.9em"
color: "#666"
marginTop: "0.5rem"
content: "Changes here automatically update the parent data above ↑"
- type: RjBuildDescriber
title: Example with Multi-Section Configuration
description: This example shows how `sharedUpdates` works with complex data mappings where different parts of the parent data are mapped to different sections in the subroot.
toDescribe:
data:
profile:
name: "Alice Smith"
age: 28
settings:
theme: "dark"
language: "en"
renderView:
- type: div
attributes:
style:
display: "grid"
gridTemplateColumns: "1fr 1fr"
gap: "1rem"
marginBottom: "1rem"
content:
- type: div
attributes:
style:
padding: "1rem"
border: "1px solid #ddd"
borderRadius: "4px"
content:
- type: h5
content: "Profile Data"
- type: p
content: ["Name: ", ~~.profile.name]
- type: p
content: ["Age: ", ~~.profile.age]
- type: div
attributes:
style:
padding: "1rem"
border: "1px solid #ddd"
borderRadius: "4px"
content:
- type: h5
content: "Settings Data"
- type: p
content: ["Theme: ", ~~.settings.theme]
- type: p
content: ["Language: ", ~~.settings.language]
- type: ReactiveJsonSubroot
sharedUpdates: true
rjOptions:
dataOverride:
userProfile: ~~.profile
userSettings: ~~.settings
maybeRawAppRjBuild:
renderView:
- type: div
attributes:
style:
display: "grid"
gridTemplateColumns: "1fr 1fr"
gap: "1rem"
padding: "1rem"
border: "2px solid #17a2b8"
borderRadius: "8px"
content:
- type: div
content:
- type: h5
content: "Edit Profile"
- type: TextField
label: "Name"
dataLocation: ~.userProfile.name
- type: NumberField
label: "Age"
dataLocation: ~.userProfile.age
- type: div
content:
- type: h5
content: "Edit Settings"
- type: TextField
label: "Theme"
dataLocation: ~.userSettings.theme
- type: TextField
label: "Language"
dataLocation: ~.userSettings.language
- type: RjBuildDescriber
title: Example with Array References
description: This example demonstrates how to work with arrays in `dataOverride` where each array item is mapped to different parts of the parent data. The subroot can edit individual array items and changes will be propagated back to their respective locations in the parent array.
toDescribe:
data:
items:
- title: "Task 1"
completed: false
- title: "Task 2"
completed: true
renderView:
- type: div
attributes:
style:
padding: "1rem"
border: "1px solid #ddd"
borderRadius: "4px"
marginBottom: "1rem"
content:
- type: h4
content: "Parent Items Display"
- type: p
content: ["Item 1: ", ~~.items.0.title, " (", ~~.items.0.completed, ")"]
- type: p
content: ["Item 2: ", ~~.items.1.title, " (", ~~.items.1.completed, ")"]
- type: ReactiveJsonSubroot
sharedUpdates: true
rjOptions:
dataOverride:
- ~~.items.0
- ~~.items.1
maybeRawAppRjBuild:
renderView:
- type: div
attributes:
style:
padding: "1rem"
border: "2px solid #ffc107"
borderRadius: "8px"
content:
- type: h4
content: "Array Items Editor"
- type: div
content:
- type: h5
content: "First Item"
- type: TextField
label: "Title"
dataLocation: ~.0.title
- type: CheckBoxField
label: "Completed"
dataLocation: ~.0.completed
- type: div
content:
- type: h5
content: "Second Item"
- type: TextField
label: "Title"
dataLocation: ~.1.title
- type: CheckBoxField
label: "Completed"
dataLocation: ~.1.completed
- type: Markdown
content: |
## Caveat: Data Loss Risk with sharedUpdates
**⚠️ Important Warning**: When using `sharedUpdates`, there is a risk of data loss in specific scenarios.
### The Problem
If the subroot modifies data that is **not** covered by a template reference from the parent's `dataOverride`, and subsequently the parent re-renders the subroot, the local modifications will be **lost**.
### When This Happens
1. **Subroot modifies local data**: The subroot changes a value that doesn't correspond to any template reference in the parent's `dataOverride`.
2. **Parent re-renders**: This can occur when:
- The parent updates itself for any reason.
- A subroot action triggers an upstream update that causes the parent to re-render.
3. **Data loss**: The parent's `dataOverride` completely overwrites the subroot's data, erasing local modifications.
### Best Practices to Avoid Data Loss
1. **Map all needed data**: Ensure all data fields the subroot might modify are included in the parent's data structure.
2. **Use complete dataOverride**: Include all necessary data paths in your `dataOverride` mapping.
3. **Avoid local-only modifications**: Design your data flow so that all user inputs correspond to parent data.
4. **Consider alternatives**: For truly local data, consider using component state or separate data management instead of `sharedUpdates`.
## Limitations
- **Data loss risk**: Local modifications in subroot may be lost when parent re-renders (see Caveat section above).
- **Nested references in data**: References within references are not supported.
- **Circular references**: May cause infinite loops if data structures reference each other.
- **Isolation by default**: Without `sharedUpdates`, the subroot is completely isolated from the parent.
- **Backward compatibility**: `sharedUpdates` is disabled by default to maintain existing behavior.
- type: Markdown
content: |
## Changelog
### reactive-json@0.1.0
- **New**: `sharedUpdates` feature - enables automatic data synchronization between subroot and parent
- **New**: `dataOverride` property - allows complete data replacement in subroot with support for template references
- **New**: Automatic analysis of template references in `dataOverride` for shared updates propagation