Angular.js Basics

All you need to know for getting started angular.js

Angular is a popular web application framework for building complex, dynamic applications using HTML, CSS, and TypeScript. Here are some steps to get started with learning Angular:

  1. Install Angular CLI: The Angular Command Line Interface (CLI) is a powerful tool that helps you to create, manage, and build Angular applications. You can install it globally on your computer by running the following command in your terminal:

     npm install -g @angular/cli
    
  2. Learn TypeScript: TypeScript is a superset of JavaScript that adds static typing and other features to the language. Since Angular is written in TypeScript, it's important to have a basic understanding of it. You can learn TypeScript from the official documentation or online tutorials.

  3. Learn Angular basics: Once you have the Angular CLI installed, you can use it to create a new Angular project. Then, you can start learning the basics of Angular by building a simple application with components, services, and routing. The official Angular documentation provides a great starting point for learning Angular basics.

  4. Practice and build projects: Practice is key to learning any new technology. You can practice by building small projects or by contributing to open-source Angular projects. Some many online tutorials and courses can help you improve your Angular skills.

  5. Stay up-to-date: Angular is constantly evolving, so it's important to stay up-to-date with the latest changes and best practices. You can follow the official Angular blog or join the Angular community to keep yourself informed.

TypeScript Intro

  1. Variables and Data Types:

     let myString: string = 'Hello, World!'; // String variable
     let myNumber: number = 123; // Number variable
     let myBoolean: boolean = true; // Boolean variable
    
  2. Functions:

     function addNumbers(a: number, b: number): number {
       return a + b;
     }
    
     console.log(addNumbers(1, 2)); // Output: 3
    
  3. Classes and Objects:

     class Person {
       name: string;
       age: number;
    
       constructor(name: string, age: number) {
         this.name = name;
         this.age = age;
       }
    
       sayHello() {
         console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
       }
     }
    
     let person1 = new Person('John', 30);
     person1.sayHello(); 
     // Output: Hello, my name is John and I'm 30 years old.
    
  4. Arrays:

     let myArray: number[] = [1, 2, 3, 4, 5];
    
     for (let i = 0; i < myArray.length; i++) {
       console.log(myArray[i]);
     }
    

    Output:

     2
     3
     4
     5
    
  5. Interfaces:

interface User {
  name: string;
  age: number;
  email: string;
}

function sendEmail(user: User, message: string) {
  console.log(`Sending email to ${user.name} (${user.email}): ${message}`);
}

let user1: User = { name: 'John', age: 30, email: 'john@example.com' };
sendEmail(user1, 'Hello, John!');

Output:

Sending email to John (john@example.com): Hello, John!

Create a new Angular app

  1. Open your terminal or command prompt.

  2. Install the Angular CLI by running the following command:

     npm install -g @angular/cli
    
  3. Once the installation is complete, you can create a new Angular app by running the following command:

     ng new my-app
    

    Replace my-app with the name you want to give your app. This will create a new directory with your app name and generate the basic files and folders needed for an Angular app.

  4. Change into the new app directory:

     cd my-app
    
  5. Run the app by running the following command:

     ng serve --open
    

    This will start the development server and open your app in your default web browser. You should see a "Welcome to my-app!" message.

That's it! You've successfully created a new Angular app using the Angular CLI. You can start building your app by modifying the files in the src directory.

To create a new component in your Angular app using the Angular CLI, follow these steps:

ng generate component my-component

Pass data from parent to child

  1. Parent component:

     import { Component } from '@angular/core';
    
     @Component({
       selector: 'app-parent',
       template: `
         <h1>Parent Component</h1>
         <app-child [message]="parentMessage"></app-child>
       `
     })
     export class ParentComponent {
       parentMessage = 'Message from parent';
     }
    

    In this example, the ParentComponent has a property called parentMessage, which is passed to the ChildComponent using an input property binding.

  2. Child component:

     import { Component, Input } from '@angular/core';
    
     @Component({
       selector: 'app-child',
       template: '<h2>{{ message }}</h2>'
     })
     export class ChildComponent {
       @Input() message: string;
     }
    

    In this example, the ChildComponent has an input property called message, which is bound to the parentMessage property of the ParentComponent. The @Input() decorator is used to indicate that message is an input property.

When the ParentComponent is rendered, the parentMessage property is passed to the ChildComponent as an input property. The ChildComponent then displays the message property in its template.

<app-parent></app-parent>

When this code is run, it will render the ParentComponent, which will pass its parentMessage property to the ChildComponent. The ChildComponent will then display the message in its template.

This is just one way to pass data from a parent component to a child component in Angular. There are other techniques you can use, such as services or observables, depending on your specific use case.

Pass data from child to parent

  1. Parent component:

     import { Component } from '@angular/core';
    
     @Component({
       selector: 'app-parent',
       template: `
         <h1>Parent Component</h1>
         <app-child (messageEvent)="receiveMessage($event)"></app-child>
         <p>{{ message }}</p>
       `
     })
     export class ParentComponent {
       message: string;
    
       receiveMessage($event) {
         this.message = $event;
       }
     }
    

    In this example, the ParentComponent has a method called receiveMessage() that receives a message from the ChildComponent. The ChildComponent emits an event using an event emitter that the ParentComponent listens to.

  2. Child component:

     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>();
       message = 'Hello, parent!';
    
       sendMessage() {
         this.messageEvent.emit(this.message);
       }
     }
    

    In this example, the ChildComponent has an output property called messageEvent, which is an event emitter that emits a message to the parent component when the sendMessage() method is called. The ParentComponent listens to this event using (messageEvent)="receiveMessage($event)".

