TypeScript Basics
Basic Types
TypeScript supports various basic types, including string, number, boolean, null, undefined, any, void, never, and unknown.
let name: string = "John Doe";
let age: number = 30;
let isStudent: boolean = false;
let nothing: null = null;
let nothingDefined: undefined = undefined;
let anything: any = "This can be anything";
let nothingToReturn: void = undefined;
let neverEnding: never = (() => { throw new Error("This function never ends!"); })();
let unknownValue: unknown = "This could be any type";
console.log(name);
console.log(age);
console.log(isStudent);
console.log(nothing);
console.log(nothingDefined);
console.log(anything);
console.log(nothingToReturn);
console.log(neverEnding);
console.log(unknownValue);Arrays and Tuples
Arrays are defined with a [] suffix, while tuples are fixed-length arrays with specific types.
let numbers: number[] = [1, 2, 3];
let strings: Array<string> = ["Hello", "World"];
let mixedTuple: [number, string] = [1, "two"];
console.log(numbers);
console.log(strings);
console.log(mixedTuple);Enums
Enums allow defining a set of named constants.
enum Color { Red, Green, Blue }
let c: Color = Color.Green;
console.log(c); // Outputs 1, as the default starts at 0 and incrementsObject Types
Object types are defined with key-value pairs, where keys are strings or symbols and values are any type.
let person: { name: string, age: number } = {
name: "John",
age: 30
};
console.log(person);Type Aliases
Type aliases assign a name to a type, improving code readability.
type Person = { name: string, age: number };
let person: Person = {
name: "Jane",
age: 25
};
console.log(person);Interfaces
Interfaces describe the shape of objects, defining required properties and methods.
interface Person {
name: string;
age: number;
}
let person: Person = {
name: "Jane",
age: 25
};
console.log(person);Classes
Classes support object-oriented programming with properties, methods, inheritance, and encapsulation.
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
speak(): string {
return `${this.name} makes a noise.`;
}
}
class Dog extends Animal {
constructor(name: string) {
super(name);
}
speak(): string {
return `${this.name} barks.`;
}
}
let animal = new Animal("Animal");
let dog = new Dog("Dog");
console.log(animal.speak());
console.log(dog.speak());Generics
Generics enable reusable functions and classes that work with different types at runtime.
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("Hello World");
console.log(output);Function Types
Functions can have typed parameters and return values.
function add(x: number, y: number): number {
return x + y;
}
console.log(add(1, 2));Modules
Modules organize code into reusable units.
// math.ts
export function add(x: number, y: number): number {
return x + y;
}
// main.ts
import { add } from "./math";
console.log(add(1, 2));Namespaces
Namespaces organize and encapsulate code to prevent global naming conflicts, though modules are preferred in modern TypeScript.
// shapes.ts
namespace Shapes {
export function area(rectangle: { width: number; height: number }): number {
return rectangle.width * rectangle.height;
}
}
// main.ts
import * as Shapes from "./shapes";
console.log(Shapes.area({ width: 10, height: 5 }));Decorators
Decorators are special declarations that modify class behavior, applied at compile time.
function logClass(target: Function) {
console.log(`Logging class ${target.name}`);
}
@logClass
class MyClass {
doSomething() {
console.log("Doing something...");
}
}
new MyClass().doSomething();Type Guards
Type guards check types at runtime, often used in conditional statements.
function isString(value: any): value is string {
return typeof value === "string";
}
let myValue: any = "Hello";
if (isString(myValue)) {
console.log(myValue.toUpperCase());
}Type Compatibility
Type compatibility determines if one type can be assigned to another, crucial for avoiding type errors.
interface Shape {
draw(): void;
}
class Circle implements Shape {
draw() {
console.log("Drawing a circle...");
}
}
class Square implements Shape {
draw() {
console.log("Drawing a square...");
}
}
function drawShape(shape: Shape) {
shape.draw();
}
drawShape(new Circle());
drawShape(new Square());Type Inference
TypeScript infers variable types when not explicitly specified.
let message = "Hello TypeScript!";
console.log(message.length); // Inferred as stringUtility Types
Utility types provide predefined types for flexible and precise type definitions.
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Partial<Todo>;
let todo: TodoPreview = {
title: "Learn TypeScript",
description: "Start with the basics"
};
console.log(todo);Conditional Types
Conditional types select types based on conditions.
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" : "object";
console.log(TypeName<string>("hello")); // Outputs "string"Intersection Types
Intersection types (&) combine multiple types into one.
interface Person {
name: string;
}
interface Developer {
language: string;
}
type FullStackDeveloper = Person & Developer;
let dev: FullStackDeveloper = {
name: "John Doe",
language: "TypeScript"
};
console.log(dev);Union Types
Union types (|) indicate a value can be one of several types.
function printId(id: number | string) {
console.log(id);
}
printId(1); // Outputs 1
printId("hello"); // Outputs "hello"Type Assertions
Type assertions inform TypeScript of a value’s type using <type> or as type syntax.
let someValue: any = "This is a string";
let strLength: number = (<string>someValue).length;
let strLength2: number = (someValue as string).length;
console.log(strLength); // Outputs 17
console.log(strLength2); // Outputs 17Angular Template Syntax
Interpolation
Interpolation, using double curly braces {{ }}, inserts component property values into the template.
<!-- app.component.html -->
<div>
Hello, {{ name }}!
</div>// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
name = 'World';
}Template Expressions
Template expressions are JavaScript expressions within {{ }}, used to insert computed values.
<div>{{ firstName + ' ' + lastName }}</div>Here, firstName and lastName are component properties, concatenated and displayed.
Property Binding
Property binding binds component properties to HTML element attributes using square brackets [ ].
<!-- app.component.html -->
<img [src]="imageUrl">
<button type="button" [disabled]="isUnchanged">Disabled Button</button>// app.component.ts
export class AppComponent {
imageUrl = 'https://example.com/image.jpg';
isUnchanged = true;
}Event Binding
Event binding associates component methods with HTML element events using parentheses ( ).
<!-- app.component.html -->
<button (click)="onButtonClick()">Click me</button>// app.component.ts
export class AppComponent {
onButtonClick() {
console.log('Button clicked!');
}
}Binding to Keyboard Events
Angular supports binding to keyboard events, specifying keys or codes. The key and code fields are native parts of browser keyboard event objects. By default, event bindings use the key field, but code can be used for precision.
Key combinations are separated by dots (.). For example, keydown.enter binds to the Enter key. Modifier keys like shift, alt, control, and command (on Mac) are supported. The following binds to keydown.shift.t:
<input (keydown.shift.t)="onKeydown($event)" />On macOS, certain key combinations (e.g., shift + option) may produce special characters. To bind keydown.shift.alt.t on macOS, use the code field, e.g., keydown.code.shiftleft.altleft.keyt:
<input (keydown.code.shiftleft.altleft.keyt)="onKeydown($event)" />The code field is more specific than key (e.g., distinguishing shiftleft from shiftright) and avoids OS-specific behaviors.
Class and Style Binding
Class Binding: Use [class.className]="expression" to dynamically add or remove classes based on an expression.
<div [class.active]="isActive">Content</div>
<div [class]="class1 class2 class3">Content</div>When isActive is true, the active class is applied.
Style Binding: Use [style.stylePropertyName]="expression" to dynamically set styles.
<div [style.color]="isActive ? 'green' : 'red'">Color Changes</div>The font color toggles between green and red based on isActive.
Two-Way Binding
Two-way binding, using [()] (banana-in-a-box syntax), combines property and event binding, commonly used with form controls.
<input [(ngModel)]="userName">The ngModel directive synchronizes the userName property with the input value.
Control Flow
Control flow directives manage template structure based on conditions or loops.
ngIf: Conditionally renders elements.
<div *ngIf="isLoggedIn">Welcome back!</div>The message displays only if isLoggedIn is true.
ngFor: Iterates over arrays or objects to render lists.
<li *ngFor="let item of items">{{ item }}</li>Iterates over the items array, generating a list item per element.
ngSwitch: Renders different template blocks based on an expression’s value.
<div [ngSwitch]="userRole">
<div *ngSwitchCase="'admin'">Admin Panel</div>
<div *ngSwitchCase="'editor'">Editor Tools</div>
<div *ngSwitchDefault>User Profile</div>
</div>Displays content based on userRole.
Structural Directives
Structural directives manipulate the DOM structure, such as *ngIf and *ngFor.
<!-- app.component.html -->
<div *ngIf="showMessage">
This is a message.
</div>
<ul>
<li *ngFor="let item of items">
{{ item }}
</li>
</ul>// app.component.ts
export class AppComponent {
showMessage = true;
items = ['Item 1', 'Item 2', 'Item 3'];
}Pipes
Pipes transform data in templates, such as formatting dates or converting text case.
<!-- app.component.html -->
<p>{{ date | date: 'fullDate' }}</p>// app.component.ts
export class AppComponent {
date = new Date();
}Form Binding
Angular supports two form binding approaches: template-driven and reactive forms.
Template-Driven Form Example
<!-- app.component.html -->
<form (ngSubmit)="onSubmit()">
<input type="text" [(ngModel)]="username" name="username">
<button type="submit">Submit</button>
</form>// app.component.ts
export class AppComponent {
username = '';
onSubmit() {
console.log(this.username);
}
}Reactive Form Example
// app.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
formGroup: FormGroup;
constructor(private fb: FormBuilder) {
this.formGroup = this.fb.group({
username: ''
});
}
onSubmit() {
console.log(this.formGroup.value);
}
}<!-- app.component.html -->
<form [formGroup]="formGroup" (ngSubmit)="onSubmit()">
<input type="text" formControlName="username">
<button type="submit">Submit</button>
</form>Template Reference Variables
Template reference variables reference elements or directives in the template.
<!-- app.component.html -->
<input #usernameInput type="text">
<button (click)="logInput(usernameInput)">Log Input</button>// app.component.ts
export class AppComponent {
logInput(inputElement: HTMLInputElement) {
console.log(inputElement.value);
}
}Local Variables
Local variables, defined with let, are used with structural directives.
<!-- app.component.html -->
<ul>
<li *ngFor="let item of items; let i = index">
Item {{ i + 1 }}: {{ item }}
</li>
</ul>Conditional Structure
*ngIf and *ngSwitch display or hide template sections based on conditions.
<!-- app.component.html -->
<div *ngIf="showMessage; else noMessage">
This is a message.
</div>
<ng-template #noMessage>
No message to show.
</ng-template>Custom Directives
Custom directives extend Angular’s functionality.
// highlight.directive.ts
import { Directive, ElementRef, Renderer2, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
@Input() set appHighlight(color: string) {
this.renderer.setStyle(this.elRef.nativeElement, 'background-color', color);
}
constructor(private elRef: ElementRef, private renderer: Renderer2) {}
}<!-- app.component.html -->
<div appHighlight="yellow">
This text will have a yellow background.
</div>Angular Directives
Attribute Directives
Attribute directives modify HTML element attributes or styles.
1. NgClass
The NgClass directive dynamically adds or removes CSS classes based on conditions.
<!-- app.component.html -->
<div [ngClass]="{'red-text': isRed, 'large-font': isLarge}">
This text's style will change based on conditions.
</div>// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
})
export class AppComponent {
isRed = true;
isLarge = false;
}2. NgStyle
The NgStyle directive dynamically sets inline styles.
<!-- app.component.html -->
<div [ngStyle]="{'font-size.px': fontSize, 'color': textColor}">
This text's style will change based on conditions.
</div>// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
})
export class AppComponent {
fontSize = 20;
textColor = 'blue';
}3. NgModel
The NgModel directive enables two-way data binding, commonly used with form controls.
<!-- app.component.html -->
<input [(ngModel)]="message">
<p>You typed: {{ message }}</p>// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
})
export class AppComponent {
message = '';
}Structural Directives
Structural directives manipulate the DOM structure by adding or removing elements.
1. NgIf
The NgIf directive conditionally displays or hides elements.
<!-- app.component.html -->
<div *ngIf="isVisible">
This text will be visible when 'isVisible' is true.
</div>// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
})
export class AppComponent {
isVisible = true;
}2. NgFor
The NgFor directive iterates over arrays or iterables, generating template instances.
<!-- app.component.html -->
<ul>
<li *ngFor="let item of items">
{{ item }}
</li>
</ul>// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
})
export class AppComponent {
items = ['Apple', 'Banana', 'Cherry'];
}3. NgFor with Index
The NgFor directive supports accessing the current loop index with let i = index.
<!-- app.component.html -->
<ul>
<li *ngFor="let item of items; let i = index">
Item {{ i + 1 }}: {{ item }}
</li>
</ul>4. NgFor with trackBy
The trackBy function optimizes NgFor performance by preventing unnecessary DOM updates.
<!-- app.component.html -->
<ul>
<li *ngFor="let item of items; trackBy: trackById">
{{ item }}
</li>
</ul>// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
})
export class AppComponent {
items = [{ id: 1, name: 'Apple' }, { id: 2, name: 'Banana' }];
trackById(index: number, item: any): any {
return item.id;
}
}5. NgSwitch
The NgSwitch directive displays different templates based on an expression’s value.
<!-- app.component.html -->
<div [ngSwitch]="status">
<div *ngSwitchCase="'pending'">Pending...</div>
<div *ngSwitchCase="'success'">Success!</div>
<div *ngSwitchCase="'error'">Error!</div>
<div *ngSwitchDefault>No Status</div>
</div>// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
})
export class AppComponent {
status = 'pending';
}Importing Directives
Built-in directives like NgIf, NgFor, and NgSwitch are part of CommonModule, which must be imported in the module’s imports array.
// app.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
CommonModule // Imports CommonModule for built-in directives
],
bootstrap: [AppComponent]
})
export class AppModule { }Building Attribute Directives
Create an attribute directive to conditionally add a CSS class based on a boolean value.
Creating the Directive
// highlight.directive.ts
import { Directive, ElementRef, Input, Renderer2 } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
@Input() set appHighlight(condition: boolean) {
if (condition) {
this.renderer.addClass(this.elRef.nativeElement, 'highlighted');
} else {
this.renderer.removeClass(this.elRef.nativeElement, 'highlighted');
}
}
constructor(private elRef: ElementRef, private renderer: Renderer2) {}
}Applying Attribute Directives
Use the directive in a component template, binding a component property to the directive’s input.
<!-- app.component.html -->
<div appHighlight="isHighlighted">This text can be highlighted.</div>// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
isHighlighted = true;
}Handling User Events
Extend the directive to respond to mouse hover events, dynamically toggling the highlight state.
// highlight.directive.ts
import { Directive, ElementRef, HostListener, Renderer2 } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
@HostListener('mouseenter') onMouseEnter() {
this.renderer.addClass(this.elRef.nativeElement, 'highlighted');
}
@HostListener('mouseleave') onMouseLeave() {
this.renderer.removeClass(this.elRef.nativeElement, 'highlighted');
}
constructor(private elRef: ElementRef, private renderer: Renderer2) {}
}Passing Values to Attribute Directives
Attribute directives can receive data via @Input(). Modify HighlightDirective to accept a color value for the background.
// highlight.directive.ts
import { Directive, ElementRef, Input, Renderer2 } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
@Input() set appHighlight(color: string) {
this.renderer.setStyle(this.elRef.nativeElement, 'background-color', color);
}
constructor(private elRef: ElementRef, private renderer: Renderer2) {}
}<!-- app.component.html -->
<div appHighlight="yellow">This text has a yellow background.</div>Setting Values via User Input
Use @Input() to receive user input values and update element properties.
<!-- app.component.html -->
<input type="text" [(ngModel)]="backgroundColor">
<div appHighlight="{{ backgroundColor }}">Background color controlled by input.</div>// app.component.ts
export class AppComponent {
backgroundColor = 'blue';
}Binding to a Second Property
Directives can bind to multiple properties, e.g., handling both appHighlight and appOpacity.
// style.directive.ts
import { Directive, ElementRef, Input, Renderer2 } from '@angular/core';
@Directive({
selector: '[appStyle]'
})
export class StyleDirective {
@Input() set appHighlight(color: string) {
this.renderer.setStyle(this.elRef.nativeElement, 'background-color', color);
}
@Input() set appOpacity(opacity: number) {
this.renderer.setStyle(this.elRef.nativeElement, 'opacity', opacity.toString());
}
constructor(private elRef: ElementRef, private renderer: Renderer2) {}
}<!-- app.component.html -->
<div appStyle [appHighlight]="backgroundColor" [appOpacity]="0.5">Multiple bindings.</div>Using NgNonBindable to Disable Angular Processing
The NgNonBindable directive prevents Angular from binding expressions in a template.
<!-- app.component.html -->
<div ngNonBindable>Unbound content: {{ notBound }}</div>Structural Directive Shorthand
Angular’s shorthand syntax uses a star (*) prefix for structural directives.
<!-- Shorthand for NgIf -->
<div *ngIf="condition">Content shown if condition is true.</div>One Structural Directive per Element
Typically, one structural directive is applied per element, but multiple directives can be nested for complex structures.
<div *ngIf="showOuter">
Outer content
<div *ngIf="showInner">
Inner content
</div>
</div>Creating Structural Directives
Use ViewContainerRef and TemplateRef to create structural directives that control view insertion/removal.
// custom-if.directive.ts
import { Directive, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appCustomIf]'
})
export class CustomIfDirective {
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) {}
@Input() set appCustomIf(condition: boolean) {
if (condition) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
}Making a Directive Structural
To make a directive structural, use the * prefix and capitalize the directive name.
// Rename to CustomIf<!-- app.component.html -->
<div *customIf="condition">Content shown if condition is true.</div>Adding a selectFrom Input Property
Add input properties to enhance directive functionality.
// custom-if.directive.ts
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appCustomIf]'
})
export class CustomIfDirective {
@Input() set appCustomIf(condition: boolean) {
// ...
}
@Input() selectFrom?: any[];
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) {}
}Adding Business Logic
Use selectFrom to conditionally display templates based on array elements.
// custom-if.directive.ts
export class CustomIfDirective {
@Input() set appCustomIf(condition: boolean) {
if (this.selectFrom && this.selectFrom.length > 0) {
const item = this.selectFrom.find(item => item.condition === condition);
if (item) {
this.viewContainer.createEmbeddedView(this.templateRef, { $implicit: item });
} else {
this.viewContainer.clear();
}
}
}
// ...
}Structural Directive Syntax Reference
Angular structural directives include:
*ngIf*ngFor*ngSwitch*ngTemplateOutlet
How Angular Translates Shorthand
The Angular compiler converts shorthand syntax into full directive calls.
<!-- Shorthand -->
<div *ngIf="condition">Content</div>
<!-- Expanded -->
<ng-template ngIf="condition">
<div>Content</div>
</ng-template>Shorthand Example
Shorthand syntax enhances template clarity.
<!-- NgFor shorthand -->
<ul>
<li *ngFor="let item of items">{{ item }}</li>
</ul>Improving Template Type Checking for Directives
Use TypeScript’s type system to enhance directive type safety.
// custom-if.directive.ts
export class CustomIfDirective {
@Input() set appCustomIf(condition: boolean | string) {
// ...
}
}Using Template Guards for Type Narrowing
Template guards narrow types at runtime for precise type checking.
// custom-if.directive.ts
export class CustomIfDirective {
@Input() set appCustomIf(condition: any) {
if (typeof condition === 'boolean') {
// ...
}
}
}Specifying Context Types for Directives
Specify context types for createEmbeddedView to improve type safety.
// custom-if.directive.ts
export class CustomIfDirective {
@Input() set appCustomIf(condition: boolean) {
if (condition) {
this.viewContainer.createEmbeddedView(this.templateRef, { $implicit: {} as YourType });
}
}
}Adding Directives to Components
Create a directive to change an element’s background color and apply it to a component.
// color.directive.ts
import { Directive, ElementRef, Input, Renderer2 } from '@angular/core';
@Directive({
selector: '[appColor]'
})
export class ColorDirective {
@Input() set appColor(color: string) {
this.renderer.setStyle(this.elRef.nativeElement, 'background-color', color);
}
constructor(private elRef: ElementRef, private renderer: Renderer2) {}
}<!-- app.component.html -->
<div appColor="blue">Hello World!</div>Including Input and Output Properties
Input properties receive data, while output properties emit events.
// color.directive.ts
import { Directive, ElementRef, EventEmitter, Input, Output, Renderer2 } from '@angular/core';
@Directive({
selector: '[appColor]'
})
export class ColorDirective {
@Input() set appColor(color: string) {
this.renderer.setStyle(this.elRef.nativeElement, 'background-color', color);
this.colorChanged.emit(color);
}
@Output() colorChanged = new EventEmitter<string>();
constructor(private elRef: ElementRef, private renderer: Renderer2) {}
}<!-- app.component.html -->
<div appColor="blue" (colorChanged)="onColorChanged($event)">Hello World!</div>// app.component.ts
export class AppComponent {
onColorChanged(color: string) {
console.log('Color changed to:', color);
}
}Adding a Directive to Another Directive
Embed one directive in another for complex functionality.
// composite.directive.ts
import { Directive, ElementRef, Renderer2 } from '@angular/core';
import { ColorDirective } from './color.directive';
@Directive({
selector: '[appComposite]'
})
export class CompositeDirective extends ColorDirective {
constructor(elRef: ElementRef, renderer: Renderer2) {
super(elRef, renderer);
}
}<!-- app.component.html -->
<div appComposite="green">Hello World!</div>Host Directive Semantics
Host directive semantics define behavior on the host element, such as adding CSS classes.
// composite.directive.ts
@Directive({
selector: '[appComposite]',
host: {
'[class.highlight]': 'true'
}
})
export class CompositeDirective extends ColorDirective {
// ...
}Directive Execution Order
Directives follow this execution order:
- Host Directives: Processed before instantiation.
- Constructor: Called next.
- Lifecycle Hooks: Hooks like
ngOnInitare invoked. - Input Properties: Setter methods are executed last.
Dependency Injection
Dependency injection allows directives to access services or other directives.
// color.directive.ts
import { Directive, ElementRef, Inject, Renderer2 } from '@angular/core';
import { COLOR_SERVICE } from './color.service';
@Directive({
selector: '[appColor]'
})
export class ColorDirective {
constructor(
private elRef: ElementRef,
private renderer: Renderer2,
@Inject(COLOR_SERVICE) private colorService: ColorService
) {}
@Input() set appColor(color: string) {
const finalColor = this.colorService.adjustColor(color);
this.renderer.setStyle(this.elRef.nativeElement, 'background-color', finalColor);
}
}// color.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class ColorService {
adjustColor(color: string): string {
// Adjust color logic
return color;
}
}// app.module.ts
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { ColorService } from './color.service';
@NgModule({
declarations: [
AppComponent
],
providers: [
{ provide: COLOR_SERVICE, useClass: ColorService }
],
bootstrap: [AppComponent]
})
export class AppModule { }Angular Data Binding
Interpolation Binding
Interpolation binding displays component data using {{ }}, automatically updating when data changes.
// app.component.ts
export class AppComponent {
message = 'Hello, Angular!';
}<!-- app.component.html -->
<p>{{ message }}</p>Property Binding
Property binding binds component data to DOM element attributes using [ ].
// app.component.ts
export class AppComponent {
imageUrl = 'https://example.com/image.jpg';
}<!-- app.component.html -->
<img [src]="imageUrl" alt="Sample Image">Class and Style Binding
Class and style bindings dynamically apply CSS classes or styles based on component data.
// app.component.ts
export class AppComponent {
isHighlighted = true;
}<!-- app.component.html -->
<div [class.highlighted]="isHighlighted">This is a highlighted div.</div>Event Binding
Event binding links component methods to DOM events using (event).
// app.component.ts
export class AppComponent {
onClick() {
console.log('Button clicked!');
}
}<!-- app.component.html -->
<button (click)="onClick()">Click me</button>Two-Way Binding
Two-way binding combines property and event binding using [(ngModel)], often for form controls.
// app.component.ts
export class AppComponent {
name = '';
}<!-- app.component.html -->
<input [(ngModel)]="name" placeholder="Enter your name">
<p>Your name is: {{ name }}</p>Using NgNonBindable
The NgNonBindable directive prevents Angular from binding expressions.
<!-- app.component.html -->
<div ngNonBindable>{{ rawHtml }}</div>Comprehensive Example
This example demonstrates various data binding techniques:
// app.component.ts
export class AppComponent {
message = 'Welcome to Angular!';
imageUrl = 'https://example.com/image.jpg';
isHighlighted = true;
name = '';
onClick() {
console.log('Button clicked!');
}
}<!-- app.component.html -->
<h1>{{ message }}</h1>
<img [src]="imageUrl" alt="Sample Image">
<div [class.highlighted]="isHighlighted">This is a highlighted div.</div>
<button (click)="onClick()">Click me</button>
<input [(ngModel)]="name" placeholder="Enter your name">
<p>Your name is: {{ name }}</p>Angular Component Communication
Input/Output Properties
Input/output properties are the simplest way for parent-to-child or child-to-parent communication.
Parent to Child Data Passing
Define @Input() in the child component and bind data in the parent template.
// child.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-child',
template: `<p>{{ message }}</p>`
})
export class ChildComponent {
@Input() message: string;
}<!-- parent.component.html -->
<app-child [message]="'Hello from Parent!'"></app-child>Child to Parent Event Emission
Use @Output() and EventEmitter in the child, and listen for events in the parent.
// child.component.ts
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-child',
template: `<button (click)="sendMessage()">Send Message</button>`
})
export class ChildComponent {
@Output() messageEvent = new EventEmitter<string>();
sendMessage() {
this.messageEvent.emit("Message from Child");
}
}<!-- parent.component.html -->
<app-child (messageEvent)="receiveMessage($event)"></app-child>// parent.component.ts
export class ParentComponent {
receiveMessage(message: string) {
console.log(message);
}
}Services
Services are ideal for sharing data and functionality, especially for non-parent-child components.
Create an @Injectable() service and inject it into components using dependency injection.
// data.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class DataService {
sharedData: string;
setData(data: string) {
this.sharedData = data;
}
getData() {
return this.sharedData;
}
}// component-a.component.ts
import { Component } from '@angular/core';
import { DataService } from './data.service';
@Component({...})
export class ComponentA {
constructor(private dataService: DataService) {}
sendData() {
this.dataService.setData('Data from Component A');
}
}// component-b.component.ts
import { Component } from '@angular/core';
import { DataService } from './data.service';
@Component({...})
export class ComponentB {
constructor(private dataService: DataService) {}
getData() {
console.log(this.dataService.getData());
}
}RxJS Subject & BehaviorSubject
RxJS’s Subject and BehaviorSubject enable reactive data sharing between components.
// data.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DataService {
private messageSource = new BehaviorSubject<string>('default message');
currentMessage = this.messageSource.asObservable();
sendMessage(message: string) {
this.messageSource.next(message);
}
}// component-a.component.ts
export class ComponentA {
constructor(private dataService: DataService) {}
sendMessage() {
this.dataService.sendMessage('New Message');
}
}// component-b.component.ts
export class ComponentB {
constructor(private dataService: DataService) {
this.dataService.currentMessage.subscribe(message => console.log(message));
}
}Redux (NgRx)
NgRx is Angular’s Redux implementation for global state management, suitable for large projects.
- Define Store, Actions, and Reducers.
- Use Effects for async operations.
- Access and update state in components using Store and Selectors.
Due to NgRx’s complexity, detailed code is omitted, but the process involves creating Action, Reducer, StoreModule, and EffectsModule, and using Store and Select in components.



