Developer Guide: Adding Quick Edit to a Widget
Overview
This guide explains how to add quick editing capabilities to a new or existing widget in the Wallboard system. Quick editing allows users to override widget values at the message (sub-channel) level through a simplified interface.
Prerequisites
- Understanding of the widget system architecture
- Familiarity with AngularJS and TypeScript
- Knowledge of the widget service structure
Step-by-Step Implementation
Note: The quick edit configuration is automatically processed during content save operations by
contentSaverService
. ThegenerateQuickEditableWidgetsBlock()
method is called before saving to ensure all quick edit metadata is up-to-date.
Step 1: Initialize Quick Editables in the Widget
In your widget's initialization code, add the initQuickEditable
function and call it during editor initialization:
TypeScript Widget Example (e.g., Text Widget)
// In your widget service class
private initQuickEditable(widget: YourWidgetType) {
if (!widget.quickEditables) {
widget.quickEditables = {
// Define which properties can be quick-edited
// Property name: default enabled state
text: false // For text widget
};
}
}
// Call during editor initialization
initEditorBehaviour(scope: ng.IScope & { tagObject: YourWidgetType }) {
this.initQuickEditable(scope.tagObject);
// ... other initialization code
}
JavaScript Widget Example (e.g., Image Widget)
function initQuickEditable(tagObject) {
if (!tagObject.quickEditables) {
tagObject.quickEditables = {
src: false // For image widget
};
}
}
// In initEditorWatchers function
widgetObject.initEditorWatchers = function(scope, element, attrs, id) {
initQuickEditable(scope.tagObject);
// ... other watchers
};
Step 2: Create the Quick Edit UI Block
Define a UI block that allows users to toggle quick editing for each property:
Simple Property Example (Text, Image Source)
const quickEditBlock = {
getBlock: (widget) => {
return {
type: 'slidebox',
value: widget.quickEditables,
propertyName: 'text', // or 'src' for image
label: 'Quick editable',
tooltip: 'Enables overriding value from the quick editor',
onChange: () => {
editorStatus.contentJsonChanged();
}
};
}
};
Multiple Properties Example (Shape Widget)
const quickEditBlock = {
getBlock: (widget) => {
return [
{
type: 'slidebox',
value: widget.quickEditables,
propertyName: 'shape.fillColor',
label: 'Fill color',
tooltip: 'Enables overriding fill color from the quick editor',
onChange: () => {
editorStatus.contentJsonChanged();
}
},
{
type: 'slidebox',
value: widget.quickEditables,
propertyName: 'shape.strokeStyle',
label: 'Stroke color',
tooltip: 'Enables overriding stroke color from the quick editor',
onChange: () => {
editorStatus.contentJsonChanged();
}
}
];
}
};
Step 3: Add Quick Edit Block to Widget Tabs
Include the quick edit block in your widget's tab configuration:
getTabDetails(tabName: WidgetTabName, widget: YourWidgetType): WidgetTabDetails {
const allTabDetails: Record<WidgetTabName, WidgetTabDetails> = {
[WidgetTabName.general]: {
tabName: 'general',
tagObject: widget,
sections: [
// ... other sections
{
label: 'Quick edit',
blockSchemes: [this.quickEditBlock],
hiddenRuleBuilder: new EditorSectionHideRuleBuilder()
.setWidgetType(WidgetType.YourWidget)
.setSection(UsualSectionName.quickEdit)
}
]
},
// ... other tabs
};
return allTabDetails[tabName];
}
Step 4: Register Widget Type in QuickEditableInputMapper
Update the QuickEditableInputMapper
service to handle your widget type:
Add to Widget Type Mapping
// In quick-editable-input-mapper.service.ts
private getQuickEditGroupForWidget(widgetType: WidgetType): QuickEditAttributeGroupType {
switch (widgetType) {
// ... existing cases
case WidgetType.YourWidget:
return QuickEditAttributeGroupType.YOUR_GROUP;
default:
throw new Error(`Unsupported widget type: ${widgetType}`);
}
}
private getQuickEditAttributeTypeForWidget(
widgetType: WidgetType,
editedProperty: string
): QuickEditAttributeType {
const widgetAttributeMap: Partial<Record<WidgetType, Record<string, QuickEditAttributeType>>> = {
// ... existing mappings
[WidgetType.YourWidget]: {
yourProperty: QuickEditAttributeType.YOUR_ATTRIBUTE_TYPE,
}
};
// ... rest of the function
}
Add Property Processing
private getWidgetQuickEditSettings(
quickEditObject: QuickEditObject,
editedProperty: string
): QuickEditToggleParams {
// ... existing code
switch (editedProperty) {
// ... existing cases
case 'yourProperty':
return this.createQuickEditParams(
isEnabled,
groupType,
attributeType,
QuickEditOverrideRule.MESSAGE,
QuickEditAttributeGroupValueType.SINGLE_VALUED,
QuickEditAttributeValueType.STRING
);
default:
return null;
}
}
Step 5: Update Type Definitions
Add necessary enums and types if your widget requires new attribute types:
// In widgetTypeDefinitions.ts
// Add to QuickEditAttributeGroupType enum if needed
export enum QuickEditAttributeGroupType {
// ... existing values
YOUR_GROUP = 'YOUR_GROUP'
}
// Add to QuickEditAttributeType enum
export enum QuickEditAttributeType {
// ... existing values
YOUR_ATTRIBUTE_TYPE = 'YOUR_ATTRIBUTE_TYPE'
}
Step 6: Handle Property Mapping in Processor
Update the QuickEditableProcessor
to handle your widget's properties:
// In quick-editable-widget-processor.service.ts
private getPropertyPathFromType(type: QuickEditAttributeType): string {
switch (type) {
// ... existing cases
case QuickEditAttributeType.YOUR_ATTRIBUTE_TYPE:
return 'yourProperty'; // or 'nested.property.path'
default:
return '';
}
}
Complete Examples
Example 1: Simple Text Widget
// text.ts
@WidgetService('TextWidgetService')
export class TextWidgetService {
private quickEditBlock = {
getBlock: (widget: TextWidget) => {
return {
type: 'slidebox',
value: widget.quickEditables,
propertyName: 'text',
label: 'Quick editable',
tooltip: 'Enables overriding value from the quick editor',
onChange: () => {
this.editorStatus.contentJsonChanged();
}
};
}
};
private initQuickEditable(widget: TextWidget) {
if (!widget.quickEditables) {
widget.quickEditables = { text: false };
}
}
initEditorBehaviour(scope: ng.IScope & { tagObject: TextWidget }) {
this.initQuickEditable(scope.tagObject);
}
getTabDetails(tabName: WidgetTabName, widget: TextWidget): WidgetTabDetails {
// Include quickEditBlock in sections
}
}
Example 2: Media Widget with Caching
// video.js
const quickEditBlock = {
getBlock: (widget) => {
return {
type: 'slidebox',
value: widget.quickEditables,
propertyName: 'src',
label: 'Quick editable',
tooltip: 'Enables overriding video source from the quick editor',
onChange: () => {
editorStatus.contentJsonChanged();
}
};
}
};
function initQuickEditable(tagObject) {
if (!tagObject.quickEditables) {
tagObject.quickEditables = { src: false };
}
}
// Video sources are automatically cached by the processor
Example 3: Multi-Property Widget
// shape.js
const quickEditBlock = {
getBlock: (widget) => {
return [
{
type: 'slidebox',
value: widget.quickEditables,
propertyName: 'shape.fillColor',
label: 'Fill color',
tooltip: 'Enables overriding fill color from the quick editor',
onChange: () => {
editorStatus.contentJsonChanged();
}
},
{
type: 'slidebox',
value: widget.quickEditables,
propertyName: 'shape.gradColor',
label: 'Gradient color',
tooltip: 'Enables overriding gradient color from the quick editor',
onChange: () => {
editorStatus.contentJsonChanged();
},
ngHideFunction: () => !widget.shape.gradientEnabled
},
{
type: 'slidebox',
value: widget.quickEditables,
propertyName: 'shape.strokeStyle',
label: 'Stroke color',
tooltip: 'Enables overriding stroke color from the quick editor',
onChange: () => {
editorStatus.contentJsonChanged();
}
}
];
}
};
Best Practices
1. Property Naming
- Use dot notation for nested properties:
'shape.fillColor'
- Keep property names consistent with widget data structure
2. Default States
- Initialize quickEditables with
false
by default - Only enable quick editing when explicitly requested by user
3. Change Tracking
- Always call
editorStatus.contentJsonChanged()
in onChange handlers - This ensures changes are saved properly
4. UI Considerations
- Provide clear labels and helpful tooltips
- Use
ngHideFunction
to conditionally show options - Group related quick-editable properties together
5. Type Safety
- Define proper TypeScript interfaces for your widget type
- Use enums for attribute types and groups
- Validate property paths at compile time when possible
Summary
Adding quick edit support to a widget involves:
- Initializing the quickEditables object
- Creating UI controls for toggling quick edit
- Registering widget type and properties in the mapper
- Updating type definitions
- Handling property mapping in the processor
- Testing the implementation thoroughly