diff --git a/docs/angular/components.md b/docs/angular/components.md new file mode 100644 index 0000000..52a0037 --- /dev/null +++ b/docs/angular/components.md @@ -0,0 +1,80 @@ +# Angular Components + +## What is a Component? +A component controls a patch of screen called a view. It consists of: +- A TypeScript class (component class) +- An HTML template +- Optional CSS styles + +## Creating a Component +```bash +ng generate component my-component +# or shorthand +ng g c my-component +``` + +## Component Structure +```typescript +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-my-component', + templateUrl: './my-component.component.html', + styleUrls: ['./my-component.component.css'] +}) +export class MyComponent { + // Component logic goes here + title = 'My Component'; +} +``` + +## Component Lifecycle Hooks +- `ngOnInit()`: Called after the first ngOnChanges +- `ngOnChanges()`: Called when input properties change +- `ngDoCheck()`: Custom change detection +- `ngAfterViewInit()`: Called after the view is initialized +- `ngOnDestroy()`: Cleanup just before Angular destroys the component + +## Component Communication + +### Parent to Child: @Input() +```typescript +// parent.component.html + + +// child.component.ts +@Input() message: string; +``` + +### Child to Parent: @Output() +```typescript +// child.component.ts +@Output() notify = new EventEmitter(); + +onClick() { + this.notify.emit('Button clicked!'); +} + +// parent.component.html + +``` + +## View Encapsulation +Angular supports three view encapsulation strategies: +- `ViewEncapsulation.Emulated` (default) +- `ViewEncapsulation.None` +- `ViewEncapsulation.ShadowDom` + +## Content Projection +```html + + +

Card Title

+

Card content goes here