When the ChildComponent emits a message using the messageEvent event emitter, the ParentComponent receives the message and updates its message property. The updated message property is then displayed in the ParentComponent template.

<app-parent></app-parent>

When this code is run, it will render the ParentComponent, which will include the ChildComponent. When the "Send Message" button in the ChildComponent is clicked, it will emit a message using the messageEvent event emitter. The ParentComponent will receive the message and display it in its template.

ViewChild

You can also use ViewChild to pass data from a child component to a parent component in Angular. Here's an example:

  1. Parent component:

     import { Component, ViewChild } from '@angular/core';
     import { ChildComponent } from './child.component';
    
     @Component({
       selector: 'app-parent',
       template: `
         <h1>Parent Component</h1>
         <app-child></app-child>
         <p>{{ message }}</p>
       `
     })
     export class ParentComponent {
       message: string;
    
       @ViewChild(ChildComponent) childComponent: ChildComponent;
    
       ngAfterViewInit() {
         this.message = this.childComponent.message;
       }
     }
    

    In this example, the ParentComponent uses ViewChild to get a reference to the ChildComponent and its message property. The ngAfterViewInit() lifecycle hook is used to wait until the child component is initialized before accessing its properties.

  2. Child component:

     import { Component } from '@angular/core';
    
     @Component({
       selector: 'app-child',
       template: '<button (click)="sendMessage()">Send Message</button>'
     })
     export class ChildComponent {
       message = 'Hello, parent!';
    
       sendMessage() {
         this.message = 'Message from child';
       }
     }
    

    In this example, the ChildComponent has a message property that is initially set to "Hello, parent!" and a sendMessage() method that changes the value of message.

When the ParentComponent is initialized, it uses ViewChild to get a reference to the ChildComponent and its message property. When the ChildComponent button is clicked, the message property is updated, and the ngAfterViewInit() method in the ParentComponent is called to update its message property with the new value.

<app-parent></app-parent>

When this code is run, it will render the ParentComponent, which will include the ChildComponent. When the "Send Message" button in the ChildComponent is clicked, it will update the message property in the child component, and the ParentComponent will receive the new value and display it in its template.

This is just one way to pass data from a child component to a parent component in Angular. There are other techniques you can use, such as services or observables, depending on your specific use case.

