Skip to main content

Declarative Programming in Angular with Async Pipe and shareReplay

A declarative approach is a way that focuses on writing code that specifies what the application should do, rather than detailing how it should be done. For example, with the async pipe in Angular, we don’t need to write code to manually subscribe to an Observable, handle its data, and update the view. Instead, we simply specify in the template that we want the data from the Observable using the async pipe. Angular handles all the underlying processes to retrieve and display the data

It's often used in reactive programming with RxJS and Angular's built-in features, such as the async pipe.

export class ProductComponent {

  product$ = this.productService.getProduct();
  
  constructor(private productService: ProductService) {}
}

The product observable will hold the product data and the async pipe in the template will automatically subscribe and unsubscribe observable

<div *ngIf="product$ | async as product">
  <h1>{{ product.name }}</h1>
  <p>{{ product.description }}</p>
</div>

Benefit of declarative approach:

  • Reduces boilerplate code, making the codebase cleaner and more maintainable.
  • Automates state management, minimizing the risk of errors related to manual state management and keeping data in sync between components.
  • Improves performance through Angular's change detection strategy using OnPush, which triggers only when a component is marked as dirty.
  • Helps manage complexity in larger applications by keeping the codebase clean and modular.
shareReplay

To improve performance and network load we can cach the emissions from observables. Caching/storing data locally can:

  • Improve responsiveness
  • Reduce bandwidth and network consumption
  • Lower backend server load
  • Decrease redundant computations

One approach to cach the data is to use shareReplay(bufferSize?: number). 

ShareReplay is an operator in RxJS that caches and shares/replays the defined number of emissions subscribers. This is useful in situations where we want multiple subscribers to receive the same values without resubscribing to the source Observable or re-executing expensive operations.

      this.data$ = this.http.get('https://api.example.com/data').pipe(
        // Cache the response and share it with new subscribers
        shareReplay(1)
      );

bufferSize: This parameter specifies the maximum number of values to store in the buffer. When a new subscriber joins, it will receive the last bufferSize values emitted by the source observable. If not provided, the buffer size is unlimited.

Note: The location of the shareReplay operator is crucial. Before using shareReplay, all data processing happens prior to caching. However, once shareReplay is applied, the code below shareReplay is executed during each subscription.







Comments

Popular posts from this blog

Maximizing Efficiency: The Power of Database Indexing

What is database performance? There are two main aspects of database performance: response time and throughput . Response time is the total time it takes to process a single query and returns result to the user. It's critical metrics because it directly impacts the user's experience, especially in applications where fast access to data is essential. The response time includes CPU time (complex queries will require more computational power and increase processing time), disk access, lock waits in multiple-user environment (more about database transaction ), network traffic. Throughput refers to how many translations the system can handle per second (TPS). A transaction could include different activities to retrieve and manipulate data. A single command like SELECT, INSERT, UPDATE, DELETE or a series of commands could be used to trigger these activities. If you’re running an e-commerce site, a single transaction might include checking the inventory, confirming the payment, and...

LINQ - Deferred Execution

Deferred Execution means that queries are not executed immediately at the time it's being created. The benefit of this is to improve performance by avoiding unnecessary executions. It also allows the query to be extendable when needed such as sorting, filtering. In the example below, the queries to retrieve courses are not being executed yet var context = new DbContext(); var courses = context.Courses      .Where(c => c.Level == 1)      .OrderBy(c => c.Name); The query is only executed when one of the following scenarios occurs: Iterating over query variable Calling ToList, ToArray, ToDictionary Calling First, Last, Single, Count, Max, Min, Average For example when we loop through the course or turn the result to a list: foreach ( var c in courses) Console.WriteLine(c.Name); OR context.Courses      .Where(c => c.Level == 1)      .OrderBy(c => c.Name).ToList();

Solid Principles for Software Design - Part 2

Interface Segregation Principle The Interface Segregation Principle (ISP) is one of the five SOLID principles of object-oriented design, which recommends that "Clients should not be forced to depend on interfaces they do not use". This means we should avoid implementing an interface that has unnecessary methods and therefore not going to be implemented.  Some signs of violating ISP are: Having a "fat" interface, which means having a high number of methods in one interface that are not so related to each other or low cohesion. Empty implementation of methods, certain methods of interface are not needed for implementation. Considering the following example, we violate the principle because CannonPrinter is designed only with the functionality to print, leaving the scan and fax method unimplemented. interface IMultiFunction {      void print(); void scan(); void fax(); } public class HPPrinterNScanner implements ImultiFunction { @Override public void pr...