+
+ + +
+ +
+``` diff --git a/docs/angular/forms.md b/docs/angular/forms.md new file mode 100644 index 0000000..f0e012f --- /dev/null +++ b/docs/angular/forms.md @@ -0,0 +1,217 @@ +# Angular Forms + +## Template-Driven Forms + +### Basic Setup +```typescript +// app.module.ts +import { FormsModule } from '@angular/forms'; + +@NgModule({ + imports: [ + FormsModule + ] +}) +``` + +### Basic Form +```html +
+
+ + +
+ +
+ + +
+ + +
+``` + +### Two-way Binding +```html + +``` + +## Reactive Forms + +### Setup +```typescript +// app.module.ts +import { ReactiveFormsModule } from '@angular/forms'; + +@NgModule({ + imports: [ + ReactiveFormsModule + ] +}) +``` + +### Basic Form +```typescript +// In component +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; + +export class LoginComponent { + loginForm: FormGroup; + + constructor(private fb: FormBuilder) { + this.loginForm = this.fb.group({ + email: ['', [Validators.required, Validators.email]], + password: ['', [Validators.required, Validators.minLength(6)]] + }); + } + + onSubmit() { + if (this.loginForm.valid) { + console.log(this.loginForm.value); + } + } +} +``` + +```html +
+
+ + +
+ Email is required +
+
+ +
+ + +
+ + +
+``` + +### Form Arrays +```typescript +// In component +export class OrderFormComponent { + orderForm: FormGroup; + + constructor(private fb: FormBuilder) { + this.orderForm = this.fb.group({ + items: this.fb.array([this.createItem()]) + }); + } + + createItem(): FormGroup { + return this.fb.group({ + name: ['', Validators.required], + quantity: [1, [Validators.required, Validators.min(1)]] + }); + } + + get items() { + return this.orderForm.get('items') as FormArray; + } + + addItem() { + this.items.push(this.createItem()); + } + + removeItem(index: number) { + this.items.removeAt(index); + } +} +``` + +### Custom Validators +```typescript +export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { + return (control: AbstractControl): {[key: string]: any} | null => { + const forbidden = nameRe.test(control.value); + return forbidden ? {forbiddenName: {value: control.value}} : null; + }; +} + +// Usage +this.heroForm = this.fb.group({ + name: ['', [ + Validators.required, + forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator + ]] +}); +``` + +### Dynamic Forms +```typescript +// In component +export class DynamicFormComponent { + form: FormGroup; + fields = [ + { type: 'text', name: 'firstName', label: 'First Name', required: true }, + { type: 'email', name: 'email', label: 'Email', required: true }, + { type: 'password', name: 'password', label: 'Password', required: true } + ]; + + constructor(private fb: FormBuilder) { + const group = {}; + this.fields.forEach(field => { + group[field.name] = ['', field.required ? Validators.required : []]; + }); + this.form = this.fb.group(group); + } +} +``` + +### Form Submission with HTTP +```typescript +// In component +export class UserFormComponent { + userForm: FormGroup; + + constructor(private fb: FormBuilder, private http: HttpClient) { + this.userForm = this.fb.group({ + name: ['', Validators.required], + email: ['', [Validators.required, Validators.email]] + }); + } + + onSubmit() { + if (this.userForm.valid) { + this.http.post('/api/users', this.userForm.value).subscribe( + response => console.log('Success!', response), + error => console.error('Error!', error) + ); + } + } +} +``` + +### Cross-Field Validation +```typescript +// In component +this.form = this.fb.group({ + password: ['', [Validators.required]], + confirmPassword: ['', [Validators.required]] +}, { validator: this.passwordMatchValidator }); + +passwordMatchValidator(g: FormGroup) { + return g.get('password').value === g.get('confirmPassword').value + ? null : { 'mismatch': true }; +} +``` + +### Form Status Changes +```typescript +// In component +ngOnInit() { + this.form.statusChanges.subscribe(status => { + console.log('Form status:', status); + }); + + this.form.get('email').valueChanges.subscribe(value => { + console.log('Email changed to:', value); + }); +} +``` diff --git a/docs/angular/routing.md b/docs/angular/routing.md new file mode 100644 index 0000000..7466240 --- /dev/null +++ b/docs/angular/routing.md @@ -0,0 +1,167 @@ +# Angular Routing + +## Basic Setup + +```typescript +// app-routing.module.ts +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { HomeComponent } from './home/home.component'; +import { AboutComponent } from './about/about.component'; + +const routes: Routes = [ + { path: '', component: HomeComponent }, + { path: 'about', component: AboutComponent }, + { path: '**', redirectTo: '' } // Wildcard route for 404 +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule] +}) +export class AppRoutingModule { } +``` + +## Route Parameters + +```typescript +// Route definition +{ path: 'product/:id', component: ProductDetailComponent } + +// Accessing in component +import { ActivatedRoute } from '@angular/router'; + +constructor(private route: ActivatedRoute) { + this.route.params.subscribe(params => { + this.productId = params['id']; + }); +} +``` + +## Child Routes + +```typescript +const routes: Routes = [ + { + path: 'products', + component: ProductsComponent, + children: [ + { path: '', component: ProductListComponent }, + { path: ':id', component: ProductDetailComponent }, + { path: ':id/edit', component: ProductEditComponent } + ] + } +]; +``` + +## Route Guards + +### CanActivate +```typescript +@Injectable({ + providedIn: 'root' +}) +export class AuthGuard implements CanActivate { + constructor(private authService: AuthService, private router: Router) {} + + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot): boolean { + if (this.authService.isLoggedIn()) { + return true; + } + this.router.navigate(['/login']); + return false; + } +} + +// Usage +{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] } +``` + +### Resolve +```typescript +@Injectable({ providedIn: 'root' }) +export class ProductResolver implements Resolve { + constructor(private productService: ProductService) {} + + resolve(route: ActivatedRouteSnapshot): Observable { + return this.productService.getProduct(route.paramMap.get('id')); + } +} + +// Usage +{ + path: 'product/:id', + component: ProductDetailComponent, + resolve: { + product: ProductResolver + } +} +``` + +## Lazy Loading + +```typescript +const routes: Routes = [ + { + path: 'admin', + loadChildren: () => import('./admin/admin.module') + .then(m => m.AdminModule) + } +]; +``` + +## Navigation + +```typescript +// In component +constructor(private router: Router) {} + +gotoProduct(id: number) { + this.router.navigate(['/product', id]); + // or with query params + this.router.navigate(['/products'], { + queryParams: { page: 1, search: 'angular' } + }); +} +``` + +## Route Events + +```typescript +constructor(private router: Router) { + this.router.events.pipe( + filter(event => event instanceof NavigationEnd) + ).subscribe((event: NavigationEnd) => { + console.log('Navigation ended:', event.url); + }); +} +``` + +## Route Animations + +```typescript +// In component +@Component({ + selector: 'app-root', + template: ` +
+ +
+ `, + animations: [ + trigger('routeAnimations', [ + transition('* <=> *', [ + style({ opacity: 0 }), + animate('300ms', style({ opacity: 1 })) + ]) + ]) + ] +}) +export class AppComponent { + prepareRoute(outlet: RouterOutlet) { + return outlet?.activatedRouteData?.['animation']; + } +} +``` diff --git a/docs/angular/services.md b/docs/angular/services.md new file mode 100644 index 0000000..ed74973 --- /dev/null +++ b/docs/angular/services.md @@ -0,0 +1,119 @@ +# Angular Services + +## What is a Service? +Services are singleton objects that provide specific functionality throughout the application. They are typically used for: +- Data fetching and sharing +- Business logic +- Logging +- Authentication + +## Creating a Service +```bash +ng generate service data +# or shorthand +ng g s data +``` + +## Basic Service Example +```typescript +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' // Makes it a singleton service +}) +export class DataService { + private data: any[] = []; + + constructor() { } + + getData(): any[] { + return this.data; + } + + addData(item: any): void { + this.data.push(item); + } +} +``` + +## Dependency Injection +```typescript +// In a component +constructor(private dataService: DataService) {} + +// In a module (if not using providedIn: 'root') +@NgModule({ + providers: [DataService] +}) +``` + +## HTTP Client +```typescript +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class ApiService { + private apiUrl = 'https://api.example.com'; + + constructor(private http: HttpClient) {} + + getItems(): Observable { + return this.http.get(`${this.apiUrl}/items`); + } + + addItem(item: any): Observable { + return this.http.post(`${this.apiUrl}/items`, item); + } +} +``` + +## HTTP Interceptors +```typescript +import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +@Injectable() +export class AuthInterceptor implements HttpInterceptor { + intercept(req: HttpRequest, next: HttpHandler): Observable> { + const token = localStorage.getItem('token'); + + if (token) { + const cloned = req.clone({ + headers: req.headers.set('Authorization', `Bearer ${token}`) + }); + return next.handle(cloned); + } + + return next.handle(req); + } +} +``` + +## Error Handling +```typescript +import { catchError } from 'rxjs/operators'; +import { throwError } from 'rxjs'; + +getItems(): Observable { + return this.http.get(`${this.apiUrl}/items`).pipe( + catchError(error => { + console.error('Error fetching items:', error); + return throwError('Something went wrong'); + }) + ); +} +``` + +## Service Lifecycle Hooks +- `ngOnInit()`: Not available in services +- `ngOnDestroy()`: Can be used to clean up subscriptions + +## Best Practices +- Keep services focused on a single responsibility +- Use `providedIn: 'root'` for singleton services +- Handle errors appropriately +- Unsubscribe from observables to prevent memory leaks +- Use interfaces for request/response types diff --git a/docs/angular/setup.md b/docs/angular/setup.md new file mode 100644 index 0000000..431c8b7 --- /dev/null +++ b/docs/angular/setup.md @@ -0,0 +1,56 @@ +# Angular Setup Guide + +## Prerequisites +- Node.js (v14 or later) +- npm (v6 or later) +- Angular CLI (latest stable version) + +## Installation + +1. Install Angular CLI globally: +```bash +npm install -g @angular/cli +``` + +2. Verify installation: +```bash +ng version +``` + +## Creating a New Project +```bash +ng new my-angular-app +cd my-angular-app +``` + +## Project Structure +``` +my-angular-app/ +├── src/ +│ ├── app/ # Your application code +│ ├── assets/ # Static assets +│ ├── environments/ # Environment configurations +│ └── index.html # Main HTML file +├── angular.json # Angular CLI configuration +└── package.json # Project dependencies +``` + +## Development Server +```bash +# Start development server +ng serve + +# Open in browser +ng serve --open +``` + +## Building for Production +```bash +ng build --prod +``` + +## Common Commands +- `ng generate component component-name` - Generate a new component +- `ng generate service service-name` - Generate a new service +- `ng test` - Run unit tests +- `ng e2e` - Run end-to-end tests