Generate Angular components, services, modules, and directives. Implement dependency injection, lifecycle hooks, data binding, and build production-ready Angular architectures.
Generates Angular components, services, modules, and directives with dependency injection and lifecycle hooks. Use when creating or refactoring Angular applications requiring production-ready architecture and patterns.
/plugin marketplace add pluginagentmarketplace/custom-plugin-angular/plugin install angular-development-assistant@pluginagentmarketplace-angularThis skill inherits all available tools. When active, it can use any tool Claude has access to.
assets/README.mdassets/component.template.tsassets/config.yamlreferences/GUIDE.mdreferences/LIFECYCLE.mdreferences/README.mdscripts/README.mdscripts/generate-component.shscripts/helper.pyimport { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-user-card',
template: `
<div class="card">
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
<button (click)="onDelete()">Delete</button>
</div>
`,
styles: [`
.card { border: 1px solid #ddd; padding: 16px; }
`]
})
export class UserCardComponent {
@Input() user!: User;
@Output() deleted = new EventEmitter<void>();
onDelete() {
this.deleted.emit();
}
}
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root' // Singleton service
})
export class UserService {
private apiUrl = '/api/users';
constructor(private http: HttpClient) {}
getUsers(): Observable<User[]> {
return this.http.get<User[]>(this.apiUrl);
}
getUser(id: number): Observable<User> {
return this.http.get<User>(`${this.apiUrl}/${id}`);
}
createUser(user: User): Observable<User> {
return this.http.post<User>(this.apiUrl, user);
}
}
@Injectable()
export class NotificationService {
constructor(
private logger: LoggerService,
private config: ConfigService
) {}
notify(message: string) {
this.logger.log(message);
}
}
export class UserListComponent implements
OnInit,
OnChanges,
OnDestroy
{
@Input() users: User[] = [];
ngOnInit() {
// Initialize component, fetch data
this.loadUsers();
}
ngOnChanges(changes: SimpleChanges) {
// Respond to input changes
if (changes['users']) {
this.onUsersChanged();
}
}
ngOnDestroy() {
// Cleanup subscriptions, remove listeners
this.subscription?.unsubscribe();
}
private loadUsers() { /* ... */ }
private onUsersChanged() { /* ... */ }
}
Lifecycle Order:
ngOnChanges - When input properties changengOnInit - After first ngOnChangesngDoCheck - Every change detection cyclengAfterContentInit - After content is initializedngAfterContentChecked - After content is checkedngAfterViewInit - After view is initializedngAfterViewChecked - After view is checkedngOnDestroy - When component is destroyedimport { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@NgModule({
declarations: [
UserListComponent,
UserDetailComponent,
UserFormComponent
],
imports: [
CommonModule,
FormsModule
],
exports: [
UserListComponent,
UserDetailComponent
]
})
export class UserModule { }
const routes: Routes = [
{ path: 'users', loadChildren: () =>
import('./users/users.module').then(m => m.UsersModule)
}
];
// Parent component
<app-card>
<div class="header">Card Title</div>
<div class="content">Card content</div>
</app-card>
// Card component
@Component({
selector: 'app-card',
template: `
<div class="card">
<ng-content select=".header"></ng-content>
<ng-content select=".content"></ng-content>
<ng-content></ng-content>
</div>
`
})
export class CardComponent { }
@Component({
selector: 'app-form',
template: `<app-input #firstInput></app-input>`
})
export class FormComponent implements AfterViewInit {
@ViewChild('firstInput') firstInput!: InputComponent;
ngAfterViewInit() {
this.firstInput.focus();
}
}
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) {
this.el.nativeElement.style.backgroundColor = 'yellow';
}
}
// Usage: <p appHighlight>Highlighted text</p>
@Component({
selector: 'app-card',
template: `<div class="card">...</div>`,
styles: [`.card { color: blue; }`],
encapsulation: ViewEncapsulation.Emulated // Default
})
export class CardComponent { }
@Component({
selector: 'app-user',
template: `<div>{{ user.name }}</div>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserComponent {
@Input() user!: User;
constructor(private cdr: ChangeDetectorRef) {}
manualDetection() {
this.cdr.markForCheck();
}
}
@NgModule({
providers: [
{ provide: VALIDATORS, useValue: emailValidator, multi: true },
{ provide: VALIDATORS, useValue: minLengthValidator, multi: true }
]
})
export class ValidatorsModule { }
@NgModule({
providers: [
{
provide: ConfigService,
useFactory: (env: EnvironmentService) => {
return env.production ?
new ProdConfigService() :
new DevConfigService();
},
deps: [EnvironmentService]
}
]
})
export class AppModule { }
describe('UserCardComponent', () => {
let component: UserCardComponent;
let fixture: ComponentFixture<UserCardComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [UserCardComponent]
}).compileComponents();
fixture = TestBed.createComponent(UserCardComponent);
component = fixture.componentInstance;
});
it('should emit deleted when delete button clicked', () => {
spyOn(component.deleted, 'emit');
component.user = { id: 1, name: 'John', email: 'john@example.com' };
fixture.detectChanges();
fixture.debugElement.query(By.css('button')).nativeElement.click();
expect(component.deleted.emit).toHaveBeenCalled();
});
});
// Bad
users: User[] = [];
// Good
users$ = this.userService.getUsers();
<!-- Template -->
<app-user *ngFor="let user of users$ | async; trackBy: trackByUserId">
</app-user>
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.