Displaying the data

  1. Interpolation:

    Interpolation is the simplest way to display data in Angular. You can use interpolation to display data within an HTML template by wrapping it in curly braces, like this:

     <h1>{{ title }}</h1>
     <p>{{ message }}</p>
    

    In this example, title and message are properties defined in the component class.

  2. Property binding:

    Property binding allows you to set an element's property value based on the component's property value. You can use property binding to display data in a more dynamic way, like this:

     <img [src]="imageUrl">
    

    In this example, imageUrl is a property defined in the component class, and the src property of the img element is bound to it using square brackets.

  3. Class binding

    To use class binding, you can use the [class] attribute on an element, followed by a conditional expression. The expression should evaluate to a string containing one or more CSS class names, separated by spaces. Here's an example:

     <button [class.active]="isActive">Click me!</button>
    

    In this example, isActive is a property defined in the component class. If isActive is true, the active class will be added to the button element. If it is false, the class will be removed.

    You can also use class binding with more complex expressions that include multiple classes. For example:

     <button [class]="buttonClasses">Click me!</button>
    

    In this example, buttonClasses is a property defined in the component class that evaluates to a string containing one or more CSS class names. You could define this property like this:

     buttonClasses = 'primary small';
    
     setLarge() {
       this.buttonClasses = 'primary large';
     }
    

    In this example, the buttonClasses property is initially set to primary small, which will apply the primary and small classes to the button. If the setLarge() method is called, the buttonClasses property will be updated to primary large, which will remove the small class and add the large class.

    You can also use class binding with conditionals to apply different classes based on different conditions. For example:

     <button [class.primary]="isPrimary" [class.disabled]="isDisabled">Click me!</button>
    

    In this example, isPrimary and isDisabled are properties defined in the component class. If isPrimary is true, the primary class will be added to the button element. If isDisabled is true, the disabled class will be added. If both conditions are true, both classes will be added. If both conditions are false, neither class will be added.

  4. Style binding

    To use style binding, you can use the [style] attribute on an element, followed by a conditional expression. The expression should evaluate to an object containing one or more CSS styles and their values. Here's an example:

     <div [style.color]="textColor">Hello, world!</div>
    

    In this example, textColor is a property defined in the component class that evaluates to a string containing a color value (e.g. red, #00FF00, etc.). The color style of the div element will be set to the value of textColor.

    You can also use style binding with more complex expressions that include multiple styles. For example:

     <div [style]="styleObject">Hello, world!</div>
    

    In this example, styleObject is a property defined in the component class that evaluates to an object containing one or more CSS styles and their values. You could define this property like this:

     cssCopy codestyleObject = {
       'background-color': 'yellow',
       'font-size': '24px'
     };
    

    In this example, the styleObject property contains two styles (background-color and font-size) and their values. Both styles will be applied to the div element.

    You can also use style binding with conditionals to apply different styles based on different conditions. For example:

     <div [style.background-color]="backgroundColor">Hello, world!</div>
    

    In this example, backgroundColor is a property defined in the component class. If backgroundColor is red, the background-color style of the div element will be set to red. If it is blue, the style will be set to blue. And so on.

  5. Structural directives:

    Structural directives allow you to conditionally render elements in the template based on the component's data. The *ngIf directive is a good example of this:

     <div *ngIf="showContent">
       <p>{{ message }}</p>
     </div>
    

    In this example, showContent is a property defined in the component class, and the *ngIf directive will only render the div element if it is true.

  6. Pipes:

    Pipes allow you to transform data before displaying it in the template. For example, you can use the uppercase pipe to transform a string to uppercase:

     <p>{{ message | uppercase }}</p>
    

    In this example, message is a property defined in the component class, and the uppercase pipe will transform it to uppercase before displaying it in the p element.

  7. Event binding

    To use event binding, you can use the (event) attribute on an element, followed by the name of the event you want to handle, and a function to be called when the event occurs. Here's an example:

     <button (click)="onButtonClick()">Click me!</button>
    

    In this example, onButtonClick() is a method defined in the component class that will be called when the click event is triggered on the button element.

    You can also pass data to the event handler function by using the $event object. For example:

     <input (keyup)="onKeyUp($event)">
    

    In this example, onKeyUp() is a method defined in the component class that will be called when the keyup event is triggered on the input element. The $event object contains information about the event, such as the key that was pressed, which can be used in the event handler function.

    You can also use event binding with template expressions to perform more complex operations. For example:

     <button (click)="count = count + 1">Click me! ({{ count }})</button>
    

    In this example, a property called count is defined in the component class and is initialized to 0. When the click event is triggered on the button element, the value of count is incremented by 1 and the updated value is displayed in the button text.

  8. Event filtering

    To use event filtering, you can add a modifier to the event binding expression. For example:

     <button (click)="onButtonClick($event)">Click me!</button>
    

    In this example, onButtonClick() is a method defined in the component class that will be called when the click event is triggered on the button element. The $event object is passed as a parameter to the method.

    You can add a modifier to the event binding expression to filter or modify the event before it is passed to the method. For example, you can use the stopPropagation() method to stop the event from propagating to parent elements:

     <button (click)="onButtonClick($event); $event.stopPropagation()">Click me!</button>
    

    In this example, the stopPropagation() method is called on the $event object to prevent the click event from bubbling up to parent elements.

    You can also use other event modifiers, such as preventDefault() to prevent the default action associated with the event, or key.enter to filter events based on specific keyboard keys.

     <input (keydown.enter)="onEnterKeyPressed()">
    

    In this example, onEnterKeyPressed() is a method defined in the component class that will be called when the enter key is pressed on the input element.

These are just a few examples of different ways you can display data in an Angular application. There are many other techniques you can use, depending on your specific use case.

Template Variable

Template variables in Angular allow you to reference HTML elements in your template and manipulate their properties or attach event listeners to them. You can create a template variable by prefixing the variable name with a hash (#) symbol.

Here's an example of using template variables with the (click) and (keyup) event bindings:

<input #myInput (keyup)="onKeyUp(myInput.value)">
<button (click)="onButtonClick(myInput.value)">Click me!</button>

In this example, a template variable called myInput is defined on the input element using the # symbol. The (keyup) event binding is used to call the onKeyUp() method in the component class and pass the current value of the input element as a parameter.

The (click) event binding is used to call the onButtonClick() method in the component class and pass the current value of the input element as a parameter.

In the component class, you can define the onKeyUp() and onButtonClick() methods to perform the desired actions:

export class MyComponent {
  onKeyUp(value: string) {
    console.log(`Input value: ${value}`);
  }

  onButtonClick(value: string) {
    alert(`Button clicked with input value: ${value}`);
  }
}

In this example, the onKeyUp() method logs the current value of the input element to the console, while the onButtonClick() method shows an alert with the current value of the input element.

Two way data binding

Two-way data binding in Angular allows you to bind a property of a component class to an input element in your template, so that changes in the input element are automatically reflected in the component class and vice versa. This makes it easier to keep your application data in sync with the user interface.

Here's an example of using two-way data binding with the [(ngModel)] directive:

<input [(ngModel)]="myData">

In this example, the [(ngModel)] directive is used to create a two-way binding between the myData property of the component class and the value property of the input element.

Changes made to the input element are automatically reflected in the myData property of the component class, and changes made to the myData property of the component class are automatically reflected in the value property of the input element.

To use two-way data binding with [(ngModel)], you must import the FormsModule in your module and add it to the imports array:

import { FormsModule } from '@angular/forms';

@NgModule({
  imports: [ FormsModule ],
  ...
})
export class MyModule { }

In the component class, you can define the myData property and initialize it with a default value:

export class MyComponent {
  myData: string = 'Hello, world!';
}

In this example, the myData property is initialized with the value 'Hello, world!'.

Using two-way data binding with [(ngModel)] can make it easier to create more dynamic and responsive user interfaces in your Angular applications. However, it's important to use it judiciously and avoid creating overly complex data flows that can be difficult to manage.

Directive

In Angular, a directive is a component-like structure that allows you to extend the behavior of HTML elements or attributes in your application. Directives can be used to create reusable UI components, add behavior to existing UI components, or manipulate the DOM.

There are three types of directives in Angular:

  1. Component Directives

  2. Attribute Directives

  3. Structural Directives

Here are some examples of each type of directive:

  1. Component Directives:

/himport { Component } from '@angular/core';

@Component({
  selector: 'app-alert',
  template: '<div class="alert">{{ message }}</div>',
  styles: ['.alert { background-color: yellow; }']
})
export class AlertComponent {
  message: string = 'This is an alert message.';
}

In this example, we're creating a new component directive called AlertComponent. The selector property defines the HTML tag that will be used to insert the component into a template, and the template and styles properties define the HTML and CSS code that will be used to render the component.

  1. Attribute Directives:
import { Directive, ElementRef } from '@angular/core';

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

In this example, we're creating a new attribute directive called HighlightDirective. The selector property defines the HTML attribute that will trigger the directive, and the constructor method is used to manipulate the DOM by changing the background color of the element that the directive is applied to.

  1. Structural Directives:
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appUnless]'
})
export class UnlessDirective {
  private hasView = false;

  constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) {}

  @Input() set appUnless(condition: boolean) {
    if (!condition && !this.hasView) {
      this.viewContainer.createEmbeddedView(this.templateRef);
      this.hasView = true;
    } else if (condition && this.hasView) {
      this.viewContainer.clear();
      this.hasView = false;
    }
  }
}

In this example, we're creating a new structural directive called UnlessDirective. The selector property defines the HTML attribute that will trigger the directive, and the constructor method is used to inject the TemplateRef and ViewContainerRef services, which are used to dynamically create and remove DOM elements based on a condition specified by the appUnless input.

These are just a few examples of the many ways that directives can be used in Angular to extend the behavior of HTML elements and attributes. Directives can be a powerful tool for creating reusable UI components and adding custom behavior to your application.

Angular provides several built-in directives that you can use to add logic and interactivity to your templates. Here are some examples:

  1. ngIf Directive: This directive allows you to conditionally show or hide elements based on a boolean expression.
<p *ngIf="showMessage">Hello World!</p>

In this example, the p element will only be displayed if the showMessage property is true.

  1. ngIf-else Directive: This directive allows you to show one element if a condition is true and another element if the condition is false.
<div *ngIf="loggedIn; else loginForm">
  Welcome back, {{username}}!
</div>

<ng-template #loginForm>
  <app-login></app-login>
</ng-template>

In this example, the div element will only be displayed if the loggedIn property is true. If loggedIn is false, the app-login component will be displayed instead.

  1. ngSwitch Directive: This directive allows you to conditionally render elements based on a selection expression.
<div [ngSwitch]="role">
  <p *ngSwitchCase="'admin'">Hello Admin!</p>
  <p *ngSwitchCase="'user'">Hello User!</p>
  <p *ngSwitchDefault>Hello Guest!</p>
</div>

In this example, the div element will display a different p element based on the value of the role property. If role is 'admin', the first p element will be displayed. If role is 'user', the second p element will be displayed. If role is any other value, the default p element will be displayed.

  1. ngFor Directive: This directive allows you to loop over a collection of items and render a template for each item in the collection.
<ul>
  <li *ngFor="let item of items; let i = index">{{ item }}</li>
</ul>

In this example, the li element will be repeated for each item in the items array.

  1. ngStyle Directive: This directive allows you to dynamically set styles on an element.
<div [ngStyle]="{ 'background-color': bgColor }">Hello World!</div>

In this example, the background color of the div element will be set to the value of the bgColor property.

  1. ngClass Directive: This directive allows you to add or remove CSS classes on an element based on a condition.

     <div [ngClass]="{ 'active': isActive, 'disabled': isDisabled }">Hello World!</div>
    

    In this example, the div element will have the active class added if the isActive property is true, and the disabled class added if the isDisabled property is true.

    You can also use the ngClass directive with a string expression or an array of class names. For example:

     <div [ngClass]="'foo bar'">Hello World!</div>
    
     <div [ngClass]="['foo', 'bar']">Hello World!</div>
    

    In these examples, the div element will have the foo and bar classes added.

    Additionally, you can use the ngClass directive with a function that returns an object or a string. For example:

     <div [ngClass]="getClass()">Hello World!</div>
    
     getClass() {
       return {
         'active': this.isActive,
         'disabled': this.isDisabled
       };
     }
    

    In this example, the getClass() method returns an object that maps class names to boolean values. If the isActive property is true, the active class will be added. If the isDisabled property is true, the disabled class will be added.

Angular pipe

Angular pipes are a powerful feature that allows you to transform and format data in your Angular templates. Pipes are essentially functions that take input and output transformed data. Angular provides several built-in pipes for common operations, and you can also create your own custom pipes.

Here are some examples of built-in pipes in Angular:

  1. {{ text | uppercase }}: This pipe transforms the text to all uppercase letters.

  2. {{ text | lowercase }}: This pipe transforms the text to all lowercase letters.

  3. {{ date | date:'shortDate' }}: This pipe formats the date according to a specified format. In this example, the date is formatted as a short date (e.g., 4/8/2023).

  4. {{ number | currency:'USD':'symbol' }}: This pipe formats the number as currency, with the specified currency symbol (in this case, USD).

  5. {{ text | slice:0:10 }}: This pipe extracts a portion of the text, starting at the 0th character and ending at the 10th character.

  6. {{ array | orderBy:'name' }}: This pipe sorts an array of objects by a specified property (in this case, the name property).

  7. {{ object | json }}: This pipe formats the object as a JSON string.

  8. {{ value | percent }}: This pipe formats the value as a percentage.

To use a pipe, you simply include the pipe name followed by a colon and any necessary arguments in the Angular template expression. For example:

<p>{{ 'hello world' | uppercase }}</p>
<p>{{ date | date:'shortDate' | uppercase }}</p>

Custom Pipe

You can create a custom pipe in Angular by defining a new class that implements the PipeTransform interface. The PipeTransform interface requires that you implement a single method, transform, which takes an input value and any necessary arguments and returns the transformed output value.

Here's an example of how to create a custom pipe that doubles a number:

  1. Create a new file called double.pipe.ts in your project's app directory.

  2. In double.pipe.ts, import Pipe and PipeTransform from @angular/core:

import { Pipe, PipeTransform } from '@angular/core';
  1. Define a new class that implements the PipeTransform interface:
