Skip to main content

Custom Controls

Custom Controls are reusable UI component elements in the JitWeb framework, implemented based on React technology stack for frontend interaction functionality. They are responsible for encapsulating specific business logic, providing data binding capabilities, and responding to user operations, supporting flexible invocation in pages and workflow nodes.

The Custom Control element has a hierarchical structure of Meta (widgets.Meta) → Type (widgets.React) → Instance. Developers can quickly create custom control instance elements through JitAI's visual development tools.

Of course, developers can also create their own Type elements or modify the official widgets.ReactType element provided by JitAi in their own App to implement their own encapsulation.

Quick Start

Basic Configuration Example

Create a simple input control:

index.tsx
import type { Jit } from 'jit';
import type { FC } from 'react';
import { getRuntimeApp } from 'jit';
import { Input } from 'antd';
import { useState, useEffect } from 'react';

export const Render: FC<{
props: {
data: InstanceType<typeof Jit.BaseDataType>;
onChange: (v: string) => void;
};
}> = ({ props: p }) => {
const [value, setValue] = useState(p.data.value);

useEffect(() => {
const app = getRuntimeApp();
const handleId = p.data.onValueChange(() => {
setValue(p.data.value);
});
return () => app.off(handleId);
}, [p.data.value]);

const onChange = (e: any) => {
setValue(e.target.value);
p.onChange(e.target.value);
};

return <Input value={value} onChange={onChange} />;
};

Corresponding configuration file:

e.json
{
"title": "Custom Input Control",
"type": "widgets.React",
"outputName": "index",
"frontBundleEntry": "index.tsx",
"props": []
}

Usage in Pages

Call through ElementRender in React components:

Page Usage Example
import { ElementRender } from 'jit-web';

// Create data type instance
const textData = new Jit.datatypes.Stext({
name: 'userInput',
value: 'Initial value',
});

// Use custom control
<ElementRender
elementPath="widgets.testCustomControls"
props={{
data: textData,
onChange: (v: string) => {
console.log('Value changed:', v);
}
}}
/>

Reference through widgetFullName in workflow nodes:

Node Configuration
{
"renderByWidget": 1,
"widgetFullName": "widgets.testCustomControls",
"sendData": 1,
"sendArgs": ["node.output"]
}

Configuration Properties

PropertyTypeRequiredDefault ValueDescription
titlestringYes-Control display name
typestringYes"widgets.React"Control type, fixed value
outputNamestringYes"index"Output file name
frontBundleEntrystringYes"index.tsx"Frontend entry file path
propsarrayNo[]Property configuration list

Variables

data

  • Type: InstanceType<typeof Jit.BaseDataType>
  • Description: Bound data type instance, supports two-way data binding
  • Example: Data instance passed through props, can listen to value change events

props

  • Type: Record<string, any>
  • Description: Component properties object, containing all configuration parameters passed from external sources
  • Example: Contains business-related properties such as data, onChange

Methods

getRuntimeApp()

Get current runtime application instance.

Return Value

  • App - Application instance object

Usage Example

const app = getRuntimeApp();

publishEvent(name, data)

Publish component events, supporting cross-component communication.

Parameter Details

ParameterTypeRequiredDescription
namestringYesEvent name
dataRecord<string, any>NoEvent data

Usage Example

// Publish event
const sendEvent = () => {
const app = getRuntimeApp();
app?.emit({
name: 'dataChanged',
type: 'COMP_MESSAGE'
}, { value: newValue });
};

subscribeEvent(name, callback)

Subscribe to component events, implementing event listening.

Parameter Details

ParameterTypeRequiredDescription
namestringYesEvent name
callbackFunctionYesEvent callback function

Return Value

  • string - Event handler ID, used for unsubscribing

Usage Example

useEffect(() => {
const app = getRuntimeApp();
const handleId = app?.on(async (data) => {
console.log('Event data:', data);
}, async (event) => {
return event.name === 'targetEvent';
});

return () => {
if (handleId) app?.off(handleId);
};
}, []);

onValueChange(callback)

Listen to data value change events.

Parameter Details

ParameterTypeRequiredDescription
callbackFunctionYesValue change callback function

Return Value

  • string - Event handler ID

Usage Example

useEffect(() => {
if (!data) return;

const handleId = data.onValueChange(() => {
setValue(data.value);
});

return () => app?.off(handleId);
}, [data]);

Attributes

None

Events

onValueChange

Data value change event, triggered when the bound data type instance value changes.

Parameter Details

ParameterTypeDescription
valueanyNew value after change

Usage Example

// Listen to data changes
useEffect(() => {
if (!p.data) return;

const app = getRuntimeApp();
const handleId = p.data.onValueChange(() => {
setValue(p.data.value);
});

return () => app.off(handleId);
}, [p.data]);

Advanced Features

Two-way Data Binding

Supports two-way data binding with data type instances, implementing synchronized data updates.

Configuration Example

export const Render: FC<{ props: Props }> = ({ props: p }) => {
const [value, setValue] = useState(p.data?.value);

// Listen to data changes
useEffect(() => {
if (!p.data) return;

const app = getRuntimeApp();
const handleId = p.data.onValueChange(() => {
setValue(p.data.value);
});

return () => app.off(handleId);
}, [p.data]);

// Update data
const handleChange = (newValue: any) => {
setValue(newValue);
p.onChange?.(newValue);
// Directly update data type instance
if (p.data) {
p.data.value = newValue;
}
};

return (
<input
value={value || ''}
onChange={(e) => handleChange(e.target.value)}
/>
);
};

Cross-component Event Communication

Implement event publishing and subscription mechanism between components.

Configuration Example

export const Render: FC<{ props: Props }> = ({ props }) => {
useEffect(() => {
const app = getRuntimeApp();

// Listen to global events
const handleId = app?.on(async (data) => {
console.log('Received event:', data);
}, async (event) => {
return event.name === 'customEvent';
});

return () => {
if (handleId) app?.off(handleId);
};
}, []);

// Send event
const sendEvent = () => {
const app = getRuntimeApp();
app?.emit({
name: 'customEvent',
type: 'CUSTOM_MESSAGE'
}, { data: 'event data' });
};

return <button onClick={sendEvent}>Send Event</button>;
};

Lifecycle Management

Implement complete component lifecycle control.

Configuration Example

export const Render: FC<{ props: Props }> = ({ props }) => {
const componentRef = useRef<any>(null);

// Component mount
useEffect(() => {
console.log('Component mounted');

// Initialization logic
initComponent();

return () => {
// Cleanup logic
cleanup();
console.log('Component unmounted');
};
}, []);

const initComponent = () => {
// Initialize component state
};

const cleanup = () => {
// Clean up resources, event listeners, etc.
};

return <div ref={componentRef}>Component content</div>;
};

Error Handling Mechanism

Implement robust error handling and user feedback.

Configuration Example

export const Render: FC<{ props: Props }> = ({ props }) => {
const [error, setError] = useState<string | null>(null);

const handleOperation = async () => {
try {
setError(null);
// Execute potentially error-prone operations
await someAsyncOperation();
} catch (err) {
setError(err instanceof Error ? err.message : 'Operation failed');
console.error('Component operation error:', err);
}
};

if (error) {
return <Alert message="Error" description={error} type="error" />;
}

return (
<div>
<button onClick={handleOperation}>Execute Operation</button>
</div>
);
};
JitAI AssistantBeta
Powered by JitAI