angular-core-implementation

Angular Core Implementation Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "angular-core-implementation" with this command: npx skills add pluginagentmarketplace/custom-plugin-angular/pluginagentmarketplace-custom-plugin-angular-angular-core-implementation

Angular Core Implementation Skill

Quick Start

Component Basics

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({ selector: 'app-user-card', template: &#x3C;div class="card"> &#x3C;h2>{{ user.name }}&#x3C;/h2> &#x3C;p>{{ user.email }}&#x3C;/p> &#x3C;button (click)="onDelete()">Delete&#x3C;/button> &#x3C;/div> , styles: [ .card { border: 1px solid #ddd; padding: 16px; } ] }) export class UserCardComponent { @Input() user!: User; @Output() deleted = new EventEmitter<void>();

onDelete() { this.deleted.emit(); } }

Service Creation

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); } }

Dependency Injection

@Injectable() export class NotificationService { constructor( private logger: LoggerService, private config: ConfigService ) {}

notify(message: string) { this.logger.log(message); } }

Core Concepts

Lifecycle Hooks

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 change

  • ngOnInit

  • After first ngOnChanges

  • ngDoCheck

  • Every change detection cycle

  • ngAfterContentInit

  • After content is initialized

  • ngAfterContentChecked

  • After content is checked

  • ngAfterViewInit

  • After view is initialized

  • ngAfterViewChecked

  • After view is checked

  • ngOnDestroy

  • When component is destroyed

Modules

import { 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 { }

Lazy Loading

const routes: Routes = [ { path: 'users', loadChildren: () => import('./users/users.module').then(m => m.UsersModule) } ];

Advanced Patterns

Content Projection

// 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: &#x3C;div class="card"> &#x3C;ng-content select=".header">&#x3C;/ng-content> &#x3C;ng-content select=".content">&#x3C;/ng-content> &#x3C;ng-content>&#x3C;/ng-content> &#x3C;/div> }) export class CardComponent { }

ViewChild and ContentChild

@Component({ selector: 'app-form', template: &#x3C;app-input #firstInput>&#x3C;/app-input> }) export class FormComponent implements AfterViewInit { @ViewChild('firstInput') firstInput!: InputComponent;

ngAfterViewInit() { this.firstInput.focus(); } }

Custom Directive

@Directive({ selector: '[appHighlight]' }) export class HighlightDirective { constructor(private el: ElementRef) { this.el.nativeElement.style.backgroundColor = 'yellow'; } }

// Usage: <p appHighlight>Highlighted text</p>

Encapsulation

View Encapsulation Modes

@Component({ selector: 'app-card', template: &#x3C;div class="card">...&#x3C;/div>, styles: [.card { color: blue; }], encapsulation: ViewEncapsulation.Emulated // Default }) export class CardComponent { }

  • Emulated (default): CSS scoped to component

  • None: Global styles

  • ShadowDom: Uses browser shadow DOM

Change Detection

OnPush Strategy

@Component({ selector: 'app-user', template: &#x3C;div>{{ user.name }}&#x3C;/div>, changeDetection: ChangeDetectionStrategy.OnPush }) export class UserComponent { @Input() user!: User;

constructor(private cdr: ChangeDetectorRef) {}

manualDetection() { this.cdr.markForCheck(); } }

Provider Patterns

Multi-Provider

@NgModule({ providers: [ { provide: VALIDATORS, useValue: emailValidator, multi: true }, { provide: VALIDATORS, useValue: minLengthValidator, multi: true } ] }) export class ValidatorsModule { }

Factory Pattern

@NgModule({ providers: [ { provide: ConfigService, useFactory: (env: EnvironmentService) => { return env.production ? new ProdConfigService() : new DevConfigService(); }, deps: [EnvironmentService] } ] }) export class AppModule { }

Testing Components

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();

}); });

Performance Optimization

  • Use OnPush: Reduces change detection cycles

  • Unsubscribe: Prevent memory leaks

  • TrackBy: Optimize *ngFor rendering

  • Lazy Load: Load modules on demand

  • Avoid property binding in templates: Use async pipe

// Bad users: User[] = [];

// Good users$ = this.userService.getUsers();

<!-- Template --> <app-user *ngFor="let user of users$ | async; trackBy: trackByUserId"> </app-user>

Best Practices

  • Smart vs Presentational: Container components handle logic

  • One Responsibility: Each component has a single purpose

  • Input/Output: Use @Input/@Output for communication

  • Services: Handle business logic and HTTP

  • DI: Always use dependency injection

  • OnDestroy: Clean up subscriptions

Resources

  • Angular Documentation

  • Angular Best Practices

  • Component Interaction

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

angular-material

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

rxjs-implementation

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

state-implementation

No summary provided by upstream source.

Repository SourceNeeds Review