@Pipe({ name: 'double' })
export class DoublePipe implements PipeTransform {
  transform(value: number): number {
    return value * 2;
  }
}

In this example, we've defined a new pipe called DoublePipe. The @Pipe decorator tells Angular that this is a pipe, and the name property specifies the pipe's name (which we'll use in our templates). The PipeTransform interface requires us to implement a single method, transform, which takes an input value of type number and returns the transformed value (which is also of type number).

  1. Save double.pipe.ts and use the new pipe in your templates:

  2. Import your custom pipe class in the module where you want to use it. For example, if you created a DoublePipe class as described in my previous response, you would import it like this:

import { DoublePipe } from './double.pipe';

Add your custom pipe class to the declarations array of the @NgModule decorator in the same module. For example:

@NgModule({
  declarations: [
    AppComponent,
    DoublePipe
  ],
  ...
})
export class AppModule { }
<p>{{ 2 | double }}</p>

This will display "4" in the paragraph element.

That's it! You can now use your new custom pipe in your Angular templates to transform data in any way you like. Just remember to import your new pipe into any module where you want to use it.

Using CLI

You can also create a custom pipe using the Angular CLI. Here's how:

  1. Open a terminal window and navigate to the root directory of your Angular project.

  2. Use the Angular CLI's generate pipe command to generate a new pipe with the desired name. For example, to create a double pipe, run:


ng generate pipe double

This will create a new file called double.pipe.ts in the app directory of your project.

  1. Open double.pipe.ts in your code editor and modify the transform method to implement your custom pipe logic. For example, here's how you could modify the double pipe to double any input value:
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({ name: 'double' })
export class DoublePipe implements PipeTransform {
  transform(value: number): number {
    return value * 2;
  }
}

In this example, we're importing the Pipe and PipeTransform classes from @angular/core, and defining a new class called DoublePipe that implements the PipeTransform interface. The @Pipe decorator specifies the pipe's name, and the transform method doubles any input value.

  1. Save the double.pipe.ts file.

  2. Register your custom pipe in the module where it will be used, as described in my previous response. For example:

import { DoublePipe } from './double.pipe';

@NgModule({
  declarations: [
    AppComponent,
    DoublePipe
  ],
  ...
})
export class AppModule { }

That's it! Your custom pipe is now available to use in your Angular templates.

Arguments

If you want to create a custom pipe that accepts arguments, you can modify the transform method to accept additional parameters, like this:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({ name: 'multiply' })
export class MultiplyPipe implements PipeTransform {
  transform(value: number, multiplier: number = 1): number {
    return value * multiplier;
  }
}

In this example, we've defined a new multiply pipe that accepts an additional multiplier argument. The transform method takes two parameters: value, which is the input value that we want to transform, and multiplier, which is an optional parameter with a default value of 1.

When we use this pipe in a template, we can pass an argument for the multiplier parameter by separating it from the pipe name with a colon. For example:

<p>{{ 5 | multiply: 2 }}</p>

In this example, we're using the multiply pipe to double the value 5, so the output will be 10. We've passed 2 as the multiplier argument by separating it from the pipe name with a colon.

Service

In Angular, a service is a class that provides functionality to be used throughout an application. Services are typically used to provide data or functionality that needs to be shared across multiple components, or to encapsulate functionality that is not directly related to a component, such as data access or logging.

Services are typically defined as Injectable classes, which means they can be injected into other components or services using Angular's dependency injection system. This allows services to be easily shared and reused throughout an application.

Here are some common use cases for Angular services:

  • Data access: Services can be used to retrieve data from an external API or from a local data store, and provide that data to components as needed.

  • State management: Services can be used to manage the state of an application, such as keeping track of user authentication status or application settings.

  • Logging and error handling: Services can be used to log application events and errors, and to handle errors that occur within the application.

  • Utility functions: Services can be used to provide utility functions that can be used across multiple components, such as formatting dates or performing string manipulation.

To create a service in Angular, you can use the ng generate service command in the Angular CLI. This will create a new service class and register it with the application's dependency injection system. From there, you can define the functionality that the service provides and inject it into any components or other services that need it.

  1. Generate a new service using the Angular CLI:

     ng generate service my-service
    
  2. This will create a new my-service directory with a file called my-service.service.ts. Open this file in your code editor.

  3. In the my-service.service.ts file, define a new Injectable class that provides some functionality. For example, you could define a method that retrieves data from an API:

     import { Injectable } from '@angular/core';
     import { HttpClient } from '@angular/common/http';
     import { Observable } from 'rxjs';
    
     @Injectable({
       providedIn: 'root'
     })
     export class MyService {
       constructor(private http: HttpClient) {}
    
       getData(): Observable<any> {
         return this.http.get('https://jsonplaceholder.typicode.com/todos/1');
       }
     }
    

    In this example, the MyService class uses Angular's HttpClient to retrieve data from an external API. The getData() method returns an Observable that emits the response from the API.

  4. In a component that needs to use the service, import the MyService class and inject it into the component's constructor:

     import { Component } from '@angular/core';
     import { MyService } from './my-service/my-service.service';
    
     @Component({
       selector: 'app-my-component',
       template: `
         <h1>Data from the API:</h1>
         <p>{{ data | json }}</p>
       `
     })
     export class MyComponent {
       data: any;
    
       constructor(private myService: MyService) {}
    
       ngOnInit() {
         this.myService.getData().subscribe((response) => {
           this.data = response;
         });
       }
     }
    

    In this example, the MyComponent class uses the MyService class to retrieve data from the API and store it in the data property. The component's template uses the json pipe to display the data as JSON.

  5. Finally, add the MyComponent to your application's module so that it can be rendered:

     import { NgModule } from '@angular/core';
     import { BrowserModule } from '@angular/platform-browser';
     import { HttpClientModule } from '@angular/common/http';
     import { MyComponent } from './my-component/my-component.component';
     import { MyService } from './my-service/my-service.service';
    
     @NgModule({
       imports: [BrowserModule, HttpClientModule],
       declarations: [MyComponent],
       providers: [MyService],
       bootstrap: [MyComponent]
     })
     export class AppModule {}
    

    In this example, we're importing the BrowserModule and HttpClientModule modules, and declaring the MyComponent. We're also providing the MyService so that it can be injected into the MyComponent. Finally, we're bootstrapping the MyComponent so that it is the root component of the application.

Interface

In Angular, an interface is a TypeScript feature that allows you to define a contract for an object's properties and their types. Interfaces are used to enforce consistency and type-checking throughout your application, which can help to prevent errors and improve maintainability.

Interfaces define a set of properties and their types, but they do not provide an implementation. Instead, they are used to describe the shape of an object that will be used by other parts of the application.

Here's an example of an interface in Angular:

export interface Person {
  firstName: string;
  lastName: string;
  age?: number; // optional 
  address: {
    street: string;
    city: string;
    state: string;
    zip: string;
  }
}

In this example, we have defined a Person interface with several properties, including firstName, lastName, age, and address. The address property is itself an object with its own properties.

Interfaces can be used in many ways throughout your application, such as defining the shape of data returned from an API, or describing the properties of a component's input or output bindings.

Here's an example of using an interface to define a component's input binding:

import { Component, Input } from '@angular/core';
import { Person } from './person';

@Component({
  selector: 'app-person-details',
  templateUrl: './person-details.component.html'
})
export class PersonDetailsComponent {
  @Input() person: Person;
}

In this example, we have imported the Person interface from a separate file, and used it to define the person input binding for our PersonDetailsComponent component. This will ensure that any data passed to the person input binding conforms to the shape defined by the Person interface.

To create an interface using the Angular CLI, you can use the ng generate interface command followed by the name of your interface. Here's an example:

ng generate interface models/Person 
// models is folder name

Form

Angular provides two approaches to creating forms: template-driven forms and reactive forms.

Template-Driven forms

In template-driven forms, the form is defined in the template using directives and binding expressions. This approach is suitable for simple forms with basic validation requirements.

Here's an example of how to create a template-driven form in Angular:

  1. First, create a new component for your form:
ng generate component my-form
  1. In the template for your form component, define your form using the ngForm directive:
<form #myForm="ngForm" (ngSubmit)="onSubmit()">
  <div>
    <label for="firstName">First Name:</label>
    <input type="text" id="firstName" name="firstName"  [(ngModel)]="firstName" required>
  </div>
  <div>
    <label for="lastName">Last Name:</label>
    <input type="text" id="lastName" name="lastName" [(ngModel)]="lastName" required>
  </div>
  <button type="submit">Submit</button>
</form>

In this example, we're using the ngForm directive to define our form and the ngSubmit event to handle form submissions. We're also using the ngModel directive to bind our form inputs to properties on our component.

  1. In the component class for your form, define the properties and methods to handle form data:
import { Component } from '@angular/core';

@Component({
  selector: 'app-my-form',
  templateUrl: './my-form.component.html',
  styleUrls: ['./my-form.component.css']
})
export class MyFormComponent {
  firstName: string;
  lastName: string;

  onSubmit() {
    console.log('Form submitted!');
    console.log('First Name:', this.firstName);
    console.log('Last Name:', this.lastName);
  }
}

In this example, we're defining the firstName and lastName properties, and the onSubmit method to handle form submissions.

  1. Finally, add your form component to your application by updating your app component template:
<app-my-form></app-my-form>

That's it! You now have a working template-driven form in your Angular application. Note that you can add additional validation, styling, and other features as needed.

Validation

In the component class:

import { Component } from '@angular/core';

@Component({
  selector: 'app-form',
  templateUrl: './form.component.html'
})
export class FormComponent {
  model = {
    firstName: '',
    lastName: '',
    email: '',
    password: ''
  };

  onSubmit() {
    console.log('Form submitted successfully.');
  }
}

In the component template:

<form #f="ngForm" (ngSubmit)="onSubmit()">
  <div class="form-group">
    <label for="firstName">First Name</label>
    <input type="text" id="firstName" class="form-control"
      [(ngModel)]="model.firstName" name="firstName"
      required minlength="3" #firstName="ngModel">
    <div *ngIf="firstName.invalid && (firstName.dirty || firstName.touched)"
      class="alert alert-danger">
      <div *ngIf="firstName.errors.required">First Name is required.</div>
      <div *ngIf="firstName.errors.minlength">First Name must be at least 3 characters long.</div>
    </div>
  </div>
  <div class="form-group">
    <label for="lastName">Last Name</label>
    <input type="text" id="lastName" class="form-control"
      [(ngModel)]="model.lastName" name="lastName"
      required minlength="3" #lastName="ngModel">
    <div *ngIf="lastName.invalid && (lastName.dirty || lastName.touched)"
      class="alert alert-danger">
      <div *ngIf="lastName.errors.required">Last Name is required.</div>
      <div *ngIf="lastName.errors.minlength">Last Name must be at least 3 characters long.</div>
    </div>
  </div>
  <div class="form-group">
    <label for="email">Email</label>
    <input type="email" id="email" class="form-control"
      [(ngModel)]="model.email" name="email"
      required email #email="ngModel">
    <div *ngIf="email.invalid && (email.dirty || email.touched)"
      class="alert alert-danger">
      <div *ngIf="email.errors.required">Email is required.</div>
      <div *ngIf="email.errors.email">Email is not valid.</div>
    </div>
  </div>
  <div class="form-group">
    <label for="password">Password</label>
    <input type="password" id="password" class="form-control"
      [(ngModel)]="model.password" name="password"
      required #password="ngModel">
    <div *ngIf="password.invalid && (password.dirty || password.touched)"
      class="alert alert-danger">
      <div *ngIf="password.errors.required">Password is required.</div>
    </div>
  </div>
  <button type="submit" class="btn btn-primary" [disabled]="f.invalid">Submit</button>
</form>

In this example, we are using the ngForm directive to create a template-driven form. We are also using ngModel directive to bind form inputs to the component's model.

For each form control, we are adding validation rules as HTML attributes. We are also using template variables (#firstName, #lastName, #email, and #password) to get a reference to each form control, so that we can display validation messages based on its state.

We are using *ngIf directive to show validation messages only when the form control is invalid

Reactive forms

  1. First, create a new component for your form:
ng generate component my-form
  1. In the template for your form component, define your form using the formGroup directive:
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
  <div>
    <label for="firstName">First Name:</label>
    <input type="text" id="firstName" formControlName="firstName">
  </div>
  <div>
    <label for="lastName">Last Name:</label>
    <input type="text" id="lastName" formControlName="lastName">
  </div>
  <button type="submit" [disabled]="myForm.invalid">Submit</button>
</form>

In this example, we're using the formGroup directive to define our form and the formControlName directive to bind our form inputs to properties on our component. We're also disabling the submit button when the form is invalid.

  1. In the component class for your form, define the form and validation rules:
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-my-form',
  templateUrl: './my-form.component.html',
  styleUrls: ['./my-form.component.css']
})
export class MyFormComponent {
  myForm: FormGroup;

