What is Angular?
Angular is a development platform, built on TypeScript, for building scalable web applications with components, libraries, and tools. It was introduced to create Single Page Applications (SPAs) and bring structure and consistency to web applications, providing excellent scalability and maintainability. Angular is an open-source, JavaScript framework wholly written in TypeScript. It uses HTML’s syntax to express your application’s components clearly.
TypeScript is a superset of JavaScript that offers excellent consistency. It is highly recommended, as it provides some syntactic sugar and makes the code base more comfortable to understand and maintain. Ultimately, TypeScript code compiles down to JavaScript that can run efficiently in any environment.
What are Single Page Applications (SPAs) ?
A Single Page Application (SPA) is a web application or website that works within a web browser and loads just a single document. It does not need page reloading during its usage, and most of its content remains the same while only some of it needs updating. When the content needs to be updated, the SPA does it through JavaScript APIs. This way, users can view a website without loading the entire new page and data from the server. As a result, performance increases, and you feel like using a native application. SPAs help users be in a single, uncomplicated web space in easy, workable, and simple ways. Examples of SPAs include Gmail, Facebook, Trello, Google Maps, etc., all of which offer an outstanding user experience in the browser with no page reloading.
What is the difference between AngularJS and Angular?
AngularJS is a JavaScript-based open-source front-end web application framework that was developed by Google. It is the first version of Angular. Angular, on the other hand, is a complete rewrite of AngularJS and is a TypeScript-based open-source front-end web application framework. It is faster, more scalable, and more efficient than AngularJS. Angular is a development platform, built on TypeScript, for building scalable web applications with components, libraries, and tools. It uses HTML’s syntax to express your application’s components clearly. TypeScript is a superset of JavaScript that offers excellent consistency. It is highly recommended, as it provides some syntactic sugar and makes the code base more comfortable to understand and maintain. Ultimately, TypeScript code compiles down to JavaScript that can run efficiently in any environment.
What are the key building blocks of an Angular application?
Components:
- The fundamental building blocks of UI.
- Define visual elements and their behavior.
- Encapsulate HTML templates, CSS styles, and TypeScript logic.
- Can be nested and interact with each other.
- Examples: header, footer, product list, user profile.
Modules:
- Organize code into cohesive blocks.
- Contain components, directives, services, and other modules.
- Manage dependencies between different parts of the application.
- Define the context for dependency injection.
- Examples: app module, feature modules, shared modules.
Directives:
- Extend HTML with custom attributes and behaviors.
- Used for structural changes, DOM manipulation, or adding behavior to elements.
- Can be component directives (create new elements) or attribute directives (modify existing elements).
- Examples: ngIf, ngFor, ngClass, custom directives.
Services:
- Reusable classes that provide specific functionality.
- Encapsulate business logic, data access, and external interactions.
- Can be injected into components and other services.
- Promote code reusability and separation of concerns.
- Examples: data services, authentication services, utility services.
Pipes:
- Transform data in the template before displaying it.
- Used for formatting, filtering, or sorting data.
- Chained together to create complex transformations.
- Examples: date, currency, uppercase, lowercase, custom pipes.
Dependency Injection:
- A mechanism for providing dependencies to components and services.
- Facilitates loose coupling and testability.
- Manages relationships between classes and their dependencies.
Differentiate between components and directives.
Directives are used to add behaviour to an existing DOM element or to create a new DOM element. They are used when you need to manipulate existing elements on your page. Directives can be classified into three types: Attribute Directives, Structural Directives, and Components. Attribute directives are used to change the appearance or behaviour of an element, component, or another directive (e.g.: ngStyle). Structural directives are used to change the DOM layout by adding and removing DOM elements (e.g.: ngIf,ngFor). Components are directives with a template.
Components are a type of directive that have their own view (HTML and styles) and are used to create reusable groups of HTML elements. They are used when you need to create a custom element or section on your page, such as a custom shopping cart page or a custom user profile page.
In general, you should use components when you need to create a reusable group of HTML elements, and you should use directives when you need to manipulate existing elements on your page
Explain data binding in Angular (One-way vs. Two-way) and its uses.
Data binding in Angular refers to the synchronization of data between the component’s TypeScript code and its view (HTML template). It’s a fundamental feature that simplifies development and keeps the UI in sync with the underlying data model.
Angular supports two main types of data binding:
One-way binding (Interpolation and Property binding):
- Data flows from the component to the view (read-only).
- Changes in the component’s data are reflected in the view, but not vice versa.
- Interpolation: Displays component data within text elements using double curly braces
{{}}
. Example:{{ productName }}
- Property binding: Sets the value of an element property to a component property using square brackets
[]
. Example:[disabled]="isButtonDisabled"
Two-way binding (NgModel):
- Data flows in both directions, between the component and the view.
- Changes in the component’s data trigger updates in the view, and changes in the view (user input) update the component’s data.
- NgModel directive: Binds an input element to a component property using
[(ngModel)]
syntax. Example:<input [(ngModel)]="username">
Uses of different binding types:
- Interpolation: Displaying dynamic content in text elements.
- Property binding: Setting element properties, attributes, class bindings, styles, etc.
- Two-way binding: Creating interactive forms with input fields, dropdowns, checkboxes, etc.
Reactive Forms vs Template-Driven Forms
Reactive Forms
- Definition: Forms are built and managed in the component class using TypeScript.
- Form Model: Uses a reactive approach with FormControl, FormGroup, and FormArray classes to model the form structure.
- Validation: Uses Validators to define validation rules within the component class.
- Data Binding: Uses two-way data binding with
[formGroup]
and[formControl]
directives. - Testing: Easier to unit test as form logic is separated from the template.
- Suitable for:
- Complex forms with dynamic behavior
- Forms requiring extensive validation
- Forms tightly integrated with application logic
- Large-scale applications with a focus on maintainability
Template-Driven Forms
- Definition: Forms are built directly in the HTML template using directives.
- Form Model: Uses NgForm and FormControl directives to model the form structure.
- Validation: Uses built-in validators or custom directives for validation in the template.
- Data Binding: Uses two-way data binding with
[(ngModel)]
directive. - Testing: More challenging to unit test as form logic is embedded in the template.
- Suitable for:
- Simple forms with straightforward validation
- Forms with less emphasis on testing
- Smaller-scale applications or prototypes
Key Differences:
Feature | Reactive Forms | Template-Driven Forms |
---|---|---|
Form model | Built in component class | Built in template |
Validation | Defined in component class | Defined in template |
Data binding | [formGroup] , [formControl] | [(ngModel)] |
Testing | Easier | More challenging |
Complexity | Better for complex forms | Better for simple forms |
Flexibility | More flexible and dynamic | Less flexible |
Maintainability | Easier to maintain | Can become harder to manage |
Here is an example of a reactive form:
<form [formGroup]="myForm" (ngSubmit)="onSubmit()"> <input type="text" formControlName="firstName"> <input type="text" formControlName="lastName"> <button type="submit">Submit</button> </form>
import { Component } from '@angular/core'; import { FormGroup, FormControl } from '@angular/forms'; @Component({ selector: 'app-root', templateUrl: './app.component.html', }) export class AppComponent { myForm = new FormGroup({ firstName: new FormControl(), lastName: new FormControl(), }); onSubmit() { console.log(this.myForm.value); } }
Here is an example of a template-driven form:
<form #myForm="ngForm"> <input type="text" name="firstName" [(ngModel)]="firstName" required> <input type="text" name="lastName" [(ngModel)]="lastName" required> <button type="submit">Submit</button> </form>
Explain the purpose of Dependency Injection in Angular.
Dependency Injection (DI) is a fundamental design pattern in Angular that simplifies and improves the way components obtain the resources they need to function. Essentially, it eliminates the need for components to directly create or manage their own dependencies, promoting loose coupling and code maintainability.
Here’s how DI works in Angular:
- Components declare dependencies: Components specify the services, data, or other objects they need through constructor arguments or annotations.
- Injector provides dependencies: Angular comes with a built-in injector responsible for creating and managing dependencies. When a component is created, the injector retrieves the required dependencies and injects them into the component constructor.
- Components use injected dependencies: Components utilize the injected objects to fulfill their functionalities, without worrying about their creation or initialization.
Benefits of using DI in Angular:
- Loose coupling: Components rely on abstractions (interfaces) rather than concrete implementations, making them adaptable and easier to test.
- Code maintainability: Changes to dependencies are isolated within the injector, minimizing impact on component code.
- Testability: Components become easier to test in isolation without needing actual dependencies, simplifying test setup and execution.
- Flexibility: Different implementations of the same dependency can be easily injected based on context or environment.
- Modular design: Promotes code reusability and separation of concerns, leading to cleaner and more manageable applications.
- Reduced boilerplate: Eliminates the need for components to manually create or manage dependencies, saving development time.
Here is an example of how to use DI in Angular:
import { Component } from '@angular/core'; import { MyService } from './my.service'; @Component({ selector: 'app-my-component', template: '<h1>{{ message }}</h1>', providers: [MyService] }) export class MyComponent { message: string; constructor(private myService: MyService) { this.message = myService.getMessage(); } }
In this example, we have a component called MyComponent
that depends on a service called MyService
. The MyService
is provided in the providers
array of the @Component
decorator. The MyComponent
constructor takes an instance of MyService
as a parameter, which is automatically injected by the Angular DI system. The message
property of MyComponent
is set to the value returned by the getMessage
method of MyService
.
What are Angular lifecycle hooks ?
In Angular, lifecycle hooks are methods that are called at specific stages of a component’s lifecycle. They allow you to perform actions at certain points in the component’s lifecycle, such as when the component is created, updated, or destroyed. Here are some of the most commonly used lifecycle hooks in Angular:
- ngOnInit: This hook is called after the component has been initialized and its inputs have been bound. It is used to perform any initialization logic that the component requires. You can use this hook to fetch data from a remote server and initialize the component’s state with the data.
- ngOnChanges: This hook is called when the value of an input property changes. It is used to perform any logic that needs to be executed when the input property changes. You can use this hook to perform any validation logic on the input properties.
- ngDoCheck: This hook is called during every change detection cycle. It is used to perform any custom change detection logic that the component requires.
- ngAfterViewInit: This hook is called after the component’s view has been initialized. It is used to perform any initialization logic that requires access to the component’s view.
- ngOnDestroy: This hook is called just before the component is destroyed. It is used to perform any cleanup logic that the component requires, such as unsubscribing from observables.
Explain how routing works in Angular. What are the different types of routing?
Routing in Angular enables navigation between different views (components) within a single-page application (SPA). It allows users to transition between different sections of the application without full page reloads, providing a seamless user experience.
Here’s how routing works in Angular:
- Define Routes:
- Create a routing module (
AppRoutingModule
) using theRouterModule
. - Define routes using the
routes
array, specifying:- Path: The URL segment that triggers the route.
- Component: The component to display when the route is activated.
- Create a routing module (
- Configure Router:
- Import the
RouterModule
in the root module (AppModule
) and configure it with the defined routes. - Use the
router-outlet
directive in the main template to mark where components will be displayed based on routing.
- Import the
- Navigate Between Routes:
- Use the
Router
service to navigate between routes programmatically. - Employ router links (
<a routerLink="/path">
) to create links that trigger navigation when clicked.
- Use the
Types of Routing in Angular:
- Basic Routing:
- Maps a single path to a single component.
- Ideal for simple applications with straightforward navigation.
- Parameter-Based Routing:
- Dynamically passes data to components using route parameters.
- Example:
/products/:productId
to display a specific product.
- Child Routing:
- Nests routes within other routes to create hierarchical navigation structures.
- Useful for organizing components and managing complex navigation flows.
- Lazy Loading:
- Loads modules asynchronously as needed, improving initial load time and performance.
- Ideal for large applications with many modules.
Explain Lazy Loading.
Lazy loading is a technique in Angular that allows you to load modules asynchronously, on-demand when they are needed. It is used to improve the initial loading time of your application by loading only the necessary code for the current route, instead of loading the entire application upfront.
By default, Angular loads all the modules when the application starts, which can lead to slower performance and longer load times. With lazy loading, you can split your application into multiple feature modules and load them only when they are needed. This helps to reduce the initial bundle size of your application and improve the overall performance.
To implement lazy loading in Angular, you need to use the loadChildren property in the RouterModule. The loadChildren property takes a string that specifies the path to the module that should be loaded lazily.
Here is an example of how to use lazy loading in Angular:
const routes: Routes = [ { path: 'home', component: HomeComponent }, { path: 'about', loadChildren: () => import('./about/about.module').then(m => m.AboutModule) } ];
In this example, we define a lazy-loaded route with the path about
and the module AboutModule
. When the user navigates to the about
route, Angular will load the AboutModule
module lazily.
What are route guards ?
Route guards are mechanisms in Angular that allow you to control access to certain parts of your application. They are used to protect routes from unauthorized access and to prevent users from navigating to parts of the application without authorization. Route guards are used to control access to a route and are implemented as classes that implement the CanActivate, CanActivateChild, CanDeactivate, and CanLoad interfaces.
Here are the different types of route guards and navigation guards in Angular:
- CanActivate: Controls if a route can be activated.
- CanActivateChild: Controls if children of a route can be activated.
- CanDeactivate: Controls if the user can leave a route.
- CanLoad: Controls if a route can even be loaded.
- Resolve: Applies specifically to lazy-loaded modules, deciding whether to load them before initiating navigation.
Demonstrate different ways to navigate between components in your application.
Here are the different ways to navigate between components in an Angular application:
1. Router Links:
- Create clickable links using the
routerLink
directive in your templates. - Example:
<a routerLink="/products">View Products</a>
- Navigates to the specified route when clicked.
- Supports parameters for dynamic routing:
<a routerLink="/products/:id">Product Details</a>
- Can be styled using CSS like regular anchor tags.
2. Router.navigate() Method:
- Navigate programmatically using the
Router
service. - Inject the
Router
service into your component. - Call
this.router.navigate(['/target-route'])
to navigate. - Use
this.router.navigate(['/target-route'], { queryParams: { key: value } })
to pass query parameters. - Ideal for navigation triggered by actions or events within components.
3. Navigation Extras:
- Provide additional navigation options using
NavigationExtras
. - Example:
this.router.navigate(['/home'], { relativeTo: this.route });
- Navigate relative to the current route or a parent route.
- Preserve query parameters or fragment:
this.router.navigate(['/home'], { queryParamsHandling: 'preserve', fragment: 'anchor' });
4. Router Events:
- Access events during navigation using the
Router
service. - Example:
this.router.events.subscribe(event => { /* Handle navigation events */ });
- Useful for tracking navigation changes, performing actions before or after navigation, or implementing custom logic based on navigation events.
5. Navigation Directives:
- Use directives like
routerLinkActive
androuterLinkActiveOptions
to apply styles to active links. - Example:
<a routerLink="/home" routerLinkActive="active">Home</a>
- Enhances visual feedback for the current route.
6. Navigation in a Service:
- Inject the
Router
service into a service and navigate from there. - Useful for shared navigation logic across multiple components.
Explain the difference between AoT (Ahead-of-Time) and JIT (Just-in-Time) compilation in Angular.
In Angular, both AoT (Ahead-of-Time) and JIT (Just-in-Time) compilation translate your TypeScript code into JavaScript that the browser can understand. However, they differ in when and how this compilation happens
JIT is the default compilation method used in Angular. It compiles the application at runtime in the browser. With JIT, the application is compiled on the fly as the user navigates through the application. This means that the application is slower to load initially, but it is faster to develop and easier to debug.
AoT, on the other hand, compiles the application during the build process, before the application is deployed to the server. This means that the application is faster to load initially, but it is slower to develop and harder to debug. AoT also provides better security, as the compiled code is more difficult to reverse-engineer.
Here are some of the key differences between AoT and JIT:
- Performance: AoT provides better performance than JIT, as the application is compiled ahead of time.
- Debugging: JIT is easier to debug than AoT, as the application is compiled at runtime.
- Development: JIT is faster to develop than AoT, as the application is compiled on the fly.
- Security: AoT provides better security than JIT, as the compiled code is more difficult to reverse-engineer.
In general, you should use AoT for production builds, as it provides better performance and security, and you should use JIT for development builds, as it is faster to develop and easier to debug.
What are RxJS observables and how are they used in Angular? Difference between observables and promise
RxJS Observables are a type of data structure in the RxJS library that represent a stream of data that can arrive over time. Both promises and observables are used to handle asynchronous operations. Observables are similar to Promises, but they can emit multiple values over time, whereas Promises can only emit a single value. Observables can also be cancelled, whereas Promises cannot.
Here are some key differences between promises and observables:
- Execution: Promises execute immediately on creation, while observables only start if you subscribe to them. This is why observables are called lazy.
- Values: A promise provides a single value, whereas an observable is a stream of values (from 0 to multiple values).
- Synchronous/Asynchronous: A promise is always asynchronous, while an observable can be either synchronous or asynchronous.
- Cancellation: An observable is cancellable, while a promise will eventually call the success or failed callback even when you don’t need the notification or the result it provides anymore.
Here is an example of how to use a promise:
function getData() { return new Promise((resolve, reject) => { setTimeout(() => { resolve("Data received"); }, 1000); }); } getData().then((data) => { console.log(data); });
In the above example, we define a function getData
that returns a promise that resolves after 1 second with the message “Data received”. We then call the getData
function and use the then
method to log the data to the console.
Here is an example of how to use an observable:
import { Observable } from "rxjs"; const observable = new Observable((subscriber) => { subscriber.next("Hello"); subscriber.next("World"); subscriber.complete(); }); observable.subscribe({ next: (value) => console.log(value), complete: () => console.log("Complete"), });
In the above example, we define an observable that emits the values “Hello” and “World”, and then completes. We then subscribe to the observable and log the values to the console.
In Angular, observables are used extensively for handling asynchronous data streams and events. For example, the HttpClient service in Angular returns an observable when making HTTP requests. The Router service in Angular provides observables for handling navigation events.
Observables are also used for handling user input events, such as button clicks and form submissions. They are used to handle events that occur over time and to respond to changes in the application state.
Difference between Property and Event Binding.
In Angular, property binding and event binding are two different types of data binding.
Property binding is used to set the value of a property on an element or component. It is used to pass data from the component to the view. Property binding is denoted by square brackets []
in the template.
<input [value]="username">
In this example, we use property binding to set the value of the input
element to the value of the username
property in the component.
Event binding is used to respond to events, such as button clicks and form submissions. It is used to pass data from the view to the component. Event binding is denoted by parentheses ()
in the template.
<button (click)="submitForm()">Submit</button>
In this example, we use event binding to call the submitForm()
method in the component when the user clicks the button
element.
How to communicate between parent and child component in angular using @Input() ?
Here is an example of how to pass data from a parent component to a child component using Property Binding (@Input
):
- In the parent component template, bind the property to the child component:
import { Component } from '@angular/core'; @Component({ selector: 'app-parent', template: ` <h2>Parent Component</h2> <app-child [message]="parentMessage"></app-child> `, }) export class ParentComponent { parentMessage = 'message from parent'; }
- In the child component, define a property with the @Input decorator:
import { Component, Input } from '@angular/core'; @Component({ selector: 'app-child', template: ` <h3>Child Component</h3> <p>{{ message }}</p> `, }) export class ChildComponent { @Input() message: string; }
In this example, we define a property called parentMessage
in the parent component and pass it to the child component using the @Input decorator. We then define a property called message
in the child component and bind it to the parentMessage
property in the parent component. Finally, we display the message
property in the child component template.
How to communicate between parent and child component in angular using @Onput() ?
In Angular, you can use the Event Binding (@Output
) to communicate between child and parent components. Here is an example of how to pass data from a child component to a parent component using @Output():
- In the child component, define an event emitter with the @Output() decorator:
import { Component, EventEmitter, Output } 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'); } }
- In the parent component, listen for the event using the (messageEvent) binding:
import { Component } from '@angular/core'; @Component({ selector: 'app-parent', template: ` <h2>Parent Component</h2> <app-child (messageEvent)="receiveMessage($event)"></app-child> <p>{{ message }}</p> `, }) export class ParentComponent { message: string; receiveMessage($event) { this.message = $event; } }
In this example, we define a child component with a button that emits a message using the @Output() decorator. We then define a parent component that receives the message using the (messageEvent) binding and displays it in the template.
How can you achieve communication between sibling components.
Shared Service:
- Purpose: Create a central service to hold shared data and behavior, accessible to both siblings.
- Implementation:
- Generate a service using Angular CLI:
ng generate service shared-data
- Define properties and methods within the service to facilitate communication.
- Inject the service into both sibling components using dependency injection.
- Components can access and modify shared data through the service.
- Generate a service using Angular CLI:
shared-data.service.ts:
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class SharedDataService { sharedMessage = 'Initial message'; updateMessage(newMessage: string) { this.sharedMessage = newMessage; } }
sibling1.component.ts:
import { Component, OnInit } from '@angular/core'; import { SharedDataService } from './shared-data.service'; @Component({ selector: 'app-sibling1', template: ` <p>Message from sibling 2: {{ sharedMessage }}</p> <button (click)="updateMessage()">Update Message</button> ` }) export class Sibling1Component implements OnInit { sharedMessage: string; constructor(private sharedDataService: SharedDataService) {} ngOnInit() { this.sharedMessage = this.sharedDataService.sharedMessage; } updateMessage() { this.sharedDataService.updateMessage('Message updated by Sibling 1'); } }
sibling2.component.ts: (Similarly injects and uses SharedDataService
)
Parent-Child Communication with a Proxy:
- Purpose: Use the parent component as a mediator for communication.
- Implementation:
- Siblings emit events to the parent using
@Output()
. - Parent passes data between siblings using property binding (
@Input()
).
- Siblings emit events to the parent using
BehaviorSubject or Observable:
- Purpose: Create a shared data stream for communication.
- Implementation:
- Inject a
BehaviorSubject
or create anObservable
in a shared service. - Siblings subscribe to the stream to receive updates.
- Siblings emit new values using the
next()
method.
- Inject a
What is the use of @ViewChild ?
ViewChild is a decorator in Angular that is used to get a reference to a child component or element in the parent component. It is used to access the properties and methods of the child component from the parent component.
ViewChild is used when you need to access a child component or element from the parent component. It is commonly used to manipulate the properties and methods of the child component from the parent component.
import { Component, ViewChild } from '@angular/core'; import { ChildComponent } from './child.component'; @Component({ selector: 'app-parent', template: ` <h2>Parent Component</h2> <button (click)="increment()">Increment</button> <button (click)="decrement()">Decrement</button> `, }) export class ParentComponent { @ViewChild(ChildComponent) child: ChildComponent; increment() { this.child.counter++; } decrement() { this.child.counter--; } }
In this example, we define a parent component with two buttons that increment and decrement a counter in the child component using ViewChild. We then define a child component with a counter property that is incremented and decremented by the parent component.
What are pipes in angular ?
Pipes in Angular are used to transform data before displaying it in the view. Pipes are simple functions that accept an input value and return a transformed value. Angular provides several built-in pipes for common data transformations, including pipes for formatting dates, numbers, and currency amounts.
Here are some commonly used built-in pipes in Angular:
- DatePipe: Formats a date value according to locale rules.
- UpperCasePipe: Transforms text to all uppercase.
- LowerCasePipe: Transforms text to all lowercase.
- CurrencyPipe: Transforms a number to a currency string, formatted according to locale rules.
- DecimalPipe: Transforms a number into a string with a decimal point, formatted according to locale rules.
- PercentPipe: Transforms a number to a percentage string, formatted according to locale rules.
Here is an example of how to use the DatePipe
to format a date:
<p>{{ today | date }}</p>
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', }) export class AppComponent { today = new Date(); }
This will display the current date in the default format for the current locale.
You can also create your own custom pipes to encapsulate custom transformations and use them in template expressions like built-in pipes.
Pure vs Impure Pipes
In Angular, there are two types of pipes: pure pipes and impure pipes.
Pure pipes are pipes that are only called when Angular detects a change in the value or the parameters passed to a pipe. Pure pipes must be pure functions, meaning they take an input and return an output without any side effects. Pure pipes are suitable for simple transformations of primitive values such as strings, numbers, and booleans.
Here is an example of a pure pipe:
import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'reverse', pure: true, }) export class ReversePipe implements PipeTransform { transform(value: string): string { return value.split('').reverse().join(''); } }
Impure pipes are pipes that are called for every change detection cycle, regardless of whether the value or parameter(s) have changed. Impure pipes can detect changes within objects, including changes to array entries or object properties. Impure pipes are suitable for complex transformations of objects and arrays.
Here is an example of an impure pipe:
import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'filter', pure: false, }) export class FilterPipe implements PipeTransform { transform(items: any[], filter: any): any[] { if (!items || !filter) { return items; } return items.filter(item => item.name.indexOf(filter) !== -1); } }
In this example, the FilterPipe
is an impure pipe that filters an array of items based on a filter string.
What are standalone components ?
Standalone components are self-contained UI elements that can be easily reused across an application. They are designed to be independent of other components and can be used without any additional dependencies or setup. Standalone components are often created as individual files or modules and can be easily imported into other parts of an application. They typically consist of HTML, CSS, and JavaScript and are designed to be reusable and can be easily modified to fit different use cases. Standalone components are supported from Angular 14 onwards.
Key Features:
- No NgModules: Standalone components are not declared within NgModules, reducing boilerplate code and simplifying application structure.
- Direct Imports: Dependencies are imported directly into the component class, enhancing clarity and maintainability.
- Tree-shakable: Unused standalone components can be automatically removed during the build process, resulting in smaller bundle sizes and optimized performance.
import { Component } from '@angular/core'; @Component({ selector: 'app-standalone-component', standalone: true, // Mark as standalone imports: [CommonModule], // Import necessary modules directly template: ` <p>This is a standalone component!</p> ` }) export class StandaloneComponent { }
Common Use Cases:
- Creating reusable UI elements like buttons, cards, or modals.
- Building feature-specific components with their own dependencies.
- Structuring applications in a more modular and maintainable way.
What are angular elements?
Angular Elements is a feature that allows you to create custom elements using Angular. Custom elements are a web standard for defining new HTML elements in a framework-agnostic way. With Angular Elements, you can create native custom elements and author web components with Angular.
import { Component, Input } from '@angular/core'; import { createCustomElement } from '@angular/elements'; @Component({ selector: 'app-custom-element', template: ` <div> <h1>{{ title }}</h1> <p>{{ content }}</p> </div> `, }) export class CustomElementComponent { @Input() title: string; @Input() content: string; } const CustomElement = createCustomElement(CustomElementComponent, { injector }); customElements.define('app-custom-element', CustomElement);
You can use Angular Elements to create self-contained UI elements that can be easily reused across an application. Some benefits of using Angular Elements include:
- They can be used in pretty much all major JavaScript frameworks.
- They can be easily reused across an application.
- They are self-contained and can be used independently of other components.
- They encourage modular development and help maintain a clean codebase.
What are service workers in angular ?
Service workers are scripts that run in the background of a web application and manage caching for an application. They function as a network proxy, intercepting all outgoing HTTP requests made by the application and choosing how to respond to them. Service workers can query a local cache and deliver a cached response if one is available, and they can also cache resources referenced in HTML and even the initial request to index.html.
In Angular, adding a service worker to an application is one of the steps for turning an application into a Progressive Web App (also known as a PWA). Angular’s service worker is designed to optimize the end user experience of using an application over a slow or unreliable network connection, while also minimizing the risks of serving outdated content.
Here is an example of how to add a service worker to an Angular application:
- Install the
@angular/service-worker
package:
ng add @angular/service-worker
- Import the
ServiceWorkerModule
in yourapp.module.ts
file:
import { ServiceWorkerModule } from '@angular/service-worker'; import { environment } from '../environments/environment'; @NgModule({ imports: [ BrowserModule, ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }), ], bootstrap: [AppComponent], }) export class AppModule {}
- Build your application with the
--prod
flag:
ng build --prod
- Serve your application with the
--prod
flag:
ng serve --prod
What is server side rendering ?
Server-side rendering (SSR) is a process that involves rendering pages on the server, resulting in initial HTML content which contains initial page state. Once the HTML content is delivered to a browser, Angular initializes the application and utilizes the data contained within the HTML.
In Angular, you can use Angular Universal to implement server-side rendering. Angular Universal is a package that allows you to render your Angular application on the server using Express, a popular web framework.
Some benefits of using server-side rendering in Angular include:
- Improved performance: SSR can improve the performance of web applications by delivering fully rendered HTML to the client, which can be parsed and displayed even before the application JavaScript is downloaded.
- Improved Core Web Vitals: SSR results in performance improvements that can be measured using Core Web Vitals (CWV) statistics, such as reduced First Contentful Paint (FCP) and Largest Contentful Paint (LCP), as well as Cumulative Layout Shift (CLS).
- Better SEO: SSR can improve the search engine optimization (SEO) of web applications by making it easier for search engines to crawl and index the content of the application
How is unit testing done in Angular ?
Unit testing is a process of testing individual units of code, often isolated from their dependencies, to ensure they work as intended. In Angular, unit testing is performed using the Jasmine testing framework and the Karma test runner. The Angular CLI provides everything you need to test an Angular application with Jasmine and Karma. Here are the steps to perform unit testing in Angular:
- Set up testing: The Angular CLI downloads and installs everything you need to test an Angular application with Jasmine and Karma. The project you create with the CLI is immediately ready to test. Just run the
ng test
CLI command. - Write tests: Write tests for your Angular application using Jasmine syntax. Tests can be written for components, services, directives, pipes, and other Angular constructs.
- Run tests: Run the tests using the Karma test runner. The Karma test runner launches a browser and runs the tests in it. The test results are displayed in the browser using the Karma Jasmine HTML Reporter.
Here is an example of a simple test for an Angular component:
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MyComponent } from './my.component'; describe('MyComponent', () => { let component: MyComponent; let fixture: ComponentFixture<MyComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ MyComponent ] }) .compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(MyComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); });
In this example, we have created a test for an Angular component called MyComponent
. The test checks whether the component is created successfully or not.