mit einem Klick
judo-frontend-hooks-docs
// Frontend hook system documentation. Covers data, UI, table, action, navigation, and validation hooks.
// Frontend hook system documentation. Covers data, UI, table, action, navigation, and validation hooks.
ESM-to-UI mapping reference for JUDO React frontends. Covers widget mappings, table and navigation element mappings, and the ESM→UI transformation model.
Frontend development guide for JUDO React applications. Covers hooks, theming, i18n, and Pandino DI customization patterns.
ESM metamodel reference for JUDO. Covers namespace, type, structure, operation, accesspoint, UI, UI-behaviour, and UI-visual-styleguide packages with element-level attribute and constraint definitions.
Model documentation for JUDO applications. Covers ESM metamodel, cardinality, CRUD flags, and advanced modeling patterns.
Backend development guide for JUDO applications. Covers custom operations, interceptors, validators, data access, and error handling.
Deployment and build documentation for JUDO applications. Covers judo.sh commands, Docker setup, Karaf configuration, and production deployment.
| name | judo-frontend-hooks-docs |
| description | Frontend hook system documentation. Covers data, UI, table, action, navigation, and validation hooks. |
| disable-model-invocation | false |
| user-invocable | false |
| agent | general-purpose |
The JUDO frontend uses a hook-based customization system that allows you to extend and modify generated behavior without editing generated code. Hooks are registered through the central application-customizer.tsx file and use the Pandino dependency injection framework.
Hooks are customization points in the generated code where you can inject your own logic. They allow you to:
Benefits:
src/custom/Alternative (Not Recommended):
.generator-ignore → Lose generator updatesAll hooks register through the Pandino dependency injection framework:
// src/custom/application-customizer.tsx
import {
ApplicationCustomizer,
BundleContext,
TableRowHighlightingHook,
TABLE_ROW_HIGHLIGHTING_HOOK_INTERFACE_KEY
} from '~/generated';
export class DefaultApplicationCustomizer implements ApplicationCustomizer {
async customize(context: BundleContext): Promise<void> {
// Register hook with interface key
context.registerService<TableRowHighlightingHook>(
TABLE_ROW_HIGHLIGHTING_HOOK_INTERFACE_KEY,
myHighlightingHook,
{ component: 'ServiceCanvassingEventsTable' }
);
}
}
Interface Keys:
TABLE_ROW_HIGHLIGHTING_HOOK_INTERFACE_KEY~/generatedService Properties:
{ component, column, page }{ component: 'ServiceCampaignForm', column: 'name' }Hook Implementation:
Precedence:
Access and manage application data:
usePrincipal - Current user informationuseViewData - Page/dialog data accessCustomize visual components:
Enhance table functionality:
Control operations and flows:
Manage routing and permissions:
Customize input validation:
The generator creates .default template files as blueprints for customization.
Important: .default files (.tsx.default, .ts.default) are compiled by the build system.
Two Options:
# Find generated templates
find src/custom -name "*.default"
# Edit the .default file directly
vim src/custom/application-customizer.tsx.default
# File is compiled as-is - no additional steps needed
Advantages:
# Copy/rename to remove .default
cp src/custom/application-customizer.tsx.default src/custom/application-customizer.tsx
# Protect from regeneration
echo "src/custom/application-customizer.tsx" >> .generator-ignore
# Edit the file
vim src/custom/application-customizer.tsx
Advantages:
Generated src/custom/application-customizer.tsx.default:
import { ApplicationCustomizer, BundleContext } from '~/generated';
/**
* Default application customizer.
* Either:
* 1. Keep this .default file and edit it directly (recommended)
* 2. Rename to application-customizer.tsx and add to .generator-ignore
*/
export class DefaultApplicationCustomizer implements ApplicationCustomizer {
async customize(context: BundleContext): Promise<void> {
// Register your customizations here
// Example: Register table row highlighting
// context.registerService<TableRowHighlightingHook>(
// TABLE_ROW_HIGHLIGHTING_HOOK_INTERFACE_KEY,
// myHighlightingHook,
// { component: 'ServiceCanvassingEventsTable' }
// );
}
}
After customization:
import {
ApplicationCustomizer,
BundleContext,
TableRowHighlightingHook,
TABLE_ROW_HIGHLIGHTING_HOOK_INTERFACE_KEY
} from '~/generated';
import { canvassingEventHighlighting } from './hooks/tableRowHighlighting';
export class DefaultApplicationCustomizer implements ApplicationCustomizer {
async customize(context: BundleContext): Promise<void> {
// Register table row highlighting
context.registerService<TableRowHighlightingHook>(
TABLE_ROW_HIGHLIGHTING_HOOK_INTERFACE_KEY,
canvassingEventHighlighting,
{ component: 'ServiceCanvassingEventsTable' }
);
}
}
| Pattern | Purpose | Recommendation |
|---|---|---|
*-customizer.tsx.default | Component customization entry points | Keep .default |
hooks/*.tsx.default | Custom hook implementations | Either works |
theme/*.ts.default | Theme and styling customizations | Usually rename |
pages/*-hook-registration.tsx.default | Page-level hook registrations | Keep .default |
# Step 1: Check generated templates
find src/custom -name "*.default"
# Step 2: Choose approach (keeping .default is recommended)
vim src/custom/application-customizer.tsx.default
# Step 3: Create custom hook
mkdir -p src/custom/hooks
cat > src/custom/hooks/tableRowHighlighting.tsx << 'EOF'
import { TableRowHighlightingHook } from '~/generated';
import { ServiceCanvassingEventStored } from '~/generated/data-api';
export const canvassingEventHighlighting: TableRowHighlightingHook<ServiceCanvassingEventStored> = () => {
return () => ([
{
name: 'overdue',
label: 'Overdue Events',
backgroundColor: '#ffebee',
condition: (params) => {
const deadline = new Date(params.row.eventDate);
return deadline < new Date() && !params.row.completed;
}
}
]);
};
EOF
# Step 4: Register in customizer
# Edit application-customizer.tsx.default to add registration
# Step 5: Protect custom hook
echo "src/custom/hooks/tableRowHighlighting.tsx" >> .generator-ignore
# Step 6: Test
cd application/frontend-react/northwind__[actor_fqn]
pnpm run dev
export class DefaultApplicationCustomizer implements ApplicationCustomizer {
async customize(context: BundleContext): Promise<void> {
context.registerService<TableRowHighlightingHook>(
TABLE_ROW_HIGHLIGHTING_HOOK_INTERFACE_KEY,
myHighlightingHook,
{ component: 'ServiceCampaignsTable' }
);
}
}
export class DefaultApplicationCustomizer implements ApplicationCustomizer {
async customize(context: BundleContext): Promise<void> {
// Row highlighting
context.registerService<TableRowHighlightingHook>(
TABLE_ROW_HIGHLIGHTING_HOOK_INTERFACE_KEY,
campaignHighlighting,
{ component: 'ServiceCampaignsTable' }
);
// Custom column
context.registerService<ColumnCustomizerHook>(
TABLE_COLUMN_CUSTOMIZER_HOOK_INTERFACE_KEY,
statusColumn,
{ component: 'ServiceCampaignsTable', column: 'status' }
);
// Sidekick component
context.registerService<FC>(
SIDEKICK_COMPONENT_INTERFACE_KEY,
CampaignChartSidekick,
{ component: 'ServiceCampaignsTable' }
);
}
}
export class DefaultApplicationCustomizer implements ApplicationCustomizer {
async customize(context: BundleContext): Promise<void> {
// Only register in production
if (process.env.NODE_ENV === 'production') {
context.registerService<AnalyticsHook>(
ANALYTICS_HOOK_INTERFACE_KEY,
analyticsHook
);
}
// Feature flag based registration
if (localStorage.getItem('betaFeatures') === 'true') {
context.registerService<BetaFeatureHook>(
BETA_FEATURE_HOOK_INTERFACE_KEY,
betaFeatureHook
);
}
}
}
Add to .generator-ignore:
Don't add to .generator-ignore:
# Custom application customizer (if renamed from .default)
src/custom/application-customizer.tsx
# Custom hooks (created from scratch)
src/custom/hooks/tableRowHighlighting.tsx
src/custom/hooks/usePrincipal.tsx
src/custom/hooks/useCustomAuth.tsx
# Custom components
src/custom/components/CampaignChart.tsx
src/custom/components/NotificationBell.tsx
# Theme customizations (if renamed)
src/theme/palette.ts
src/theme/typography.ts
# Directly edited generated files (not recommended)
# src/generated/pages/CustomPage.tsx
# Check if file is protected
grep "application-customizer.tsx" .generator-ignore
# List all protected files
cat .generator-ignore
# Test regeneration
mvn clean install
git diff src/custom/
# Should show no unwanted changes
// ✅ Good - Use hook
context.registerService<TableRowHighlightingHook>(
TABLE_ROW_HIGHLIGHTING_HOOK_INTERFACE_KEY,
myHook,
{ component: 'ServiceCampaignsTable' }
);
// ❌ Bad - Edit generated file
// Editing src/generated/pages/CampaignsTable.tsx
# ✅ Recommended
vim src/custom/application-customizer.tsx.default
# ⚠️ Acceptable but requires .generator-ignore
cp src/custom/application-customizer.tsx.default src/custom/application-customizer.tsx
echo "src/custom/application-customizer.tsx" >> .generator-ignore
src/custom/
├── application-customizer.tsx.default
├── hooks/
│ ├── tableRowHighlighting.tsx
│ ├── usePrincipal.tsx
│ └── validation.tsx
├── components/
│ ├── CampaignChart.tsx
│ └── NotificationBell.tsx
└── utils/
└── helpers.ts
/**
* Highlights overdue canvassing events in red.
*
* Applied to: ServiceCanvassingEventsTable
* Updated: 2024-12-15
*/
export const canvassingEventHighlighting: TableRowHighlightingHook = () => {
return () => ([...]);
};
# Always test after mvn clean install
mvn clean install
pnpm run dev
# Verify all customizations still work
Check registration:
// Verify in application-customizer.tsx.default
console.log('Registering hook');
context.registerService<TableRowHighlightingHook>(
TABLE_ROW_HIGHLIGHTING_HOOK_INTERFACE_KEY,
myHook,
{ component: 'ServiceCampaignsTable' }
);
Verify component name:
# Find correct component name in generated code
grep -r "export const.*Table" src/generated/pages/
Add to .generator-ignore:
echo "src/custom/hooks/myHook.tsx" >> .generator-ignore
Import correct types:
// ✅ Correct
import { TableRowHighlightingHook } from '~/generated';
// ❌ Wrong
import { TableRowHighlightingHook } from '~/generated/hooks';