  constructor(private formBuilder: FormBuilder) {
    this.myForm = this.formBuilder.group({
      firstName: ['', Validators.required],
      lastName: ['', Validators.required]
    });
  }

  onSubmit() {
    console.log('Form submitted!');
    console.log('First Name:', this.myForm.value.firstName);
    console.log('Last Name:', this.myForm.value.lastName);
  }
}

In this example, we're defining the myForm property using the FormBuilder service. We're also adding validation rules for the firstName and lastName fields.

  1. Finally, add your form component to your application by updating your app component template:
<app-my-form></app-my-form>

That's it! You now have a working reactive form in your Angular application. Note that you can add additional validation, styling, and other features as needed.

Validation

  1. Import the necessary classes from the @angular/forms package:
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
  1. Create a new form group using the FormBuilder service:
constructor(private fb: FormBuilder) {
  this.myForm = this.fb.group({
    email: ['', [Validators.required, Validators.email]],
    password: ['', [Validators.required, Validators.minLength(6)]]
  });
}

In this example, we're creating a form group with two form controls: email and password. We're also adding validation rules to each control using Validators.required and Validators.email for the email control, and Validators.required and Validators.minLength(6) for the password control.

  1. Add the form group to your template using the formGroup directive:
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
  <div>
    <label for="email">Email</label>
    <input type="email" formControlName="email">
    <div *ngIf="myForm.get('email').invalid && (myForm.get('email').dirty || myForm.get('email').touched)">
      <div *ngIf="myForm.get('email').errors.required">Email is required</div>
      <div *ngIf="myForm.get('email').errors.email">Invalid email format</div>
    </div>
  </div>
  <div>
    <label for="password">Password</label>
    <input type="password" formControlName="password">
    <div *ngIf="myForm.get('password').invalid && (myForm.get('password').dirty || myForm.get('password').touched)">
      <div *ngIf="myForm.get('password').errors.required">Password is required</div>
      <div *ngIf="myForm.get('password').errors.minlength">Password must be at least 6 characters long</div>
    </div>
  </div>
  <button type="submit" [disabled]="myForm.invalid">Submit</button>
