Core Concepts
This page explains the fundamental concepts you need to understand when working with Nuraly components.
ComponentElement Interface
Every component in Nuraly is defined as a ComponentElement object with the following structure:
interface ComponentElement {
uuid: string // Unique identifier
name: string // Component name
component_type: ComponentType // Type of component (TextInput, Button, etc.)
// Dynamic properties
input?: Record<string, any> // Input handlers and static input
inputHandlers?: Record<string, string> // Maps input name to handler code
// Events
event?: Record<string, string> // Event handlers (onClick, onChange, etc.)
// Styling
style?: Record<string, string> // Static styles
styleHandlers?: Record<string, string> // Dynamic style handlers
// Hierarchy
childrenIds?: string[] // IDs of child components
children?: ComponentElement[] // Resolved child components
parent?: ComponentElement // Parent component reference
// Runtime
uniqueUUID?: string // Unique runtime UUID
Instance?: any // Component instance state (reactive proxy)
application_id?: string // Application owner
}
Input Handlers vs Static Input
Components have two ways to define properties:
Static Input
Fixed values that don't change at runtime:
input: {
placeholder: { type: "string", value: "Enter text" },
required: { type: "boolean", value: true }
}
Input Handlers
Dynamic values evaluated at runtime:
inputHandlers: {
value: `return Vars.username || '';`,
disabled: `return Vars.isProcessing || Vars.formSubmitted;`
}
Evaluation Process:
- Component mounts in the UI
- Input handlers are evaluated immediately
- Results stored in
inputHandlersValue - Component renders with these values
- On dependency changes, handlers re-evaluate
- Component updates automatically
Priority
When both static input and inputHandlers exist for the same property, inputHandlers take priority:
input: {
value: { type: "string", value: "static" }
},
inputHandlers: {
value: `return Vars.dynamicValue || "static";` // ← This is used
}
Events and EventData
Events trigger handler code in response to user interactions:
event: {
onChange: `
console.log('Value changed to:', EventData.value);
Vars.lastValue = EventData.value;
`,
onFocus: `
Vars.isFocused = true;
`,
onBlur: `
Vars.isFocused = false;
`
}
EventData Structure
Each event provides EventData object containing event-specific information:
// onChange event
EventData = {
value: string // New value
oldValue: string // Previous value
event: Event // Native browser event
}
// onClick event
EventData = {
event: Event // Native browser event
}
Event Execution Context
Event handlers have access to:
// Component context
Current.name // Component name
Current.uuid // Component UUID
Current.Instance // Component runtime state
Current.Instance.myValue // Custom property
// Variables
Vars.myVar // Local variable (microapp instance)
Vars['global.myVar'] // Global variable (shared)
// Global functions
GetVar(key) // Get global variable
GetContextVar(path, appId) // Get context variable
// Component methods
navigateTo(pageId) // Navigate to page
UpdatePage(page, appId) // Update page
Style Handlers
Dynamic styling based on runtime conditions:
styleHandlers: {
width: `
return Vars.isCompact ? '200px' : '100%';
`,
backgroundColor: `
if (Vars.theme === 'dark') return '#333';
if (Vars.theme === 'light') return '#fff';
return '#e0e0e0';
`,
color: `
return Vars.isError ? '#f44336' : '#000';
`,
display: `
return Vars.showField ? 'block' : 'none';
`
}
Evaluation:
- Style handlers are evaluated during component initialization
- Results cached in
stylesHandlersValue - CSS properties applied via
styleMap - Re-evaluated when dependencies change
- Supports all CSS properties (camelCase and kebab-case)
Component Instance (Runtime Values)
Each component has a Current.Instance object for component-scoped state:
// In event handler
Current.Instance.clickCount = (Current.Instance.clickCount || 0) + 1;
Current.Instance.isExpanded = !Current.Instance.isExpanded;
Current.Instance.selectedItems = [1, 2, 3];
// State persists with component throughout lifecycle
// Backed by the global $runtimeValues store
// Reactive - changes trigger component re-renders
Useful for:
- Tracking component-specific state
- Counters and toggles
- Selected items in lists
- Expanded/collapsed states
Component Hierarchy
Components form a tree structure:
// Parent component
{
uuid: "container-1",
name: "MainContainer",
childrenIds: ["button-1", "button-2", "label-1"],
children: [...] // Resolved child components
}
// Accessing hierarchy
const container = ExecuteInstance.applications['app-id']['MainContainer'];
// Navigate children
container.children.forEach(child => {
console.log(child.name);
});
// Access parent
const parent = container.parent;
// Navigate up the tree
let current = someComponent;
while (current.parent) {
console.log(current.parent.name);
current = current.parent;
}
Handler Execution Context
All handlers (input, event, style) execute in a special context:
// Available globals in handlers
ExecuteInstance // Global runtime context
Vars // Variables object
Current // Current component
EventData // Event data (events only)
GetVar // Function to get variables
GetContextVar // Function to get context
Utils // Utility functions
Microapp Isolation
In microapps, handlers run in an isolated context:
// Local scope (isolated to microapp instance)
Vars.localVar = value // Isolated storage
// Global scope (shared across all instances)
Vars['global.sharedVar'] = value // Shared storage
// Access to isolated stores
const app = ExecuteInstance.applications['app-id'];
const page = GetVar("currentPage");
Scoped Event Emission:
- Events trigger only within the microapp instance
- Global events use
global:variable:changedpattern - See Variable Scopes for details
Error Handling
Errors in handlers are caught and logged:
event: {
onChange: `
try {
const parsed = JSON.parse(EventData.value);
Vars.parsedValue = parsed;
} catch (error) {
Vars.parseError = error.message;
console.error('Parse error:', error);
}
`
}
Errors display in:
- Browser console
- Editor console panel
- Component error state
Performance Considerations
Caching
- Input handlers are cached to avoid redundant evaluation
- Style handlers evaluated once per dependency change
- Event handlers compiled and reused
Optimization Tips
- Minimize computations in handlers
- Use
Varsfor frequently accessed values - Avoid DOM manipulations in handlers
- Cache expensive operations outside handlers
- Use conditional logic to short-circuit evaluation
Async Operations
Handlers support async/await:
event: {
onClick: `
Vars.loading = true;
try {
const result = await FetchData();
Vars.data = result;
} finally {
Vars.loading = false;
}
`
}
See Also
- TextInput Component - Practical example
- Variable Scopes - Microapp isolation
- RuntimeContext - Runtime execution engine