6.0 KiB
6.0 KiB
name, description
| name | description |
|---|---|
| create-component | Generate a complete Angular 17+ standalone component with template, styles, and optional tests following best practices |
Generate a production-ready Angular component with all necessary files and configurations.
Component Types
Ask the user which type:
- Smart Component (Container) - Manages data and business logic
- Dumb Component (Presentational) - Displays data only
- Form Component - Handles form input
- Layout Component - App structure (header, sidebar, etc.)
Information Needed
- Component name - kebab-case (e.g.,
user-profile) - Component type - Smart or Dumb
- Location - Feature folder or shared
- Include tests? - Yes/No
- Data to display - What data does it work with?
- Actions - What actions can users perform?
Generated Files
component-name/
├── component-name.component.ts # TypeScript class
├── component-name.component.html # Template
├── component-name.component.scss # Styles
└── component-name.component.spec.ts # Tests (optional)
Smart Component Template
import { Component, signal, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ComponentNameService } from './services/component-name.service';
@Component({
selector: 'app-component-name',
standalone: true,
imports: [CommonModule],
templateUrl: './component-name.component.html',
styleUrls: ['./component-name.component.scss']
})
export class ComponentNameComponent {
private service = inject(ComponentNameService);
data = signal<DataType[]>([]);
loading = signal(false);
error = signal<string | null>(null);
ngOnInit() {
this.loadData();
}
loadData() {
this.loading.set(true);
this.service.getData().subscribe({
next: data => {
this.data.set(data);
this.loading.set(false);
},
error: err => {
this.error.set(err.message);
this.loading.set(false);
}
});
}
handleAction(id: string) {
// Handle user action
}
}
Dumb Component Template
import { Component, input, output, ChangeDetectionStrategy } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-component-name',
standalone: true,
imports: [CommonModule],
templateUrl: './component-name.component.html',
styleUrls: ['./component-name.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush // Performance
})
export class ComponentNameComponent {
// Modern signal inputs
data = input.required<DataType>();
disabled = input(false);
// Modern signal outputs
action = output<string>();
delete = output<string>();
handleClick() {
this.action.emit(this.data().id);
}
}
Template Structure
For List Components
<div class="component-name-container">
@if (loading()) {
<app-loading-spinner />
} @else if (error()) {
<app-error-message [message]="error()" />
} @else {
@for (item of data(); track item.id) {
<div class="item">
{{ item.name }}
</div>
} @empty {
<p class="empty-state">No items found</p>
}
}
</div>
For Detail Components
<div class="component-name-detail">
@if (data(); as item) {
<h1>{{ item.title }}</h1>
<p>{{ item.description }}</p>
<div class="actions">
<button (click)="handleEdit()">Edit</button>
<button (click)="handleDelete()">Delete</button>
</div>
}
</div>
Styling Template (SCSS)
:host {
display: block;
}
.component-name-container {
padding: 1rem;
.item {
margin-bottom: 1rem;
padding: 1rem;
border: 1px solid #e0e0e0;
border-radius: 8px;
&:hover {
background-color: #f5f5f5;
}
}
.empty-state {
text-align: center;
color: #666;
padding: 2rem;
}
}
// Responsive
@media (max-width: 768px) {
.component-name-container {
padding: 0.5rem;
}
}
Test Template
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentNameComponent } from './component-name.component';
describe('ComponentNameComponent', () => {
let component: ComponentNameComponent;
let fixture: ComponentFixture<ComponentNameComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ComponentNameComponent]
}).compileComponents();
fixture = TestBed.createComponent(ComponentNameComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should display data', () => {
component.data.set([{ id: '1', name: 'Test' }]);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('.item')).toBeTruthy();
});
});
Best Practices to Include
- Always standalone: true
- Separate template and style files (never inline)
- OnPush for dumb components
- Use signals for local state
- TrackBy in @for loops
- Proper error handling
- Loading states
- Empty states
- Accessibility attributes
- Responsive design
Component Checklist
Generated component should have:
- Standalone: true
- Proper imports
- Separate template file
- Separate styles file
- Signal-based state (if smart)
- Input/Output (if dumb)
- OnPush (if dumb)
- TrackBy functions
- Error handling
- Loading states
- Tests (if requested)
Usage
# In Claude Code
/angular-development:create-component
# Natural language
"Create a smart component called product-list that displays products"
"Generate a dumb component for user-card with name and email inputs"
Notes
- Smart components go in
features/<feature>/components/ - Dumb components go in
shared/components/ - Always ask about data structure before generating
- Include proper TypeScript types
- Add comments for complex logic