</form>

In this example, we're using the formControlName directive to bind our form inputs to the controls in our form group. We're also using the *ngIf directive to show error messages if the form control is invalid.

  1. Add a submit function to your component to handle form submissions:
onSubmit() {
  console.log(this.myForm.value);
}

In this example, we're simply logging the form value to the console. You can use this function to send the form data to a server, update a database, or perform any other necessary action.

That's it! You now have a form with basic validation in your Angular application. Note that you can add additional validation rules and customize the error messages as needed.

Custom Validator

  1. First, create a new file for your custom validator. For this example, let's call it custom-validator.ts.

  2. In custom-validator.ts, import AbstractControl from @angular/forms. This is the base class for form controls in Angular.

  3. Define a function that takes an AbstractControl parameter and returns a ValidationErrors object. This object will contain a set of key-value pairs, where the key is the name of the error and the value is a boolean indicating whether the error is present.

  4. Here's an example custom-validator.ts file that checks if the input string contains a specified substring:

import { AbstractControl, ValidationErrors } from '@angular/forms';

export function containsSubstringValidator(substring: string) {
  return (control: AbstractControl): ValidationErrors | null => {
    const value = control.value;
    const containsSubstring = value && value.indexOf(substring) !== -1;

    return containsSubstring ? null : { containsSubstring: true };
  };
}

In this example, we've defined a function called containsSubstringValidator that takes a substring as a parameter and returns another function that takes an AbstractControl and returns a ValidationErrors object.

The returned function checks whether the input string contains the specified substring. If it does, it returns null (indicating that there are no errors). Otherwise, it returns an object with a single key-value pair: { containsSubstring: true }.

  1. To use this custom validator in your component, import it and add it to your form control's validator array. Here's an example:
