QUICK REFERENCE
Comprehensive guidelines for AI-assisted development within the Editor platform
Quick Reference - editor2 Module Generation
Code Snippets for Rapid Module Generation Copy-paste ready templates optimized for AI code generation
Last Updated: December 27, 2025
Platform: editor2
Generation Variables
| Placeholder | Example | Description |
|---|---|---|
<module-name> |
product-catalog |
Module folder name (kebab-case) |
<ModuleName> |
ProductCatalog |
Module class prefix (PascalCase) |
<moduleName> |
productCatalog |
Module variable name (camelCase) |
<Entity> |
Product |
Main entity (PascalCase) |
<entity> |
product |
Entity lowercase |
<entities> |
products |
Entity plural lowercase |
<Entities> |
Products |
Entity plural (PascalCase) |
<base-path> |
products |
URL base path |
<resource> |
products |
Permission resource |
1. Component Files (Separate Files - Best Practice)
Component TypeScript
// <entity>-list/<entity>-list.component.ts
import { Component, inject, signal, computed, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { LoggerService } from '@libs-core/log-system';
import { autoFlashState } from '@storage-system';
import { <Entity>Repository } from '../../data/repositories/<entity>.repository';
import { <Entity>, <Entity>Filters } from '../../models';
import { firstValueFrom } from 'rxjs';
@Component({
selector: 'lib-<entity>-list',
standalone: true,
imports: [CommonModule, RouterModule],
templateUrl: './<entity>-list.component.html',
styleUrl: './<entity>-list.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class <Entity>ListComponent implements OnInit {
private readonly repository = inject(<Entity>Repository);
private readonly logger = inject(LoggerService).child({ source: '<Entity>ListComponent' });
readonly entities = signal<<Entity>[]>([]);
readonly loading = signal(false);
readonly error = signal<string | null>(null);
readonly filters = autoFlashState<<Entity>Filters>('<module-name>-filters', {
status: 'all',
searchTerm: '',
});
readonly filteredEntities = computed(() => {
const filters = this.filters();
let result = this.entities();
if (filters.status !== 'all') {
result = result.filter(e => e.status === filters.status);
}
if (filters.searchTerm) {
const term = filters.searchTerm.toLowerCase();
result = result.filter(e => e.name.toLowerCase().includes(term));
}
return result;
});
ngOnInit(): void {
this.loadEntities();
}
private async loadEntities(): Promise<void> {
this.loading.set(true);
try {
const entities = await firstValueFrom(this.repository.findAll());
this.entities.set(entities);
} catch (err) {
this.error.set(err instanceof Error ? err.message : 'Unknown error');
} finally {
this.loading.set(false);
}
}
}
Component HTML
<!-- <entity>-list/<entity>-list.component.html -->
<div class="page-container">
<header class="page-header">
<h1><Entities></h1>
<button (click)="onCreate()">Create New</button>
</header>
@if (loading()) {
<div class="loading-state">Loading...</div>
} @else if (error()) {
<div class="error-state">{{ error() }}</div>
} @else if (filteredEntities().length === 0) {
<div class="empty-state">No <entities> found</div>
} @else {
<div class="data-grid">
@for (entity of filteredEntities(); track entity.id) {
<div class="data-card" (click)="onEntityClick(entity)">
<h3>{{ entity.name }}</h3>
<p>{{ entity.description }}</p>
</div>
}
</div>
}
</div>
Component SCSS
// <entity>-list/<entity>-list.component.scss
.page-container {
padding: var(--spacing-4);
backdrop-filter: blur(16px);
background: rgba(var(--ux-surface-rgb), 0.8);
border-radius: var(--radius-lg);
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-4);
h1 {
margin: 0;
color: var(--ux-text-primary);
font-size: var(--font-size-2xl);
}
button {
padding: var(--spacing-2) var(--spacing-4);
background: var(--ux-primary);
border: none;
border-radius: var(--radius-md);
color: white;
cursor: pointer;
}
}
.data-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: var(--spacing-4);
}
.data-card {
padding: var(--spacing-4);
backdrop-filter: blur(8px);
background: rgba(var(--ux-surface-rgb), 0.6);
border: 1px solid var(--ux-border);
border-radius: var(--radius-md);
cursor: pointer;
transition: all 0.2s ease;
&:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
}
2. Module Class
// <module-name>.module.ts
import { Injectable, inject } from '@angular/core';
import { Observable, of } from 'rxjs';
import { BrowserModuleContract, ModuleManifest, ModuleDecorator } from '@libs-core/module-base';
import { LoggerService } from '@libs-core/log-system';
import { EventBusService } from '@libs-core/event-system';
@ModuleDecorator({
id: '<module-name>',
version: '1.0.0',
name: '<Module Name>',
description: '<Module description>',
permissions: ['<resource>:read', '<resource>:write']
})
@Injectable({ providedIn: 'root' })
export class <ModuleName>Module implements BrowserModuleContract {
private readonly logger = inject(LoggerService).child({ source: '<ModuleName>Module' });
private readonly eventBus = inject(EventBusService);
readonly manifest: ModuleManifest = {
id: '<module-name>',
version: '1.0.0',
name: '<Module Name>',
description: '<Module description>',
permissions: ['<resource>:read', '<resource>:write'],
publicActions: [/* actions here */],
embeddableComponents: [/* widgets here */],
};
configure<T>(config: T): Observable<void> {
this.logger.info('Configuring', config);
return of(void 0);
}
initialize(): Observable<void> {
this.logger.info('Initializing');
return of(void 0);
}
start(): Observable<void> {
this.logger.info('Starting');
this.eventBus.emit({ type: 'MODULE_STARTED', payload: { moduleId: this.manifest.id } });
return of(void 0);
}
stop(): Observable<void> {
this.logger.info('Stopping');
return of(void 0);
}
destroy(): Observable<void> {
this.logger.info('Destroying');
return of(void 0);
}
}
3. Repository
// data/repositories/<entity>.repository.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BaseRepository } from '@libs-core/data-access';
import { LoggerService } from '@libs-core/log-system';
import { <Entity> } from '../models/<entity>.model';
@Injectable({ providedIn: 'root' })
export class <Entity>Repository extends BaseRepository<<Entity>> {
private readonly http = inject(HttpClient);
private readonly logger = inject(LoggerService).child({ source: '<Entity>Repository' });
static readonly ENTITY_NAME = '<Entity>';
protected entityName = '<Entity>Repository.ENTITY_NAME;
protected baseUrl = '/api/<entities>';
// Inherited methods (DO NOT redefine):
// - findAll(): Observable<<Entity>[]>
// - findById(id: string): Observable<<Entity> | null>
// - create(entity: Partial<<Entity>>): Observable<<Entity>>
// - update(id: string, entity: Partial<<Entity>>): Observable<<Entity>>
// - delete(id: string): Observable<boolean>
// Add custom methods:
findByStatus(status: string): Observable<<Entity>[]> {
return this.http.get<<Entity>[]>(`${this.baseUrl}/by-status/${status}`);
}
}
4. Routes
// <module-name>.routes.ts
import { Routes } from '@angular/router';
import { authGuard, permissionGuard } from '@libs-core/security';
export const <moduleName>Routes: Routes = [
{
path: '',
redirectTo: 'list',
pathMatch: 'full',
},
{
path: 'list',
loadComponent: () => import('./components/<entity>-list/<entity>-list.component')
.then(m => m.<Entity>ListComponent),
canActivate: [authGuard, permissionGuard('<resource>:read')],
data: {
title: '<Entities>',
permissions: ['<resource>:read'],
animation: 'fade',
},
},
{
path: 'create',
loadComponent: () => import('./components/<entity>-form/<entity>-form.component')
.then(m => m.<Entity>FormComponent),
canActivate: [authGuard, permissionGuard('<resource>:create')],
data: {
title: 'Create <Entity>',
permissions: ['<resource>:create'],
mode: 'create',
},
},
{
path: ':id',
loadComponent: () => import('./components/<entity>-detail/<entity>-detail.component')
.then(m => m.<Entity>DetailComponent),
canActivate: [authGuard, permissionGuard('<resource>:read')],
data: {
title: '<Entity> Detail',
permissions: ['<resource>:read'],
},
},
];
5. Barrel Exports
Main Index
// src/index.ts
export * from './lib/<module-name>.module';
export * from './lib/<module-name>.routes';
export * from './lib/models';
export * from './lib/services';
export * from './lib/data';
export * from './lib/components';
export * from './lib/embeddables';
export * from './lib/actions';
export * from './lib/events';
Sub-folder Indexes
// actions/index.ts
export * from './navigate-to-<entity>.action';
export * from './create-<entity>.action';
export * from './update-<entity>.action';
export * from './delete-<entity>.action';
// components/index.ts
export * from './<entity>-list/<entity>-list.component';
export * from './<entity>-detail/<entity>-detail.component';
export * from './<entity>-form/<entity>-form.component';
// embeddables/index.ts
export * from './<entity>-widget/<entity>-widget.component';
// models/index.ts
export * from './<entity>.model';
export * from './<entity>-filters.model';
// services/index.ts
export * from './<entity>-api.service';
// data/index.ts
export * from './repositories';
// data/repositories/index.ts
export * from './<entity>.repository';
// events/index.ts
export * from './<entity>.events';
6. Action Template
// actions/navigate-to-<entity>.action.ts
import { Injectable, inject } from '@angular/core';
import { Router } from '@angular/router';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
import { PublicAction, ExecutableAction, ActionContext, ActionParams } from '@libs-core/action-registry';
import { LoggerService } from '@libs-core/log-system';
export interface NavigateTo<Entity>Input extends ActionParams {
<entity>Id: string;
mode?: 'detail' | 'edit';
}
export interface NavigateTo<Entity>Output {
success: boolean;
targetRoute: string;
}
@PublicAction({
id: '<resource>:navigate-to-<entity>',
name: 'Navigate to <Entity>',
description: 'Navigate to <entity> page',
permissions: ['<resource>:read'],
tags: ['navigation'],
parameters: [
{ name: '<entity>Id', type: 'string', required: true, description: '<Entity> ID' },
{ name: 'mode', type: 'string', required: false, default: 'detail' },
],
config: { category: 'navigation', module: '<module-name>' },
})
@Injectable({ providedIn: 'root' })
export class NavigateTo<Entity>Action implements ExecutableAction<NavigateTo<Entity>Output> {
readonly metadata = {
id: '<resource>:navigate-to-<entity>',
name: 'Navigate to <Entity>',
description: 'Navigate to <entity> page',
requiredPermissions: ['<resource>:read'] as const,
category: 'navigation',
enabled: true,
};
private readonly router = inject(Router);
private readonly logger = inject(LoggerService).child({ source: 'NavigateTo<Entity>Action' });
execute(params: NavigateTo<Entity>Input, context: ActionContext): Observable<NavigateTo<Entity>Output> {
const route = params.mode === 'edit'
? ['/<base-path>', params.<entity>Id, 'edit']
: ['/<base-path>', params.<entity>Id];
return of({ success: true, targetRoute: route.join('/') }).pipe(
tap(() => this.router.navigate(route))
);
}
validate(params: NavigateTo<Entity>Input): Observable<boolean> {
return of(!!params.<entity>Id);
}
cleanup(): void {}
}
7. Event Template
// events/<entity>.events.ts
import { DomainEvent, DomainEventPayload } from '@libs-core/event-system';
import { <Entity> } from '../models/<entity>.model';
export interface <Entity>CreatedPayload extends DomainEventPayload {
<entity>: <Entity>;
createdBy: string;
}
export class <Entity>CreatedEvent extends DomainEvent<<Entity>CreatedPayload> {
static readonly TYPE = '<entity>.created';
constructor(payload: <Entity>CreatedPayload) {
super(<Entity>CreatedEvent.TYPE, '<module-name>', payload);
}
}
8. Model Template
// models/<entity>.model.ts
import { BaseEntity } from '@libs-core/shared-models';
export type <Entity>Status = 'active' | 'inactive' | 'pending';
export interface <Entity> extends BaseEntity {
name: string;
description: string;
status: <Entity>Status;
}
export interface <Entity>Filters {
status?: <Entity>Status | 'all';
searchTerm?: string;
page?: number;
pageSize?: number;
}
9. Flash Persistence Usage
// For persistent UI state
readonly viewMode = autoFlashState('<module-name>-view-mode', 'grid' as 'grid' | 'list');
// For complex objects
readonly settings = autoFlashState('<module-name>-settings', {
sortBy: 'name',
sortDirection: 'asc',
itemsPerPage: 20,
});
// Update state
this.settings.update(current => ({ ...current, sortBy: 'date' }));
// Read state
const currentSort = this.settings().sortBy;
10. Theme System Usage
// Always use CSS variables
.my-component {
// Colors
background: var(--ux-surface);
color: var(--ux-text-primary);
border-color: var(--ux-border);
// Glassmorphism
backdrop-filter: blur(16px);
background: rgba(var(--ux-surface-rgb), 0.8);
// Spacing
padding: var(--spacing-4);
gap: var(--spacing-3);
margin: var(--spacing-2);
// Border radius
border-radius: var(--radius-lg);
// Font sizes
font-size: var(--font-size-lg);
// Shadows
box-shadow: var(--shadow-lg);
// States
&:hover {
background: var(--ux-primary-hover);
}
}
11. Intelligence Pyramid Integration
// Before generation
const context = await mcp.preflight({
task: 'Generate <module-name> module',
categories: ['angular', 'modules']
});
// Use context in generation
// ...
// After generation
await mcp.log_decision({
type: 'implementation',
question: 'How to implement <module-name> module?',
options: ['Approach A', 'Approach B'],
chosen: 'Approach A',
reasoning: 'Because...',
confidence: 0.9,
files: ['<module-name>.module.ts', '<entity>.repository.ts'],
});
12. Project Configuration
project.json
{
"name": "<module-name>",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/libs-modules/<module-name>/src",
"prefix": "lib",
"projectType": "library",
"tags": ["type:module", "scope:business"],
"targets": {
"build": {
"executor": "nx:run-commands",
"outputs": ["{workspaceRoot}/dist/libs/libs-modules/<module-name>"],
"options": {
"command": "node tools/scripts/build/build-angular-library.js libs/libs-modules/<module-name>"
}
},
"test": {
"executor": "@nx/vite:test",
"options": {
"config": "libs/libs-modules/<module-name>/vite.config.mts"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}
tsconfig.lib.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/libs/libs-modules/<module-name>"
},
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.spec.ts", "src/test-setup.ts"]
}
13. Common Patterns
Loading Pattern
readonly loading = signal(false);
readonly data = signal<T[]>([]);
readonly error = signal<string | null>(null);
async loadData() {
this.loading.set(true);
this.error.set(null);
try {
const data = await firstValueFrom(this.service.getData());
this.data.set(data);
} catch (err) {
this.error.set(err instanceof Error ? err.message : 'Unknown error');
} finally {
this.loading.set(false);
}
}
Filtering Pattern
readonly filters = autoFlashState('filters', { status: 'all', search: '' });
readonly filteredItems = computed(() => {
const f = this.filters();
return this.items().filter(item => {
if (f.status !== 'all' && item.status !== f.status) return false;
if (f.search && !item.name.toLowerCase().includes(f.search.toLowerCase())) return false;
return true;
});
});
Form Pattern
readonly formData = signal<CreateEntityRequest>({ name: '', description: '' });
async onSubmit() {
const data = this.formData();
const result = await firstValueFrom(this.repository.create(data));
this.router.navigate(['/<base-path>', result.id]);
}
14. Validation Integration
MCP Validation
// After generating module files
const validation = await mcp.executeToolCall({
server: 'gograb-mcp-server',
tool: 'validate_module',
arguments: {
modulePath: 'libs/libs-modules/<module-name>',
strictMode: true
}
});
if (!validation.valid) {
// Handle validation issues
for (const issue of validation.issues) {
if (issue.autoFixable) {
await autoFix(issue);
}
}
}
15. Testing Template
// <entity>.repository.spec.ts
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { <Entity>Repository } from './<entity>.repository';
import { LoggerService } from '@libs-core/log-system';
describe('<Entity>Repository', () => {
let repository: <Entity>Repository;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
<Entity>Repository,
{ provide: LoggerService, useValue: { child: () => ({ debug: vi.fn(), info: vi.fn(), error: vi.fn() }) } }
]
});
repository = TestBed.inject(<Entity>Repository);
});
it('should be created', () => {
expect(repository).toBeTruthy();
});
it('should have correct entity name', () => {
expect(<Entity>Repository.ENTITY_NAME).toBe('<Entity>');
});
});
Version: 1.0.0
Last Updated: December 27, 2025
Platform: editor2