generate angular view component

This commit is contained in:
Roland Schneider 2025-11-19 10:13:57 +01:00
parent 27829fc7ad
commit fa06cb3de4
5 changed files with 103 additions and 3 deletions

View File

@ -37,6 +37,8 @@ export class AngularGeneratorService {
await this.generateService(names, featureDir);
await this.generateFilterComponent(names, featureDir);
await this.generateListComponent(names, featureDir);
// NEW: Generate the details component
await this.generateDetailsComponent(names, featureDir);
const listComponentPath = path.join(
featureDir,
@ -45,6 +47,17 @@ export class AngularGeneratorService {
`${names.singular}-list.component.ts`,
);
const detailsComponentPath = path.join(
featureDir, 'components', `${names.singular}-details`, `${names.singular}-details.component.ts`
);
// Add details route FIRST, as it's more specific (`/products/:id`)
await this.moduleUpdaterService.addRouteToAngularApp(
`${names.pascal}DetailsComponent`,
detailsComponentPath,
`${names.plural}/:id`
);
await this.moduleUpdaterService.addRouteToAngularApp(
`${names.pascal}ListComponent`,
listComponentPath,
@ -52,13 +65,23 @@ export class AngularGeneratorService {
);
console.log(`✅ Angular files for "${tableName}" created successfully in: ${featureDir}`);
console.log('\n✨ app.routes.ts has been updated automatically! ✨');
console.log('\n✨ app.routes.ts has been updated automatically with list and details routes! ✨');
} catch (error) {
console.error(`❌ An error occurred during Angular generation:`, error.message);
}
}
private async generateDetailsComponent(names: NamingConvention, featureDir: string) {
const compDir = path.join(featureDir, 'components', `${names.singular}-details`);
const tsContent = this.templateService.render('angular/details.component.ts.tpl', names);
const htmlContent = this.templateService.render('angular/details.component.html.tpl', names);
await fsPromises.mkdir(compDir, { recursive: true });
fs.writeFileSync(path.join(compDir, `${names.singular}-details.component.ts`), tsContent);
fs.writeFileSync(path.join(compDir, `${names.singular}-details.component.html`), htmlContent);
}
private async generateModel(names: NamingConvention, featureDir: string) {
const modelsDir = path.join(featureDir, 'models');
const content = this.templateService.render('angular/model.ts.tpl', names);

View File

@ -0,0 +1,42 @@
<!-- dvbooking-cli/src/templates/angular/details.component.html.tpl -->
<!-- Generated by the CLI -->
<div class="p-4 md:p-8">
<ng-container *ngIf="{{camel}}$ | async as {{camel}}; else loading">
<div class="card bg-base-100 shadow-xl max-w-2xl mx-auto">
<div class="card-body">
<h2 class="card-title text-3xl">{{title}} Details</h2>
<!-- Details List -->
<div class="overflow-x-auto mt-4">
<table class="table w-full">
<tbody>
<!-- Row for ID -->
<tr>
<th class="w-1/3">ID</th>
<td>{{ {{camel}}.id }}</td>
</tr>
<!-- Row for Name -->
<tr>
<th>Name</th>
<td>{{ {{camel}}.name }}</td>
</tr>
<!-- Add more rows for other properties here -->
</tbody>
</table>
</div>
<div class="card-actions justify-end mt-6">
<a routerLink="/{{plural}}" class="btn btn-secondary">Back to List</a>
<button class="btn btn-primary">Edit</button>
</div>
</div>
</div>
</ng-container>
<ng-template #loading>
<div class="text-center p-8">
<span class="loading loading-spinner loading-lg"></span>
</div>
</ng-template>
</div>

View File

@ -0,0 +1,34 @@
// dvbooking-cli/src/templates/angular/details.component.ts.tpl
// Generated by the CLI
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { {{pascal}} } from '../../models/{{singular}}.model';
import { {{pascal}}Service } from '../../services/{{singular}}.service';
@Component({
selector: 'app-{{kebab}}-details',
templateUrl: './{{singular}}-details.component.html',
standalone: true,
imports: [CommonModule, RouterModule],
})
export class {{pascal}}DetailsComponent implements OnInit {
{{camel}}$!: Observable<{{pascal}}>;
constructor(
private route: ActivatedRoute,
private {{camel}}Service: {{pascal}}Service
) {}
ngOnInit(): void {
this.{{camel}}$ = this.route.params.pipe(
switchMap(params => {
const id = params['id'];
return this.{{camel}}Service.findOne(id);
})
);
}
}

View File

@ -23,7 +23,7 @@
<td>{{ item.name }}</td>
<!-- Add other table data cells here -->
<td class="text-right space-x-2">
<button class="btn btn-sm btn-ghost">View</button>
<a [routerLink]="['/{{plural}}', item.id]" class="btn btn-sm btn-ghost">View</a>
<button class="btn btn-sm btn-ghost">Edit</button>
<button (click)="deleteItem(item.id)" class="btn btn-sm btn-error btn-ghost">Delete</button>
</td>

View File

@ -3,6 +3,7 @@
// Generated by the CLI
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { BehaviorSubject, Observable, combineLatest } from 'rxjs';
import { switchMap, startWith } from 'rxjs/operators';
import { {{pascal}}, PaginatedResponse } from '../../models/{{singular}}.model';
@ -13,7 +14,7 @@ import { {{pascal}}FilterComponent } from '../{{singular}}-filter/{{singular}}-f
selector: 'app-{{kebab}}-list',
templateUrl: './{{singular}}-list.component.html',
standalone: true,
imports: [CommonModule, {{pascal}}FilterComponent],
imports: [CommonModule,RouterModule, {{pascal}}FilterComponent],
})
export class {{pascal}}ListComponent implements OnInit {