Scaffold a new Angular feature in the ProperTea Landlord Portal. Use when creating a new page, CRUD interface, or feature module in the frontend.
You are scaffolding a new feature for the ProperTea Landlord Portal.
src/app/features/ for pattern reference.src/app/features/{feature-name}/
├── routes.ts # Lazy-loaded route definitions
├── models/
│ └── {feature-name}.models.ts # TypeScript interfaces
├── services/
│ └── {feature-name}.service.ts # HTTP service
├── list-view/
│ ├── {feature-name}-list.component.ts
│ └── {feature-name}-list.component.html # Only if template is large
├── details/
│ ├── {feature-name}-details.component.ts
│ └── {feature-name}-details.component.html
└── create-drawer/ # If feature supports creation
└── create-{feature-name}-drawer.component.ts
import { Routes } from '@angular/router';
export const {featureName}Routes: Routes = [
{
path: '',
loadComponent: () =>
import('./list-view/{feature-name}-list.component')
.then(m => m.{FeatureName}ListComponent),
},
{
path: ':id',
loadComponent: () =>
import('./details/{feature-name}-details.component')
.then(m => m.{FeatureName}DetailsComponent),
},
];
Register in app.routes.ts:
{
path: '{feature-name}',
loadChildren: () =>
import('./features/{feature-name}/routes')
.then(m => m.{featureName}Routes),
},
@Injectable({ providedIn: 'root' })
export class {FeatureName}Service {
private http = inject(HttpClient);
list(params?: PaginationParams) {
return this.http.get<PagedResult<{FeatureName}>>('/api/{feature-name}', { params });
}
getById(id: string) {
return this.http.get<{FeatureName}>(`/api/{feature-name}/${id}`);
}
create(request: Create{FeatureName}Request) {
return this.http.post<{ id: string }>('/api/{feature-name}', request);
}
}
@Component({
selector: 'app-{feature-name}-list',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [/* only what's needed */],
template: `
<!-- Use TanStack Table via EntityListViewComponent for list views -->
<!-- Use native control flow: @if, @for -->
<!-- All strings via transloco pipe -->
`,
})
export class {FeatureName}ListComponent {
private service = inject({FeatureName}Service);
items = signal<{FeatureName}[]>([]);
loading = signal(false);
totalCount = computed(() => this.items().length);
}
ChangeDetectionStrategy.OnPush on every component.input() / output() functions, not decorators.inject() for DI, not constructor.computed() for all state. No BehaviorSubject.@if, @for). Never *ngIf, *ngFor.ngClass/ngStyle. Use class/style bindings.| transloco).EntityListViewComponent + TanStack Table for data tables.app.routes.tsinject(HttpClient)OnPush, signals, input()/output()assets/i18n/en.json and assets/i18n/uk.json