import { containsSubstringValidator } from './custom-validator';

@Component({
  // ...
})
export class MyComponent {
  myForm = new FormGroup({
    input: new FormControl('', [
      Validators.required,
      containsSubstringValidator('foo')
    ])
  });
}

In this example, we've created a new form control called input that requires a value (using the built-in Validators.required validator) and also checks whether the input string contains the substring 'foo' (using our custom containsSubstringValidator).

When the form is submitted, if the input string does not contain the substring 'foo', the form control's errors object will contain a key-value pair with the key 'containsSubstring'.

Routing & Navigation

Angular Routing and Navigation is the process of navigating between different components and views based on the URL changes in the browser. In Angular, the Router module provides a powerful mechanism to implement the routing and navigation features.

To set up routing in an Angular app, we need to follow these steps:

  1. Import the RouterModule and Routes from @angular/router package.

  2. Define the routes array and specify the path and component for each route.

  3. Add the RouterModule to the imports array of the @NgModule decorator.

  4. Use the routerLink directive to create links to navigate to different routes.

Here's an example of setting up routing in an Angular app:

  1. Import the RouterModule and Routes from @angular/router package.
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
  1. Define the routes array and specify the path and component for each route.
typescriptCopy codeconst routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  { path: 'home', component: HomeComponent },
  { path: 'about', component: AboutComponent },
  { path: 'contact', component: ContactComponent },
  { path: '**', component: PageNotFoundComponent }
];

In this example, we have defined four routes: home, about, contact, and ** (wildcard route for handling invalid routes).

  1. Add the RouterModule to the imports array of the @NgModule decorator.
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Here, we are importing RouterModule and adding it to the imports array. We are also exporting RouterModule so that other modules can use it.

  1. Use the routerLink directive to create links to navigate to different routes.
<nav>
  <a routerLink="/home" routerLinkActive="active">Home</a>
  <a routerLink="/about" routerLinkActive="active">About</a>
  <a routerLink="/contact" routerLinkActive="active">Contact</a>
</nav>

Here, we are using the routerLink directive to create links to the home, about, and contact routes. We are also adding the routerLinkActive directive to apply a CSS class to the active link.

That's how we can set up routing in an Angular app. We can also use various other features of the Router module, such as nested routes, route parameters, route guards, and more, to create more complex routing and navigation scenarios.

Path Variables

Path variables are used to define a dynamic parameter in the URL. We can define path variables by adding a colon (:) followed by the variable name in the route path.

For example, let's say we have a route path like this:

{ path: 'users/:id', component: UserComponent }

Here, :id is the path variable, which means the actual value of id will be provided dynamically in the URL. When we navigate to a URL like /users/123, Angular will extract the value of id as 123 and pass it to the UserComponent.

We can access the path variable in the component using the ActivatedRoute service. Here's an example:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-user',
  template: `<h2>User Details: {{ userId }}</h2>`
})
export class UserComponent implements OnInit {
  userId: string;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.userId = this.route.snapshot.params['id'];
  }
}

In the above example, we inject the ActivatedRoute service in the UserComponent and extract the value of id using this.route.snapshot.params['id'].

Query Parameters

Query parameters are used to pass additional information to a component. We can define query parameters by adding a question mark (?) followed by the parameter name in the URL.

For example, let's say we have a route path like this:

{ path: 'products', component: ProductListComponent }

We can add a query parameter to the URL like this:

/products?category=electronics

Here, category is the query parameter, and its value is electronics.

We can access the query parameter in the component using the ActivatedRoute service. Here's an example:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-product-list',
  template: `<h2>Products: {{ category }}</h2>`
})
export class ProductListComponent implements OnInit {
  category: string;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.category = this.route.snapshot.queryParams['category'];
  }
}

In the above example, we inject the ActivatedRoute service in the ProductListComponent and extract the value of category using this.route.snapshot.queryParams['category'].

Observable

In Angular, an Observable is a data stream that can be used to pass data between different parts of an application. It is similar to a Promise, but with some key differences. While a Promise is used for a single value that is returned asynchronously, an Observable is used for a sequence of values that are emitted over time.

Observables are useful for handling asynchronous operations, such as making HTTP requests or handling user input, in a reactive and efficient way. They allow you to handle and manipulate data streams in a declarative and composable way, making it easier to write complex applications that respond to changing user input and external events.

Observables can be created using the RxJS library, which provides a rich set of operators and utilities for working with data streams. You can subscribe to an Observable to receive the values it emits, and you can use operators to transform and manipulate those values in various ways.

When working with Observables, it's important to unsubscribe to prevent memory leaks and unnecessary computations. To unsubscribe from an Observable, you can store the subscription in a variable and call its unsubscribe() method when you no longer need to receive updates from the Observable. Here's an example:

typescriptCopy codeconst myObservable = new Observable(observer => {
  // Emit a value
  observer.next('Hello');

  // Emit another value after 1 second
  setTimeout(() => {
    observer.next('world');
  }, 1000);

  // Handle completion
  observer.complete();
});

const mySubscription = myObservable.subscribe(value => {
  console.log(value);
});

// Unsubscribe after 3 seconds
setTimeout(() => {
  mySubscription.unsubscribe();
}, 3000);

In this example, we're creating a subscription to the myObservable Observable and logging its values to the console. We're also setting a timeout to unsubscribe after 3 seconds. When the unsubscribe() method is called, the Observable stops emitting values and any ongoing computation is canceled. This ensures that your application is not using unnecessary resources and avoids potential memory leaks.