DEV Community

Connie Leung
Connie Leung

Posted on

2

Mastering httpResource Equality in Angular

Introduction to httpResource

The httpResource function, introduced in Angular 19.2.0, provides a new way to fetch data in our Angular applications using the power of signals. It simplifies data fetching by automatically handling subscriptions and updates, integrating seamlessly with Angular's reactive programming model. The httpResource function was first introduced in Angular 19.2.0 where its first argument is a URL, and the second argument is a an optional HttpResourceOptions. This blog post will explore how the equal option within HttpResourceOptions works in Angular 19.2.2 and Angular 20.0.0-next.2.

The URL provided to httpResource can be either a simple text string or a more dynamic, reactive function. If you use a text string for the URL, the httpResource function will request the HTTP once. If you use a reactive function that returns undefined, the httpResource function delays execution. If the reactive function returns a text string, the httpResource function will monitor that function using signals, and update the resource whenever the URL changes.

HttpResourceOptions Properties

The HttpResourceOptions has the following properties:

  • injector: The injector that creates httpResource. When the component's injector is used, httpResource is automatically cleaned up when the component is destroyed.
  • defaultValue: The default value of the httpResource when it is in idle, error, or loading state.
  • parse: The transform function to transform the HTTP response before delivering the transformed result to the httpResource.
  • equal: The equal function is used to compare two sets of httpResource results. If the comparison function determines that they are equal, Angular prevents marking the signal as dirty, avoids the change detection cycle, and skips updating the view.

Setting up Angular

To get started, make sure you have the Angular CLI installed. Then, you can create a new project or update an existing one. For this blog post, we'll use the latest Angular 19 version. You can update your Angular project by running the following command:

ng update @angular/core @angular/cli
Enter fullscreen mode Exit fullscreen mode

Demo Setup

In this demo, we will create a jokeResource httpResource to request a joke API to generate random programming jokes. This will help us illustrate how the equal option works.

category = signal('');
injector = inject(Injector);

jokeResource = httpResource(() =>
  this.category()
    ? `https://v2.jokeapi.dev/joke/Programming?type=single&idRange=1-5`
    : undefined,
  {
    parse: jokeSchema.parse,
    equal: (a, b) => jokeEquality(a, b),
    defaultValue: {
      id: -1,
      error: false,
      joke: '',
      category: '',
    },
    injector: this.injector,
  }
);
Enter fullscreen mode Exit fullscreen mode

The URL is a reactive function that depends on the category signal. Initially, the category signal is empty, so the httpResource doesn't do anything and is idle.

While the httpResource is idle, the defaultValue displays an empty joke.

Installing Zod

The demo uses the Zod library to validate the HTTP response and transform it to the Joke type. Install it by running:

npm install --save-exact zod
Enter fullscreen mode Exit fullscreen mode

Defining the Zod Schema

Here is the Zod schema used to define the shape of the joke data:

import { z } from 'zod';

export const jokeSchema = z.object({
  error: z.boolean(),
  id: z.number(),
  joke: z.string(),
  category: z.string(),
});

export type Joke = z.infer<typeof jokeSchema>;
export type JokeAudit = Joke & { numUpdates: number };
Enter fullscreen mode Exit fullscreen mode

The jokeSchema schema defines the shape of the response and infers the type, Joke. JokeAudit is the linkedSignal's type, and the numUpdates property tracks the number of times the signal is updated.

Code Explanation: Equality Function

The equal function is crucial for optimizing performance. It compares the previous and new joke IDs. If this function determines the IDs are equal, Angular prevents marking the signal as dirty, avoids the change detection cycle, and skips updating the view.

const jokeEquality = (a: Joke, b: Joke) => {
  const isEqual = a.id == b.id;
  console.log('isEqual', a.id, b.id, isEqual);
  return isEqual;
};
Enter fullscreen mode Exit fullscreen mode

In this example, we are comparing jokes by their id. If the id is the same, the function returns true, indicating the jokes are equal.

The jokeAudit is a linkedSignal that depends on the jokeResource. The computation function increments numUpdates to verify the httpResource's equality function marks the signal as dirty only when the IDs differ.

jokeAudit = linkedSignal<{ joke: Joke }, JokeAudit>({
  source: () => ({ joke: this.jokeResource.value() }),
  computation: (source, previous) => {
    const previousUpdates = previous?.value?.numUpdates;
    const numUpdates = typeof previousUpdates !== 'undefined' ? previousUpdates : -1;
    return {
      ...source.joke,
      numUpdates: numUpdates + 1,
    };
  },
});
Enter fullscreen mode Exit fullscreen mode

Template

The template displays the joke data and a button to fetch a new joke:

<div>
  <div>
    <p>Programming Jokes:</p>
    <div>
      <button (click)="generateJoke()">Random joke</button>
      <span>{{ `Number of clicks: ${numClicked()}` }}</span>
    </div>

    @if (jokeResource.hasValue()) {
      @let value = jokeResource.value();
      <p><label>Id: </label><span>{{ value.id }}</span></p>
      <p><label>Category: </label><span>{{ value.category }}</span></p>
      <p><label>Joke: </label><span>{{ value.joke }}</span></p>
    }
    <hr />
    <p>Fresh Programming Joke with timestamp:</p>
    @let value = jokeAudit();
    <p><label>Id: </label><span>{{ value.id }}</span></p>
    <p><label>Category: </label><span>{{ value.category }}</span></p>
    <p><label>Joke: </label><span>{{ value.joke }}</span></p>
    <p><label>Update #: </label><span>{{ value.numUpdates }}</span></p>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Generate Joke Method

The generateJoke method sets the joke category and triggers a resource reload:

generateJoke() {
  this.category.set('Programming');
  this.numClicked.update((prev) => prev + 1);
  this.jokeResource.reload();
}
Enter fullscreen mode Exit fullscreen mode

The method sets the category to "Programming". The URL is a reactive function that returns a string. The httpResource invokes the reload method to retrieve a programming joke. When the httpResource's signal receives a new joke, the template displays it with an incremented numUpdates.

Benefits of httpResource and Equality Function

  • Performance Optimization: The equality function prevents unnecessary change detection cycles.
  • Simplified Data Fetching: httpResource simplifies asynchronous data handling with signals.
  • Reactive Updates: Automatically updates the view when the URL changes.
  • Integration with Signals: Integrates seamlessly with Angular's reactive programming model.

Does httpResource replace HttpClient?

No, httpResource is not meant to replace HttpClient. httpResource is designed to work directly with signals for data fetching and benefit from Angular's reactive programming model. It abstracts away the complexities of managing HTTP requests, status, and error handling by utilizing HttpClient under the hood.

However, HttpClient remains essential for more complex HTTP interactions, such as:

  • Making mutations (POST, PUT, DELETE requests)
  • Advanced error handling with catchError operator.
  • Combine with RxJS operators such as forkJoins to achieve batch retrieval.
  • Call HttpClient in the loader of rxResource to map an Observable to ResourceRef.

Conclusion

The httpResource function, along with the equal option, provides a powerful and efficient way to manage data fetching in Angular applications. By allowing you to define how data is compared, Angular can optimize performance and reduce unnecessary updates. This will be consistent in Angular 19.2.2 and Angular 20.0.0-next.2.

Resources

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay