<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ingila Ejaz</title>
    <description>The latest articles on DEV Community by Ingila Ejaz (@ingila185).</description>
    <link>https://dev.to/ingila185</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1396787%2F2a683704-6660-43e6-b6f9-d4db59026b39.jpg</url>
      <title>DEV Community: Ingila Ejaz</title>
      <link>https://dev.to/ingila185</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ingila185"/>
    <language>en</language>
    <item>
      <title>Secure Airtable Integration: Mastering OAuth 2.0 PKCE with Node.js 22 and Angular 20</title>
      <dc:creator>Ingila Ejaz</dc:creator>
      <pubDate>Mon, 28 Jul 2025 14:46:11 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/secure-airtable-integration-mastering-oauth-20-pkce-with-nodejs-22-and-angular-20-71a</link>
      <guid>https://dev.to/playfulprogramming/secure-airtable-integration-mastering-oauth-20-pkce-with-nodejs-22-and-angular-20-71a</guid>
      <description>&lt;p&gt;Integrating third-party services into B2B and SaaS applications demands robust security. When connecting to powerful platforms like Airtable, understanding their authentication mechanisms is paramount. Airtable, embracing modern security standards, utilizes OAuth 2.0 with PKCE (Proof Key for Code Exchange) for its authentication flow, a critical extension that significantly bolsters security.&lt;/p&gt;

&lt;p&gt;Pronounced “PIX-y,” PKCE is an ingenious mechanism designed specifically to prevent authorization code interception attacks. This ensures that the application requesting the final access token is indeed the very same one that initiated the authorization process. This “proof-of-possession” is vital for safeguarding user data and maintaining application integrity, especially for public clients like single-page applications or mobile apps.&lt;/p&gt;

&lt;p&gt;Let’s break down the core components of PKCE that make this security possible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;code_verifier:&lt;/code&gt; This is a high-entropy, cryptographically random secret generated by your application at the very beginning of the authorization flow. Think of it as a unique, one-time-use password that your application creates for a single login session. It's never sent directly to the authorization server (Airtable) in the initial request, remaining a secret known only to your application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;code_challenge_method:&lt;/code&gt; A simple string (e.g., S256) that explicitly communicates to Airtable which hashing algorithm was used to transform the code_verifier. It's essentially your application declaring: "The code_challenge I'm providing was generated using the SHA-256 hashing algorithm." This standardization allows the authorization server to independently verify the challenge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;code_challenge:&lt;/code&gt; This is the hashed and specially encoded version of your code_verifier. It's the only PKCE parameter sent to Airtable when your application first redirects the user to the authorization endpoint. Airtable securely stores this code_challenge temporarily. When your application later exchanges the authorization code for an access token, it must present the original code_verifier. Airtable will then re-compute the code_challenge from the code_verifier it just received and compare it against the one it initially stored. If they don't match, the token exchange is denied, effectively thwarting any unauthorized attempts. For this tutorial, we leverage Node.js's built-in crypto module, mirroring the robust practices often seen in official documentation and advanced implementations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the following sections, we’ll walk through the practical steps of setting up your application to authenticate with Airtable using this secure PKCE flow. We’ll be working with an Airtable base similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flfv1lcoxzqjqkmpujixr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flfv1lcoxzqjqkmpujixr.png" alt="Base for Airtable OAuth" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Create OAuth Integrations in Airtable
&lt;/h2&gt;

&lt;p&gt;First things first, create an Airtable Integration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="https://airtable.com/create/oauth" rel="noopener noreferrer"&gt;https://airtable.com/create/oauth&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click on Register New OAuth Integration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fputkc2j4k7yig49pzl2h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fputkc2j4k7yig49pzl2h.png" alt="Register new OAuth Integration" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next page, give your integration a name and redirect URL. This URL will be redirected when the authentication is successful.&lt;/p&gt;

&lt;p&gt;Now click on Register Integration and you will be redirected to another page with your client_id and client_secret.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3g1dvzbynmp5yisr9ius.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3g1dvzbynmp5yisr9ius.png" alt="Register new Integration in Airtable" width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The client_secret is optional but recommended so go ahead and create a client_secret. Save the client_id in your .env file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvienbn59c69olegntaor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvienbn59c69olegntaor.png" alt="Airtable Client ID Creation" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can select the scope of access as well from the Scopes section. For now, I have selected read-based access only.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylqsdlniwcenpvp6l7qv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylqsdlniwcenpvp6l7qv.png" alt="Selecting scopes for Airtable Integration" width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You also need to generate client_secret from the integration panel. To do this, click on Generate client secret.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr7gw8tqc8wzn158zz2ok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr7gw8tqc8wzn158zz2ok.png" alt="Generate Client_secret" width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click generate on the dialog and copy the client_secret.&lt;/p&gt;

&lt;p&gt;Note: the client_secret is only shown once so make sure you copy and paste it in a secure file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq65hb35jlrf1udxk6m51.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq65hb35jlrf1udxk6m51.png" alt="Create client_secret (optional but recommended)" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1cutf7gzsdobpio1o0ac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1cutf7gzsdobpio1o0ac.png" alt="Create client_secret Confirmation" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on Save Changes and you should now see your integration in the integrations list:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l64gdmq8aiw4hg15j0h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l64gdmq8aiw4hg15j0h.png" alt="Create new Integration in Airtable" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Create a Backend — Node.js 22 + Express
&lt;/h2&gt;

&lt;p&gt;Create New Node.js Project. I am using Node.js 22 but you are free to use any version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir airtable-integration-backend
cd airtable-integration-backend
npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the following packages as we will be using them later on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install express dotenv cors crypto axios qs

#dotenv - For .env files to store client_id, client_secret, redirect_url and scope
#express - For making the http calls
#cors - For allowing cors (locally)
#crypto - To generate code_verifier, code_challenge and code_challenge_method
#axios - To call airtable APIs and fetch response
#qs - To build queryString
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a new file called server.js. Your project structure should look something like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpj5y6dfhcj8z5apqjnl0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpj5y6dfhcj8z5apqjnl0.png" alt="Node.js project structure" width="435" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will create these functions in our server.js file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;get_authorization_url&lt;/code&gt;&lt;/strong&gt; — This function is designed to initiate an OAuth 2.0 authorization flow with Airtable, specifically utilizing the Proof Key for Code Exchange (PKCE) extension for enhanced security.&lt;/p&gt;

&lt;p&gt;The primary goal of this function is to generate and return an authorization URL to the client. The client (e.g., a web browser) will then redirect the user to this URL, prompting them to grant permission to access their Airtable data. Here’s a detailed breakdown of this method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates state, codeVerifier, and codeChallenge.&lt;/li&gt;
&lt;li&gt;Stores codeVerifier (and state) in a temporary cache.&lt;/li&gt;
&lt;li&gt;Constructs the Airtable authorization URL with all the necessary parameters, including state and code_challenge.&lt;/li&gt;
&lt;li&gt;Sends this URL back to the client.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Get authorization URL
app.get("/get-authorization-url", (req, res) =&amp;gt; {
  // prevents others from impersonating Airtable
  const state = crypto.randomBytes(100).toString("base64url");

  // prevents others from impersonating you
  const codeVerifier = crypto.randomBytes(96).toString("base64url"); // 128 characters
  const codeChallengeMethod = "S256";
  const codeChallenge = crypto
    .createHash("sha256")
    .update(codeVerifier) // hash the code verifier with the sha256 algorithm
    .digest("base64") // base64 encode, needs to be transformed to base64url
    .replace(/=/g, "") // remove =
    .replace(/\+/g, "-") // replace + with -
    .replace(/\//g, "_"); // replace / with _ now base64url encoded

  // ideally, entries in this cache expires after ~10-15 minutes
  authorizationCache[state] = {
    // we'll use this in the redirect url route
    codeVerifier,
    // any other data you want to store, like the user's ID
  };

  // build the authorization URL
  const authorizationUrl = new URL(`${airtableBaseUrl}/oauth2/v1/authorize`);
  authorizationUrl.searchParams.set("code_challenge", codeChallenge);
  authorizationUrl.searchParams.set(
    "code_challenge_method",
    codeChallengeMethod
  );
  authorizationUrl.searchParams.set("state", state);
  authorizationUrl.searchParams.set("client_id", clientId);
  authorizationUrl.searchParams.set("redirect_uri", redirectUri);
  authorizationUrl.searchParams.set("response_type", "code");
  // your OAuth integration register with these scopes in the management page
  authorizationUrl.searchParams.set("scope", scope);
  res.json({ authorizationUrl: authorizationUrl.toString() });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;get_auth_token — This method is the crucial next step in the OAuth 2.0 PKCE flow with Airtable. After a user has granted (or denied) the application permission, Airtable redirects their browser back to this endpoint.&lt;/p&gt;

&lt;p&gt;This method is responsible for validating that redirect and then exchanging the received authorization code for actual access tokens. We can also call this as the “token exchange” phase, where the temporary authorization code is swapped for long-lived access and refresh tokens.&lt;/p&gt;

&lt;p&gt;The core purpose of this method is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate the incoming redirect from Airtable using the state parameter to prevent CSRF.&lt;/li&gt;
&lt;li&gt;Handle potential errors from the authorization attempt (e.g., user denied access).&lt;/li&gt;
&lt;li&gt;Exchange the authorization_code received from Airtable for access_token and refresh_token by making a POST request to Airtable's token endpoint, securely using the code_verifier.&lt;/li&gt;
&lt;li&gt;Respond to the client with the acquired tokens or an error.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Get Auth token
app.get("/request-oauth-token", (req, res) =&amp;gt; {
  const state = req.query.state;
  const cached = authorizationCache[state];
  if (cached === undefined) {
    return res
      .status(400)
      .json({ error: "This request was not from Airtable!" });
  }
  // clear the cache
  delete authorizationCache[state];

  // Check if the redirect includes an error code.
  // Note that if your client_id and redirect_uri do not match the user will never be re-directed
  if (req.query.error) {
    const error = req.query.error;
    const errorDescription = req.query.error_description;
    return res.status(400).json({ error, errorDescription });
  }

  const code = req.query.code;
  const codeVerifier = cached.codeVerifier;

  const headers = {
    // Content-Type is always required
    "Content-Type": "application/x-www-form-urlencoded",
  };
  if (clientSecret !== "") {
    // Authorization is required if your integration has a client secret
    // omit it otherwise
    headers.Authorization = authorizationHeader;
  }

  setLatestTokenRequestState("LOADING");
  // make the POST request
  axios({
    method: "POST",
    url: `${airtableBaseUrl}/oauth2/v1/token`,
    headers,
    // stringify the request body like a URL query string
    data: qs.stringify({
      client_id: clientId,
      code_verifier: codeVerifier,
      redirect_uri: redirectUri,
      code,
      grant_type: "authorization_code",
    }),
  })
    .then((response) =&amp;gt; {
      // book-keeping so we can show you the response
      setLatestTokenRequestState("AUTHORIZATION_SUCCESS", response.data);

      res.json(response.data);
    })
    .catch((e) =&amp;gt; {
      // 400 and 401 errors mean some problem in our configuration, the user waited too
      // long to authorize, or there were multiple requests using this auth code.
      // We expect these but not other error codes during normal operations
      if (e.response &amp;amp;&amp;amp; [400, 401].includes(e.response.status)) {
        setLatestTokenRequestState("AUTHORIZATION_ERROR", e.response.data);
        res.status(e.response.status).json(e.response.data);
      } else if (e.response) {
        console.log("uh oh, something went wrong", e.response.data);
        setLatestTokenRequestState("UNKNOWN_AUTHORIZATION_ERROR");
        res
          .status(e.response.status)
          .json({ error: "Unknown error", details: e.response.data });
      } else {
        console.log("uh oh, something went wrong", e);
        setLatestTokenRequestState("UNKNOWN_AUTHORIZATION_ERROR");
        res.status(500).json({ error: "Unknown error", details: e.message });
      }
    });
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, this is how your .env should look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2rqyr4cyrd42b9gvz3j0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2rqyr4cyrd42b9gvz3j0.png" alt=".env for Airtable OAuth Backend" width="800" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now run the service by executing node server.js and you should see the server running successfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Frontend — Angular 20
&lt;/h2&gt;

&lt;p&gt;Once the service is up and running, we create a new Angular app.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng new airtable-integration-frontend&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For my angular app, I have created these basic components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Login — To display the SSO login page
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Template login.html

&amp;lt;p&amp;gt;Welcome to Airtable Integration!&amp;lt;/p&amp;gt;
&amp;lt;button (click)="loginWithAirtable()"&amp;gt;Click to Login with SSO&amp;lt;/button&amp;gt;

//Login.ts
import { Component, inject } from '@angular/core';
import { Auth } from '../auth';

@Component({
  selector: 'app-login',
  imports: [],
  templateUrl: './login.html',
  styleUrl: './login.css',
})
export class Login {
  private service = inject(Auth);
  loginWithAirtable() {
    this.service.getAuthorizationURL().subscribe((response: any) =&amp;gt; {
      window.location.href = response.authorizationUrl;
    });
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;AuthCallback — To display a message “Processing OAuth…” while the app is working on generating a token in exchange of code sent by Airtable.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Template auth-callback.html
&amp;lt;p&amp;gt;Performing OAuth...&amp;lt;/p&amp;gt;

//auth-callback.ts
import { Component, inject, OnInit } from '@angular/core';
import { Auth } from '../auth';
import { ActivatedRoute, Router } from '@angular/router';

@Component({
  selector: 'app-auth-callback',
  imports: [],
  templateUrl: './auth-callback.html',
  styleUrl: './auth-callback.css',
})
export class AuthCallback implements OnInit {
  private authService = inject(Auth);
  private router = inject(Router);
  private route = inject(ActivatedRoute);

  ngOnInit() {
    const code = this.route.snapshot.queryParamMap.get('code');
    const state = this.route.snapshot.queryParamMap.get('state');
    const code_challenge_method = this.route.snapshot.queryParamMap.get(
      'code_challenge_method'
    );
    const code_challenge =
      this.route.snapshot.queryParamMap.get('code_challenge');

    if (code &amp;amp;&amp;amp; state) {
      // Exchange code for token (if needed)
      this.authService.exchangeCodeForToken(code, state).subscribe({
        next: (data: any) =&amp;gt; {
          localStorage.clear();
          localStorage.setItem('airtable_access_token', data.access_token);
          localStorage.setItem('airtable_refresh_token', data.refresh_token);
          localStorage.setItem('airtable_expires_in', data.expires_in);
          this.router.navigate(['/login-success']);
        },
        error: () =&amp;gt; this.router.navigate(['/login-failure']),
      });
    } else {
      this.router.navigate(['/login-failure']);
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;LoginSuccess — Protected route and only accessible on successful authentication.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//login-success.html

&amp;lt;p&amp;gt;Logged In successfully! This is a route protected by AuthGuard.&amp;lt;/p&amp;gt;
&amp;lt;button (click)="logout()"&amp;gt;Logout&amp;lt;/button&amp;gt;

//login-success.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-login-success',
  imports: [],
  templateUrl: './login-success.html',
  styleUrl: './login-success.css',
})
export class LoginSuccess {
  logout() {
    localStorage.clear();
    window.location.href = '/';
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;LoginFailure — To redirect to in case of authentication failure. This is optional as you can just simply redirect to the /login page as well.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//login-failure.html
&amp;lt;p&amp;gt;Authentication Failed&amp;lt;/p&amp;gt;

//login-failure.ts

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

@Component({
  selector: 'app-login-failure',
  imports: [],
  templateUrl: './login-failure.html',
  styleUrl: './login-failure.css',
})
export class LoginFailure {}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is what the routes look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//app.routes.ts

export const routes: Routes = [
  {
    path: '',
    component: Login,
  },
  {
    path: 'auth-callback',
    component: AuthCallback,
  },
  {
    path: 'login-success',
    component: LoginSuccess,
    canActivate: [authGuard], //Protected route via AuthGuard
  },
  {
    path: 'login-failure',
    component: LoginFailure,
  },
];

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Service Integration:
&lt;/h2&gt;

&lt;p&gt;We now create a new service called auth-service and integrate our endpoints we created earlier.&lt;/p&gt;

&lt;p&gt;Create new service called auth:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng g s auth&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Create the following functions in your service.ts file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//service.ts

import { HttpClient } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class Auth {
  private http = inject(HttpClient);
  API_BASE_URL = 'http://localhost:3000';

  getAuthorizationURL() {
    return this.http.get(`${this.API_BASE_URL}/get-authorization-url`);
  }

  exchangeCodeForToken(code: string, state: string) {
    return this.http.get&amp;lt;any&amp;gt;(
      `${this.API_BASE_URL}/request-oauth-token?code=${encodeURIComponent(
        code
      )}&amp;amp;state=${encodeURIComponent(state)}`
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to create an authGuard to protect the route. For the sake of the demo, I am only checking if localstorage contains token, but you can be as creative as you want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { CanActivateFn } from '@angular/router';

export const authGuard: CanActivateFn = (route, state) =&amp;gt; {
  return localStorage.getItem('airtable_access_token') !== null;
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run the application using&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng serve --host 127.0.0.1&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;You should see the following screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxpvzbx2xivtetkn63j1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxpvzbx2xivtetkn63j1.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full application can be found at my Github.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt; &lt;a href="https://github.com/Ingila185/airtable-integration-frontend.git" rel="noopener noreferrer"&gt;https://github.com/Ingila185/airtable-integration-frontend.git&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend:&lt;/strong&gt; &lt;a href="https://github.com/Ingila185/airtable-integration-backend.git" rel="noopener noreferrer"&gt; https://github.com/Ingila185/airtable-integration-backend.git&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>angular</category>
    </item>
    <item>
      <title>Cracking the FAANG Code: My 2024 Google Interview Journey &amp; Key Takeaways (with Actionable Tips!)</title>
      <dc:creator>Ingila Ejaz</dc:creator>
      <pubDate>Fri, 06 Dec 2024 14:53:48 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/cracking-the-faang-code-my-2024-google-interview-journey-key-takeaways-with-actionable-tips-3f59</link>
      <guid>https://dev.to/playfulprogramming/cracking-the-faang-code-my-2024-google-interview-journey-key-takeaways-with-actionable-tips-3f59</guid>
      <description>&lt;p&gt;Earlier this year, I had the opportunity to interview for a role at Google. A recruiter reached out to me on LinkedIn, and after enthusiastically saying yes, I dove headfirst into preparation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Technical Assessment
&lt;/h2&gt;

&lt;p&gt;While initial anxiety set in with the vast amount of material to cover, I tackled a mix of technical and non-technical questions in a 45-minute screening.&lt;/p&gt;

&lt;p&gt;The interview commenced with standard introductions. The interviewer inquired about my professional background and past projects. Subsequently, I was presented with a series of technical questions pertaining to fundamental algorithms such as merge sort, quick sort, and binary search. The discussion extended to data structures, their optimal applications, and the associated time and space complexities of various search algorithms, including DFS and BFS. To facilitate further preparation, the interviewer provided relevant study materials and allotted a three-week timeframe for the upcoming technical interview.&lt;/p&gt;

&lt;p&gt;I was asked about my preferred coding language and was recommended to the second round. Advancing to the technical round, I honed my coding skills (focusing on my preferred language) and prepared from LeetCode, HackerRank , W3Schools.com and CodeSignal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Interview: A Deep Dive into Graphs
&lt;/h2&gt;

&lt;p&gt;The second interview was primarily focused on algorithmic problem-solving. Unlike technical interviews of other organizations where coding on an IDE is involved, this phase emphasizes clear communication of problem-solving approaches using Google docs or simple text editor. Candidates are expected to demonstrate their thought process, starting with a brute-force solution and progressing towards an optimized one.&lt;/p&gt;

&lt;p&gt;For my second interview, I was presented with a medium-level graph problem from LeetCode. I successfully implemented the brute-force solution and discussed the potential optimizations with the interviewer before the time was out.&lt;/p&gt;

&lt;p&gt;While I was not selected for the System Design round, this experience proved invaluable. It highlighted the specific areas where I needed improvement to successfully navigate FAANG/GAMAM/MAANG interviews.&lt;/p&gt;

&lt;p&gt;The following key takeaways will be beneficial for anyone preparing for FAANG/MAANG/GAMAM interviews:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Coding platforms are a stepping stone, not a silver bullet:&lt;/strong&gt;&lt;br&gt;
 While coding platforms are great for building problem-solving logic, real-world problems might require different approaches. Time management is also crucial!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Clarify, discuss, but don’t stall:&lt;/strong&gt;&lt;br&gt;
 While asking questions is encouraged, striking a balance is key. Aim to reach a brute force solution, optimize it, and calculate time/space complexity within the allotted time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Learn from others, but find your own path:&lt;/strong&gt; Don’t get stuck on coding problems. Utilize resources like YouTube videos to learn different approaches and broaden your problem-solving horizons. Google seeks your unique approach, not a memorized solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Feedback is your friend:&lt;/strong&gt; Request a short follow-up call with your recruiter to gain valuable insights into your interview performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Rejection is a chance to grow:&lt;/strong&gt; Google has a 6-month reapplication period. Use this time to refine your skills and come back stronger! I will be re-applying again in January 2025 and would document the process again!&lt;/p&gt;

&lt;p&gt;I’m especially grateful to fellow Googlers who provided invaluable support, influence and guidance throughout the process. Their encouragement helped me manage interview anxiety and motivated me to keep learning.&lt;/p&gt;

&lt;p&gt;This experience has been incredibly rewarding and most importantly, it has given me the exact picture of where I lag as a developer and I can’t wait to apply at Google with better preparation this time.&lt;/p&gt;

&lt;p&gt;Here’s my &lt;a href="https://next-js-portfolio-two-ebon.vercel.app/en" rel="noopener noreferrer"&gt;portfolio&lt;/a&gt;, &lt;a href="https://github.com/Ingila185" rel="noopener noreferrer"&gt;Github&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/ingila-ejaz/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; for anyone who would like to stay in touch and say Hi!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>devrel</category>
      <category>coding</category>
    </item>
    <item>
      <title>Angular 19 - 5 Game-Changing Features You Need to Know</title>
      <dc:creator>Ingila Ejaz</dc:creator>
      <pubDate>Wed, 06 Nov 2024 16:53:20 +0000</pubDate>
      <link>https://dev.to/playfulprogramming-angular/angular-19-5-game-changing-features-you-need-to-know-32cd</link>
      <guid>https://dev.to/playfulprogramming-angular/angular-19-5-game-changing-features-you-need-to-know-32cd</guid>
      <description>&lt;p&gt;Angular continues to evolve, bringing exciting features with each release. Angular 19 is no exception, focusing on improving developer experience (DX) and application performance. Let's dive into five key features expected in Angular 19 that will enhance your development workflow and create smoother, faster applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Partial and Incremental Hydration
&lt;/h2&gt;

&lt;p&gt;Angular's dedication to improving hydration is a welcome sight. While traditional hydration has been around, Angular 19 introduces partial hydration and incremental hydration. These features enhance DX by prioritizing the loading of critical deferred components first, leading to faster initial load times. &lt;a href="https://medium.com/@ingila185/angular-18-improving-application-performance-with-partial-hydration-and-ssr-c0d077ac4331" rel="noopener noreferrer"&gt;Learn more here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Incremental hydration takes it a step further. It allows developers to defer loading certain functionalities of deferred components based on triggers and user interaction. This means the application only sends the minimum amount of Javascript initially, with additional functionalities loading based on user actions like hovering or clicking. This approach results in a noticeably faster first impression and a smoother user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Standalone Components
&lt;/h2&gt;

&lt;p&gt;For improved code reusability and overall application performance, consider using standalone components. Prior to Angular 14, all components needed to be declared within a module. This often led to boilerplate code and unnecessary overhead. Angular 14 introduced standalone components, which encapsulate both component logic and dependencies within themselves, eliminating the need for module declaration.&lt;/p&gt;

&lt;p&gt;Angular 19 is poised to make standalone components the default option. This means that when you create a new component, it will be considered standalone by default. If you specifically want a component to be part of a module, you'll explicitly set &lt;code&gt;standalone: false&lt;/code&gt; during creation. This shift simplifies code structure and promotes reusability across different parts of your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Zoneless Change Detection
&lt;/h2&gt;

&lt;p&gt;Angular has continuously refined its change detection strategy. While &lt;code&gt;Zone.js&lt;/code&gt; provided a solid foundation in the early days, it introduced some performance overhead and increased bundle size. To address this, Angular has introduced the experimental zoneless change detection feature, activated through &lt;code&gt;provideExperimentalZonelessChangeDetection()&lt;/code&gt;. &lt;a href="https://dev.to/this-is-angular/the-evolution-of-change-detection-from-angular-2-zonejs-to-angular-18-provideexperimentalzonelesschangedetection-4f77"&gt;Read more about it here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Zoneless change detection promises substantial benefits, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Improved Performance:&lt;/strong&gt; Expect faster initial renders and smoother overall application performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smaller Bundle Sizes:&lt;/strong&gt; Reduced overhead translates to smaller application bundles, leading to faster download times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simpler Debugging:&lt;/strong&gt; Zoneless change detection simplifies the debugging process by removing the complexity associated with Zone.js.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. linkedSignal: Boosting Reactivity for a Responsive Application
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;linkedSignal&lt;/code&gt; is a new primitive designed to enhance the reactivity of Angular applications. It provides a way to create writable signals that automatically update their values based on changes in a source signal. This feature simplifies data flow and promotes a more responsive user experience. You can find more details about linkedSignal from &lt;a href="https://dev.to/this-is-angular/angular-19-introduction-to-linkedsignal-190a"&gt;this&lt;/a&gt; article.&lt;/p&gt;

&lt;p&gt;Angular 19 is expected to introduce several overloads of &lt;code&gt;linkedSignal&lt;/code&gt;, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;linkedSignal&lt;/code&gt; with Source and Computation:&lt;/strong&gt; This allows you to define a source signal and a computation function to determine the updated value of the linked signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;linkedSignal&lt;/code&gt; Shorthand Version:&lt;/strong&gt; This provides a simplified syntax for creating linked signals, making your code more concise and easier to maintain.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Resource and rxResource APIs: Streamlining Data Retrieval
&lt;/h2&gt;

&lt;p&gt;Managing asynchronous data retrieval can be cumbersome. Angular 19 introduces experimental APIs – &lt;code&gt;resource&lt;/code&gt; and &lt;code&gt;rxResource&lt;/code&gt; – designed to simplify this process. These APIs provide a unified approach for data retrieval using both &lt;code&gt;promises&lt;/code&gt; (resource) and &lt;code&gt;Observables&lt;/code&gt; (rxResource). Here's what you can expect:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource API:&lt;/strong&gt; This API offers three key properties: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;status&lt;/strong&gt;: Indicates the current state of the resource (loading, success, error).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;value&lt;/strong&gt;: Holds the retrieved data upon successful completion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;error&lt;/strong&gt;: Provides an error handler for potential issues during data retrieval.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;rxResource API:&lt;/strong&gt; This API utilizes &lt;code&gt;Observables&lt;/code&gt; to manage asynchronous data retrieval. It simplifies the handling of data streams, making it easier to control data flow and handle errors.&lt;br&gt;
Both resource and &lt;code&gt;rxResource&lt;/code&gt; APIs aim to improve the way developers interact with asynchronous data within Angular applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/this-is-angular/angular-19-streamlining-data-retrieval-with-resource-and-rxresource-apis-3lb2"&gt;Here&lt;/a&gt; you can find more information about &lt;code&gt;resource&lt;/code&gt; and &lt;code&gt;rxResource&lt;/code&gt; API.&lt;/p&gt;

&lt;p&gt;These are just a few of the exciting features expected in Angular 19. With its focus on DX and performance, Angular 19 promises to streamline development workflows and create faster, more responsive web applications. Stay tuned for the official release to experience these advancements firsthand!&lt;/p&gt;

&lt;p&gt;If you liked this article, feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/ingila-ejaz/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://github.com/Ingila185" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, and maybe see my &lt;a href="https://next-js-portfolio-two-ebon.vercel.app/en/" rel="noopener noreferrer"&gt;portfolio here&lt;/a&gt; :) I'd love to connect!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Angular 19 - Streamlining Data Retrieval with Experimental Resource and rxResource APIs</title>
      <dc:creator>Ingila Ejaz</dc:creator>
      <pubDate>Wed, 06 Nov 2024 04:41:29 +0000</pubDate>
      <link>https://dev.to/playfulprogramming-angular/angular-19-streamlining-data-retrieval-with-resource-and-rxresource-apis-3lb2</link>
      <guid>https://dev.to/playfulprogramming-angular/angular-19-streamlining-data-retrieval-with-resource-and-rxresource-apis-3lb2</guid>
      <description>&lt;p&gt;Angular 19 introduces two exciting experimental APIs, &lt;code&gt;resource&lt;/code&gt; and &lt;code&gt;rxResource&lt;/code&gt;, designed to simplify asynchronous data retrieval and management. This article explores these APIs, diving into their functionalities and showcasing how they enhance developer experience (DX) for crafting reactive and efficient Angular applications. All API endpoints used in the article are from &lt;a href="https://jsonplaceholder.typicode.com/" rel="noopener noreferrer"&gt;JSON Placeholder&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Born from a Vision: Asynchronous Data Loading with &lt;code&gt;Signals&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The idea behind these APIs originated from a pull request by &lt;a href="https://github.com/angular/angular/pull/58255" rel="noopener noreferrer"&gt;Alex Rickabaugh&lt;/a&gt;. The core concept lies in utilizing Signals to manage the asynchronous loading of resources. While &lt;code&gt;resource&lt;/code&gt; utilizes Promises, &lt;code&gt;rxResource&lt;/code&gt; leverages Observables, catering to different developer preferences. Both APIs provide a &lt;code&gt;WritableResource&lt;/code&gt; object, allowing you to update resource data locally.&lt;/p&gt;

&lt;p&gt;A resource offers several signals to keep you informed about its state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Value&lt;/strong&gt;: Provides the current data of the resource, representing the result of a query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Status&lt;/strong&gt;: Reflects the resource's current state. Here's a breakdown of the various status types:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb0iu5xam4nimamoqxhbt.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb0iu5xam4nimamoqxhbt.PNG" alt="ResourceStatus in Angular 19" width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Error: Provides details on errors encountered during data retrieval.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating a Resource
&lt;/h2&gt;

&lt;p&gt;Instantiating a resource is simple:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjpqbrrgk4fckqq9hgb10.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjpqbrrgk4fckqq9hgb10.png" alt="Creating a resource Angular 19" width="800" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will result the following output. Notice how initially the status is "Loading" (2) and eventually it becomes "Resolved" (4).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnforghh3xbwbc4xlewin.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnforghh3xbwbc4xlewin.png" alt="Resource simple output" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating Resource Data Locally
&lt;/h2&gt;

&lt;p&gt;To update a resource's data locally, leverage the &lt;code&gt;update()&lt;/code&gt; method of the &lt;code&gt;value&lt;/code&gt; signal. See the following template and component for reference:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdxby2poiu0maww1lts3t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdxby2poiu0maww1lts3t.png" alt="Update resource data locally" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;updateResource()&lt;/code&gt; function will update the value of resource locally with a different string.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fffwz1w3be6e3ogedatbd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fffwz1w3be6e3ogedatbd.png" alt="Update Resource Data locally" width="800" height="787"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will produce the following output. Notice the status being "Local" (5) as it's value has been updated locally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gbzzt9ff6glznoqje7t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gbzzt9ff6glznoqje7t.png" alt="Update resource data locally output" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Refreshing a Resource
&lt;/h2&gt;

&lt;p&gt;Let's create a Refresh button in our template and refresh a resource when the user clicks it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdurirb1cxm7e9cjwvbw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdurirb1cxm7e9cjwvbw.png" alt="Refreshing a resource" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;reload&lt;/code&gt; function in the code below triggers the &lt;code&gt;resource&lt;/code&gt; loader to execute again. If user clicks Refresh button multiple times, the loader will be triggered only once until the previous request is finished. It is similar to &lt;code&gt;exhaustMap&lt;/code&gt; in &lt;code&gt;Rxjs&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqj4vaki3l7qgzx3t55s9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqj4vaki3l7qgzx3t55s9.png" alt="Refresh Resource in Angular 19" width="800" height="888"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice the status transitioning from "Reloading" (3) to "Resolved" (4) in the output below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgnjtyg3lk4pm2l2cmp2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgnjtyg3lk4pm2l2cmp2.png" alt="Refresh Resource Output in Angular 19" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Based on Signals: Dynamic Resource Loading
&lt;/h2&gt;

&lt;p&gt;Suppose you want to fetch posts based on an &lt;code&gt;postId&lt;/code&gt; signal. You can achieve this by passing the signal as a request parameter to your endpoint:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbr4fot3p8fzibohl1zd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbr4fot3p8fzibohl1zd.png" alt="Load data with signals template" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By passing the signal &lt;code&gt;postId&lt;/code&gt; as a request parameter, you can achieve dynamic data retrieval based on the &lt;code&gt;postId&lt;/code&gt; value. Here's an example: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fliuawe7abq66t798c1vs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fliuawe7abq66t798c1vs.png" alt="Load data with signals component" width="800" height="633"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will result in the following output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0rvndju34poitux8nol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0rvndju34poitux8nol.png" alt="Load data with Signals output" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While this approach works for initial data fetching, it lacks reactivity. &lt;strong&gt;Loaders in Angular's resource API are inherently untracked.&lt;/strong&gt; This means that if a signal like &lt;code&gt;postId&lt;/code&gt; changes after the initial resource creation, the loader won't automatically re-execute. &lt;/p&gt;

&lt;p&gt;To overcome this limitation and ensure reactive behavior, we need to explicitly bind the signal to the resource's &lt;code&gt;request&lt;/code&gt; parameter. This establishes a dependency between the &lt;code&gt;resource&lt;/code&gt; and the &lt;code&gt;signal&lt;/code&gt;, ensuring that the loader is triggered whenever the signal's value changes.&lt;/p&gt;

&lt;p&gt;Let's create a button to update the signal &lt;code&gt;postId&lt;/code&gt; to a random number. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l28md53sudsjkgp98rl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l28md53sudsjkgp98rl.png" alt="Signal change template" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, in the component, we add a method to update the signal postId to a random number. We also bind &lt;code&gt;postId&lt;/code&gt; to the &lt;code&gt;request&lt;/code&gt; parameter of our resource to ensure reactivity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83kmhtz6s0zymwl655bv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83kmhtz6s0zymwl655bv.png" alt="Signal Change Component" width="800" height="746"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Local Data Changes During Active Requests
&lt;/h2&gt;

&lt;p&gt;When a local data change occurs while a resource is fetching data from a remote source, a potential race condition arises. To mitigate this, we can leverage the abortSignal() function to gracefully handle concurrent requests.&lt;/p&gt;

&lt;p&gt;By providing an AbortSignal object to the resource's loader function, we can cancel ongoing requests if the signal is aborted. This is particularly useful when a new request is triggered before the previous one completes.&lt;/p&gt;

&lt;p&gt;Here's a breakdown of the process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local Data Change:&lt;/strong&gt; A user modifies data locally, triggering a new request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abort Signal:&lt;/strong&gt; An AbortSignal is created and passed to the resource's loader.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Cancellation:&lt;/strong&gt; If the previous request is still in progress, it's canceled using the AbortSignal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New Request Initiation:&lt;/strong&gt; The loader is invoked with the updated postId and the new AbortSignal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Fetching and Update:&lt;/strong&gt; The new request proceeds, and the resource's value is updated with the fetched data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's an example which will fetch data based on new value of signal and cancel the previous request in progress in case of multiple triggers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqovj992up6dpwroolbda.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqovj992up6dpwroolbda.png" alt="Abort Signal Component" width="800" height="780"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple Signal Dependencies: Reactive Resource Loading
&lt;/h2&gt;

&lt;p&gt;A resource can be made reactive to changes in multiple signals, allowing for complex data fetching scenarios. By binding multiple signals to the resource's request parameter, the loader will be triggered whenever any of the dependent signals change.&lt;/p&gt;

&lt;p&gt;Here's an example demonstrating this behavior where both &lt;code&gt;postId&lt;/code&gt; and &lt;code&gt;userId&lt;/code&gt; are being set by a random number and the resource is made reactive to changes in both the signals:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9lbkh7lqra6p2mrav7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9lbkh7lqra6p2mrav7l.png" alt="Multiple Dependenies in resources Angular 19" width="800" height="730"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the above example, the loader will be re-executed whenever either the &lt;code&gt;userId&lt;/code&gt; or &lt;code&gt;postId&lt;/code&gt; signal changes. This ensures that the resource always reflects the latest data based on the current values of its dependent signals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Reusability with Resource Functions
&lt;/h2&gt;

&lt;p&gt;To enhance code maintainability and promote a modular approach, consider creating reusable resource functions. These functions encapsulate the logic for creating resources with specific configurations, making them easily shareable across your application.&lt;/p&gt;

&lt;p&gt;Here's an example of a reusable resource function:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fruq0qve8j1p1bnqvksh1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fruq0qve8j1p1bnqvksh1.png" alt="Reusable resources in Angular 19" width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the example above, &lt;code&gt;myResource&lt;/code&gt; can be used across different areas of your application, ensuring clean code and reusability.&lt;/p&gt;

&lt;h2&gt;
  
  
  RxResource: Leveraging Observables for Reactive Data Fetching
&lt;/h2&gt;

&lt;p&gt;When working with Observables in your Angular application, the rxResource API provides a powerful mechanism for managing asynchronous data operations. Similar to the resource API, rxResource allows you to define resources that fetch data and emit it as an Observable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Differences from resource:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Observable-Based:&lt;/strong&gt; &lt;code&gt;rxResource&lt;/code&gt; leverages &lt;code&gt;Observables&lt;/code&gt; to provide a stream of data, enabling more flexible and reactive data handling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No abortSignal:&lt;/strong&gt; Since &lt;code&gt;Observables&lt;/code&gt; can be easily unsubscribed, there's no need for an explicit &lt;code&gt;abortSignal&lt;/code&gt; to cancel requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First-Value-Only:&lt;/strong&gt; The current implementation of rxResource only considers the first emitted value from the Observable. Subsequent emissions are ignored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writable Resources:&lt;/strong&gt; Like resource, &lt;code&gt;rxResource&lt;/code&gt; allows you to &lt;code&gt;update&lt;/code&gt; the local state of a resource using &lt;code&gt;Observables&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's an example of a resource created using rxResource:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpb8aqz4f5okr9o7hnbd7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpb8aqz4f5okr9o7hnbd7.png" alt="rxResource - Observables based resource API in Angular 19" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, the loader will emit the posts as an &lt;code&gt;Observable&lt;/code&gt;. You can subscribe to this Observable to react to data changes and perform necessary actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Angular's resource and rxResource APIs represent a significant step forward in simplifying asynchronous data operations. These APIs offer a declarative and concise approach to fetching and managing data, enhancing developer productivity and application performance.&lt;/p&gt;

&lt;p&gt;While still in developer preview, these APIs hold the promise of revolutionizing the way Angular developers handle data retrieval. By leveraging Signals and Observables, these APIs provide a flexible and efficient mechanism for managing data flow and reactivity in Angular applications.&lt;/p&gt;

&lt;p&gt;Github PR: &lt;a href="https://github.com/angular/angular/pull/58255" rel="noopener noreferrer"&gt;https://github.com/angular/angular/pull/58255&lt;/a&gt;&lt;br&gt;
Code repository: &lt;a href="https://github.com/Ingila185/angular-resource-demo" rel="noopener noreferrer"&gt;https://github.com/Ingila185/angular-resource-demo&lt;/a&gt;&lt;br&gt;
Stackblitz Playground: &lt;a href="https://stackblitz.com/edit/stackblitz-starters-hamcfa?file=src%2Fmain.ts" rel="noopener noreferrer"&gt;https://stackblitz.com/edit/stackblitz-starters-hamcfa?file=src%2Fmain.ts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Credits to Enea Jahollari for writing such a detailed &lt;a href="https://push-based.io/article/everything-you-need-to-know-about-the-resource-api" rel="noopener noreferrer"&gt;article&lt;/a&gt; about resource and rxResource on Push Based.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>angular</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Angular 19 - Introduction to linkedSignal</title>
      <dc:creator>Ingila Ejaz</dc:creator>
      <pubDate>Sun, 03 Nov 2024 08:27:13 +0000</pubDate>
      <link>https://dev.to/playfulprogramming-angular/angular-19-introduction-to-linkedsignal-190a</link>
      <guid>https://dev.to/playfulprogramming-angular/angular-19-introduction-to-linkedsignal-190a</guid>
      <description>&lt;p&gt;Angular 19 is on the horizon, and it’s bringing a host of exciting new features to the table. One of the most notable additions is the linkedSignal primitive, which promises to revolutionize the way we handle reactive programming in Angular applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Reset Patterns
&lt;/h2&gt;

&lt;p&gt;Traditionally, implementing reset patterns in Angular involved using &lt;code&gt;computed()&lt;/code&gt; signals. While effective, this approach has limitations. When you need to set the value of a signal explicitly, it becomes a read-only signal, hindering flexibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  The linkedSignal Solution
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;linkedSignal&lt;/code&gt; addresses this limitation by providing a writable signal that automatically updates its value based on changes to a source signal. This enables us to create a seamless synchronization between the two, ensuring a glitch-free user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding linkedSignal
&lt;/h2&gt;

&lt;p&gt;While &lt;code&gt;linkedSignal&lt;/code&gt; will have multiple overloads, two of them are worth mentioning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;linkedSignal with Source and Computation&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This overload allows you to create a linkedSignal that computes its value based on the value of a source signal. Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { signal, linkedSignal } from '@angular/core';

  const sourceSignal = signal(0);
  const linkedSignal = linkedSignal({
    source: this.sourceSignal,
    computation: () =&amp;gt; this.sourceSignal() * 5,
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;linkedSignal&lt;/code&gt; will always be twice the value of &lt;code&gt;sourceSignal&lt;/code&gt;. Whenever sourceSignal changes, &lt;code&gt;linkedSignal&lt;/code&gt; will automatically recompute its value. Here’s a more real-world example of linkedSignal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxs8jzhyrq47nvj3m1c0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxs8jzhyrq47nvj3m1c0.png" alt="Angular 19 linkedSignal Primitive"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;CourseDetailComponent&lt;/code&gt; component accepts a &lt;code&gt;courseId&lt;/code&gt; as input and displays the number of enrolled students. We aim to reset the student count whenever the selected &lt;code&gt;courseId&lt;/code&gt; changes. This necessitates a mechanism to synchronize two signals: the &lt;code&gt;courseId&lt;/code&gt; and the studentCount.&lt;/p&gt;

&lt;p&gt;While the usage of &lt;code&gt;computed()&lt;/code&gt; can be effective in deriving values from other signals, they are read-only. To dynamically update the studentCount based on changes in the &lt;code&gt;courseId&lt;/code&gt;, we leverage the &lt;code&gt;linkedSignal&lt;/code&gt; primitive. By creating a writable signal linked to the &lt;code&gt;courseId&lt;/code&gt;, we can both set the studentCount explicitly and automatically update it whenever the &lt;code&gt;courseId&lt;/code&gt; changes. This approach provides a robust and flexible solution for managing signal dependencies and ensuring data consistency.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;linkedSignal&lt;/code&gt; Shorthand&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For simpler scenarios, you can use a shorthand syntax to create &lt;code&gt;linkedSignal&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const sourceSignal = signal(10);
const linkedSignal = linkedSignal(() =&amp;gt; sourceSignal() * 2);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shorthand syntax is equivalent to the first overload, but it’s more concise and easier to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Benefits of LinkedSignals
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Reset Patterns:&lt;/strong&gt; Easily implement reset patterns without the complexities of computed() signals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Flexibility:&lt;/strong&gt; Maintain the ability to set signal values explicitly while ensuring automatic updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Performance:&lt;/strong&gt; Optimized under the hood for efficient updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleaner Code:&lt;/strong&gt; More concise and readable code, especially for complex reactive scenarios.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;linkedSignal&lt;/code&gt; is a powerful new tool in Angular's reactive toolkit. By understanding its core concepts and usage patterns, you can create more robust, responsive, and user-friendly Angular applications. With its ability to combine the best aspects of &lt;code&gt;computed()&lt;/code&gt; and writable signals, &lt;code&gt;linkedSignal&lt;/code&gt; is poised to become an indispensable tool for Angular developers. You can learn more about &lt;code&gt;linkedSignals&lt;/code&gt; &lt;a href="https://stackblitz.com/edit/stackblitz-starters-ejsbos?file=src%2Fmain.ts" rel="noopener noreferrer"&gt;from this stackblitz&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Github PR: &lt;a href="https://github.com/angular/angular/pull/58189" rel="noopener noreferrer"&gt;https://github.com/angular/angular/pull/58189&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Core Web Vitals — Now Built-n to Chrome</title>
      <dc:creator>Ingila Ejaz</dc:creator>
      <pubDate>Fri, 18 Oct 2024 05:32:02 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/core-web-vitals-now-built-n-to-chrome-1bdh</link>
      <guid>https://dev.to/playfulprogramming/core-web-vitals-now-built-n-to-chrome-1bdh</guid>
      <description>&lt;p&gt;Have you wondered what these numbers in the DevTools are?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fin4r78y6ns4jknc1jxt1.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fin4r78y6ns4jknc1jxt1.PNG" alt="Core Web Vitals (CWV) introduced in Chrome&amp;lt;br&amp;gt;
"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Earlier this year, Google introduced an in-browser preview of Core Web Vitals in the DevTools. This article dives deep into what Core Web Vitals (CWV) are, why they matter and how can we improve them.&lt;/p&gt;

&lt;p&gt;Core Web Vitals (CWV) are a new addition to the &lt;a href="https://developer.chrome.com/docs/devtools/performance/overview" rel="noopener noreferrer"&gt;performance&lt;/a&gt; tab of Google Chrome. CWV are a set of metrics that Google uses to assess the overall user experience of a web page. They focus on three key aspects of performance:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://web.dev/articles/lcp" rel="noopener noreferrer"&gt;Largest Contentful Paint (LCP)&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;LCP is the measure of how quickly the web page loads. A fast LCP indicates that the user can quickly start interacting with the most important content on the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9kj15qkzeo3fc5h4wc0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9kj15qkzeo3fc5h4wc0.png" alt="Good LCP Values"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://web.dev/articles/optimize-lcp" rel="noopener noreferrer"&gt;docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To provide a good user experience, sites should strive to have an LCP of 2.5 seconds or less for at least 75% of page visits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Best Practices to optimize LCP:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optimize images and reduce their file size.&lt;/li&gt;
&lt;li&gt;Minimize render-blocking resources.&lt;/li&gt;
&lt;li&gt;Leverage browser caching.&lt;/li&gt;
&lt;li&gt;Improve server response times.&lt;/li&gt;
&lt;li&gt;Use a CDN.&lt;/li&gt;
&lt;li&gt;Learn more &lt;a href="https://web.dev/articles/optimize-lcp" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://web.dev/articles/inp" rel="noopener noreferrer"&gt;Interactive Paint (INP)&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;INP is the measure of the responsiveness of the page during interactions like clicking buttons or typing in forms. A fast INP indicates that the page is responsive and doesn’t feel sluggish or unresponsive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1qcxqh706yho0qmuumm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1qcxqh706yho0qmuumm.png" alt="Good INP Values"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://web.dev/articles/optimize-inp" rel="noopener noreferrer"&gt;docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To provide a good user experience, websites should strive to have an Interaction to Next Paint of 200 milliseconds or less.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Best Practices to Optimize INP:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimize JavaScript execution time.&lt;/li&gt;
&lt;li&gt;Avoid long tasks that block the main thread.&lt;/li&gt;
&lt;li&gt;Prioritize critical tasks and defer non-critical ones.&lt;/li&gt;
&lt;li&gt;Optimize event handling and input processing.&lt;/li&gt;
&lt;li&gt;Learn more &lt;a href="https://web.dev/articles/optimize-inp" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://web.dev/articles/cls" rel="noopener noreferrer"&gt;Cumulative Layout Shift (CLS)&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;CLS is the measure of the visual stability of the page, assessing how often elements shift unexpectedly after the page has loaded. An example of unexpected layouts can be found &lt;a href="https://web.dev/static/articles/cls/video/web-dev-assets/layout-instability-api/layout-instability2.webm" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj074bkkg2of05j1ufddd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj074bkkg2of05j1ufddd.png" alt="Good CLS Values"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A low CLS indicates that the page is visually stable and doesn’t suffer from unexpected layout shifts that can disrupt the user experience. According to the &lt;a href="https://web.dev/articles/cls" rel="noopener noreferrer"&gt;docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To provide a good user experience, sites should strive to have a CLS score of 0.1 or less.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Best Practices to optimize CLS:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reserve space for dynamic content.&lt;/li&gt;
&lt;li&gt;Avoid images without specified dimensions.&lt;/li&gt;
&lt;li&gt;Use font-display: swap for fonts.&lt;/li&gt;
&lt;li&gt;Minimize JavaScript-triggered layout shifts.&lt;/li&gt;
&lt;li&gt;Learn more &lt;a href="https://web.dev/articles/optimize-cls" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why are Core Web Vitals important?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;User Experience:&lt;/strong&gt; A good user experience is crucial for website success. Core Web Vitals directly impact how users perceive a website’s performance and usability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Search Engine Ranking:&lt;/strong&gt; Google has indicated that Core Web Vitals are a ranking factor. Websites with poor Core Web Vital scores may rank lower in search results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conversion Rates:&lt;/strong&gt; A fast and stable website is more likely to convert visitors into customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion:
&lt;/h2&gt;

&lt;p&gt;By focusing on improving Core Web Vitals, you can create a better user experience, improve your website’s search engine ranking, and increase your website’s conversion rates.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Improving Core Web Vitals including LCP and CLS with Partial Hydration in Angular 18</title>
      <dc:creator>Ingila Ejaz</dc:creator>
      <pubDate>Sat, 05 Oct 2024 13:56:01 +0000</pubDate>
      <link>https://dev.to/playfulprogramming-angular/angular-18-improving-application-performance-with-partial-hydration-and-ssr-2nie</link>
      <guid>https://dev.to/playfulprogramming-angular/angular-18-improving-application-performance-with-partial-hydration-and-ssr-2nie</guid>
      <description>&lt;p&gt;Angular 18 introduced Partial Hydration in &lt;a href="https://www.angulartraining.com/daily-newsletter/recap-of-ng-conf-2024/" rel="noopener noreferrer"&gt;ng-conf 2024&lt;/a&gt;, a powerful technique that significantly improves application performance in conjunction with Server-Side Rendering (SSR). This article dives into the concept of partial hydration, its benefits, and how it leverages deferrable views introduced in &lt;a href="https://v17.angular.io/docs" rel="noopener noreferrer"&gt;Angular 17&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://angular.dev/roadmap" rel="noopener noreferrer"&gt;Angular Roadmap:&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We’re already seeing significant improvements to Core Web Vitals, including LCP and CLS. In lab tests, we consistently observed 45% better LCP of a real-world app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why Partial Hydration and SSR Matter
&lt;/h2&gt;

&lt;p&gt;Traditional Angular applications often suffer from a performance bottleneck when loading all JavaScript upfront. This can significantly impact the initial load time, especially for large and performance-critical applications. By strategically reducing the amount of JavaScript loaded at the start, we can drastically enhance user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Partial Hydration: A Smarter Approach to SSR
&lt;/h2&gt;

&lt;p&gt;Partial hydration builds upon the foundation of &lt;a href="https://next.angular.dev/guide/defer" rel="noopener noreferrer"&gt;deferrable views&lt;/a&gt;, introduced in Angular 17. Instead of rendering a simple placeholder on the server, Angular can now render the main content of a designated block marked with &lt;a class="mentioned-user" href="https://dev.to/defer"&gt;@defer&lt;/a&gt;. Here's how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Server-side Rendering:&lt;/strong&gt; The server renders the essential content of the application along with the &lt;a class="mentioned-user" href="https://dev.to/defer"&gt;@defer&lt;/a&gt; block containing the component.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-side Hydration:&lt;/strong&gt; When the application runs on the client, Angular downloads the necessary JavaScript for the deferred component.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selective Activation:&lt;/strong&gt; The deferred component only becomes interactive when it meets specific conditions, like entering the user's viewport.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach offers several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster Initial Load Times&lt;/strong&gt;: By deferring unnecessary JavaScript, users experience a quicker initial page load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Perception:&lt;/strong&gt; The application feels more responsive as core functionalities are available instantly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Data Consumption:&lt;/strong&gt; Smaller initial JavaScript payloads translate to lower data usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Enabling Partial Hydration
&lt;/h2&gt;

&lt;p&gt;Utilizing partial hydration is going to be straightforward as mentioned in &lt;a href="https://blog.angular.dev/angular-v18-is-now-available-e79d5ac0affe" rel="noopener noreferrer"&gt;the Angular blog&lt;/a&gt;. Here's a potential example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  @defer (render on server; on viewport) {
    &amp;lt;my-deferrable-component&amp;gt;&amp;lt;/my-deferrable-component&amp;gt;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;my-deferrable-component&lt;/code&gt; is rendered on the server.&lt;/li&gt;
&lt;li&gt;Client-side, Angular downloads the required JavaScript for the component.&lt;/li&gt;
&lt;li&gt;Interaction with &lt;code&gt;my-deferrable-component&lt;/code&gt; only occurs when it enters the viewport, optimizing rendering and performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Partial hydration is one of the most requested features of Angular that empowers Angular developers to create performant and user-friendly applications. By strategically deferring component hydration based on user interaction or visibility,  Angular 18 ensures a smooth and responsive user experience, especially for complex and data-heavy applications.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Angular Change Detection from zone.js to Experimental Zoneless</title>
      <dc:creator>Ingila Ejaz</dc:creator>
      <pubDate>Thu, 19 Sep 2024 09:07:16 +0000</pubDate>
      <link>https://dev.to/playfulprogramming-angular/the-evolution-of-change-detection-from-angular-2-zonejs-to-angular-18-provideexperimentalzonelesschangedetection-4f77</link>
      <guid>https://dev.to/playfulprogramming-angular/the-evolution-of-change-detection-from-angular-2-zonejs-to-angular-18-provideexperimentalzonelesschangedetection-4f77</guid>
      <description>&lt;p&gt;Change detection is a fundamental aspect of Angular, responsible for identifying and updating parts of the DOM that have changed in response to data modifications or user interactions. This process ensures that the UI remains consistent with the underlying data, enhancing user experience and application performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Role of Zone.js
&lt;/h2&gt;

&lt;p&gt;Historically, Angular has relied on Zone.js for its change detection mechanism. Zone.js is a JavaScript library that intercepts asynchronous operations, allowing Angular to monitor changes and trigger updates accordingly. However, the inclusion of Zone.js can add overhead to the application, particularly in scenarios with frequent asynchronous activities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change Detection Strategies
&lt;/h2&gt;

&lt;p&gt;Angular provides two primary change detection strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Default:&lt;/strong&gt; Change detection is triggered after every lifecycle hook, such as &lt;code&gt;ngOnInit&lt;/code&gt; or &lt;code&gt;ngAfterViewInit&lt;/code&gt;. This strategy is straightforward but can lead to unnecessary DOM updates, especially in large applications.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  selector: 'app-my-component',
  template: `
    &amp;lt;p&amp;gt;{{ message }}&amp;lt;/p&amp;gt;
  `
})
export class MyComponent {
  message = 'Hello, world!';
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This strategy is easier to implement, as Angular handles most of the change detection logic automatically. &lt;br&gt;
The biggest challenge with this change detection strategy was that it led to unnecessary DOM updates which gets critical for large applications. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OnPush:&lt;/strong&gt; Change detection is triggered only when input properties or asynchronous observables change. This strategy is more performant for complex components with frequent data updates but requires more manual management.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  selector: 'app-my-component',
  template: `
    &amp;lt;p&amp;gt;{{ message }}&amp;lt;/p&amp;gt;
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyComponent {
  message = 'Hello, world!';
} 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;While &lt;code&gt;onPush&lt;/code&gt; change detection strategy minimized unnecessary DOM manipulations, the biggest challenge with &lt;code&gt;onPush&lt;/code&gt; change strategy was that the developers had to do more manual management and trigger changes manually. They had to handle change detection explicitly with &lt;code&gt;changeDetectorRef.detectChanges()&lt;/code&gt;  in certain scenarios, such as when data changes indirectly or when using mutable objects.&lt;/p&gt;
&lt;h2&gt;
  
  
  Angular 18 and Hybrid Change Detection: A Zone-less Approach
&lt;/h2&gt;

&lt;p&gt;To address the challenges associated with &lt;code&gt;Zone.js&lt;/code&gt;, Angular 18 introduced an experimental feature known as Hybrid Change Detection. This approach aims to eliminate &lt;code&gt;Zone.js&lt;/code&gt; entirely, using a new change detection mechanism to detect changes and manipulate the DOM.&lt;/p&gt;
&lt;h2&gt;
  
  
  Enabling Hybrid Change Detection:
&lt;/h2&gt;

&lt;p&gt;To enable Hybrid Change Detection, you can use the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bootstrapApplication(RootCmp,
{ providers: [provideExperimentalZonelessChangeDetection()] 
}
);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will trigger change detection in the following scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A signal is updated.&lt;/li&gt;
&lt;li&gt;changeDetectorRef.markForCheck() is called.&lt;/li&gt;
&lt;li&gt;An observable subscribed with the AsyncPipe receives a new value.&lt;/li&gt;
&lt;li&gt;A component gets attached or detached.&lt;/li&gt;
&lt;li&gt;An input is set.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once Hybrid Change Detection is enabled, you can safely remove Zone.js from your application's polyfills.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; "polyfills": [
              "zone.js" //remove this line
            ],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Benefits of Hybrid Change Detection
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved performance:&lt;/strong&gt; Eliminating Zone.js reduces overhead, leading to better performance, especially in applications with numerous asynchronous operations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhanced developer experience:&lt;/strong&gt; The removal of manual change detection simplifies development and reduces the likelihood of errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Smaller application size:&lt;/strong&gt; Zone.js typically adds around 13KB to the application size. Removing it can lead to a smaller bundle.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Monorepos with Nx: Why You Might Want a Single Home for All Your Code</title>
      <dc:creator>Ingila Ejaz</dc:creator>
      <pubDate>Wed, 11 Sep 2024 17:33:13 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/monorepos-with-nx-why-you-might-want-a-single-home-for-all-your-code-2hka</link>
      <guid>https://dev.to/playfulprogramming/monorepos-with-nx-why-you-might-want-a-single-home-for-all-your-code-2hka</guid>
      <description>&lt;p&gt;Ever felt like your codebase is scattered across a million different repositories? That’s the life of a polyrepo, the traditional way of managing software development. But what if there was a better way? Enter the monorepo, a single giant repository housing all your projects and libraries.&lt;/p&gt;

&lt;p&gt;This article dives into the world of monorepos, explores their benefits and drawbacks, and introduces NX, a powerful tool built specifically for managing these code havens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monorepo vs Polyrepo: Collaboration vs. Isolation
&lt;/h2&gt;

&lt;p&gt;Imagine a world where each development team has its own code kingdom — a separate repository for every project. This is the essence of a polyrepo. While it offers autonomy (each team makes its own decisions), it can lead to isolation. Changes in one project might break another, simply because they haven’t been tested together.&lt;/p&gt;

&lt;p&gt;Monorepos flip this script. They bring all your code under one roof, fostering collaboration and ensuring everyone’s on the same page (literally, in the same repository). But wait, doesn’t that sound like a tangled mess? Not quite. Monorepos allow for well-defined relationships between projects, keeping things organized even within the one big codebase.&lt;/p&gt;

&lt;p&gt;But before you jump ship to monorepo land, remember — it’s not for everyone. It works best for organizations with a shared codebase and a strong focus on collaboration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monorepo Myth Busting: It’s Not a Monolith!
&lt;/h2&gt;

&lt;p&gt;Don’t confuse monorepos with monoliths. Monoliths are giant, tightly coupled applications where everything’s intertwined. Monorepos, on the other hand, can house independent, loosely coupled projects. Think of it as a library full of books — each book is a project, but they’re all neatly organized on the same shelves (the monorepo).&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter NX: Your Monorepo Management Superhero
&lt;/h2&gt;

&lt;p&gt;So, you’ve decided to explore the monorepo world. Here’s where NX comes in. It’s a build system specifically designed for the unique challenges of managing these large-scale codebases. NX boasts a toolbox of features to keep your monorepo running smoothly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parallelize Your Workflow: Speed things up by running tasks simultaneously. No more waiting for one build to finish before starting another. &lt;a href="https://nx.dev/features/run-tasks" rel="noopener noreferrer"&gt;Learn more here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI Powerhouse:&lt;/strong&gt; Improve your continuous integration performance by &lt;a href="https://nx.dev/ci/features/distribute-task-execution" rel="noopener noreferrer"&gt;distributed task execution&lt;/a&gt; across multiple virtual machines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Like a Boss:&lt;/strong&gt; Avoid unnecessary rebuilds with &lt;a href="https://nx.dev/features/cache-task-results" rel="noopener noreferrer"&gt;local&lt;/a&gt; and &lt;a href="https://nx.dev/ci/features/remote-cache" rel="noopener noreferrer"&gt;remote&lt;/a&gt; caching. Only rebuild what needs rebuilding, saving precious time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tame the Test Beast:&lt;/strong&gt; &lt;a href="https://nx.dev/ci/features/split-e2e-tasks" rel="noopener noreferrer"&gt;Split large end-to-end tests&lt;/a&gt; across VMs to identify and re-run flaky tests more efficiently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugin Power:&lt;/strong&gt; &lt;a href="https://nx.dev/concepts/nx-plugins" rel="noopener noreferrer"&gt;NX plugins&lt;/a&gt; extend its functionality. &lt;a href="https://nx.dev/features/generate-code" rel="noopener noreferrer"&gt;Generate code&lt;/a&gt;, &lt;a href="https://nx.dev/features/automate-updating-dependencies" rel="noopener noreferrer"&gt;automate dependency upgrades&lt;/a&gt;, and enforce best practices across your organization.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Final Verdict: Monorepos with NX — A Powerful Duo
&lt;/h2&gt;

&lt;p&gt;Monorepos offer a compelling alternative to the traditional polyrepo approach, fostering collaboration and reducing the risk of breaking changes. NX, with its suite of management tools, empowers you to take full advantage of the monorepo model.&lt;/p&gt;

&lt;p&gt;However, remember — monorepos aren’t a one-size-fits-all solution. Consider your team structure and development style before making the switch. But if collaboration and streamlined workflows are your goals, a monorepo managed by NX might just be the key to unlocking a new level of development efficiency.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>opensource</category>
      <category>git</category>
    </item>
    <item>
      <title>Leveraging Django 5.1.1 and PostgreSQL 16 for an Efficient Geo-Targeted Rating API</title>
      <dc:creator>Ingila Ejaz</dc:creator>
      <pubDate>Tue, 03 Sep 2024 17:21:14 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/leveraging-django-511-and-postgresql-16-for-an-efficient-geo-targeted-rating-api-58hf</link>
      <guid>https://dev.to/playfulprogramming/leveraging-django-511-and-postgresql-16-for-an-efficient-geo-targeted-rating-api-58hf</guid>
      <description>&lt;p&gt;Last week, I had a chance to dive into a case study that involved developing an HTTP-based REST API. This API's core functionality was to calculate the average rating between designated geographical locations. The locations encompassed regions, ports within those regions, and the API facilitated retrieving ratings across various combinations: port-to-port, region-to-region, port-to-region, and region-to-port.&lt;/p&gt;

&lt;p&gt;For the backend, I selected a powerful tech stack: Django 5.1.1 with Django REST Framework (DRF) running on Python 3.12. The database of choice was a PostgreSQL 16 instance, conveniently deployed using Docker. This combination proved to be an exceptional choice, offering a seamless developer experience and impressive performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Django 5.1.1: A Performance Leap
&lt;/h2&gt;

&lt;p&gt;It had been a while since I last utilized Django. My prior experience stemmed from the &lt;a href="https://coursera.org/share/2641cc5ab130fcbb040873a6efcdc1ce" rel="noopener noreferrer"&gt;Meta Backend Developer specialization&lt;/a&gt;, where I employed Django 4.1, the latest version at that time. &lt;/p&gt;

&lt;p&gt;Stepping into Django 5.1.1, a distinct sense of improvement in performance was undeniable. This solidified my appreciation for Django's exceptional Object-Relational Mapper (ORM), which continues to streamline database interactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  PostgreSQL 16: Power Under the Hood
&lt;/h2&gt;

&lt;p&gt;While the case study didn't necessitate crafting particularly complex queries, PostgreSQL 16's capabilities were nonetheless impressive. The Parallel Execution feature significantly enhanced query execution speed for various operations, including joins, aggregations, and scans. Additionally, the Bulk Data Loading feature offered a compelling solution for swift loading of large datasets using a novel binary format.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Developer-Centric Tech Stack
&lt;/h2&gt;

&lt;p&gt;The combination of Django 5.1.1, DRF, Python 3.12, and PostgreSQL 16 within a Dockerized environment culminated in a developer experience that surpassed any I've encountered with other frameworks. The overall synergy between these technologies fostered an efficient and streamlined development process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In conclusion, this project served as a valuable exploration of the latest advancements in Django and PostgreSQL. The performance optimizations in Django 5.1.1, coupled with PostgreSQL 16's feature set, particularly Parallel Execution and Bulk Data Loading, make this tech stack a compelling choice for building robust and scalable REST APIs. The seamless integration within a Dockerized environment further enhances development efficiency. I highly recommend considering this combination for your next project that demands exceptional performance and a smooth developer experience.&lt;/p&gt;

&lt;p&gt;If you want to have a look at the API, you can simply visit &lt;a href="https://github.com/Ingila185" rel="noopener noreferrer"&gt;my github&lt;/a&gt;. You can also &lt;a href="https://next-js-portfolio-two-ebon.vercel.app/en" rel="noopener noreferrer"&gt;learn more about me&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>django</category>
      <category>backenddevelopment</category>
      <category>restapi</category>
    </item>
    <item>
      <title>Building and Serving Angular Applications Across Environments with environment.ts (Angular 15+)</title>
      <dc:creator>Ingila Ejaz</dc:creator>
      <pubDate>Tue, 03 Sep 2024 11:17:45 +0000</pubDate>
      <link>https://dev.to/playfulprogramming-angular/building-and-serving-angular-applications-across-environments-with-environmentts-angular-15-6dk</link>
      <guid>https://dev.to/playfulprogramming-angular/building-and-serving-angular-applications-across-environments-with-environmentts-angular-15-6dk</guid>
      <description>&lt;p&gt;As an Angular developer, deploying your application to different environments – development (dev), user acceptance testing (UAT), and production – is a common occurrence. However, constantly changing code to cater to these environment specifics can be tedious, error-prone, and hinders efficiency.&lt;/p&gt;

&lt;p&gt;This article outlines a step-by-step approach to building and serving your Angular application across various environments without modifying the codebase, leveraging the powerful environment.ts functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario:
&lt;/h2&gt;

&lt;p&gt;Imagine an Angular application where the frontend interacts with backend APIs hosted on different environments. Let's explore how to create new environments, configure them, and serve/build your application based on the target environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Environments:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Generate Environment Files:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the following command in your terminal:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng generate environments&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This creates a folder named environments within the src directory, containing an initial environment.ts file. By default, this file serves as your development environment configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fim0gfzyl4oyziem18dub.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fim0gfzyl4oyziem18dub.PNG" alt="environment.ts"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Defining Environment Variables:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;environment.ts&lt;/code&gt; and define your development environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const environment = {
  production: false, //Set to False for development
  apiUrl: 'http://my-dev-url' //Replace with your development URL
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Creating Environment-Specific Files:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For UAT and Production environments, create separate files:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;environment.test.ts (for UAT)&lt;/code&gt;&lt;br&gt;
&lt;code&gt;environment.prod.ts (for Production)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add your respective UAT and Production API URLs to these files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// environment.test.ts (UAT)
export const environment = {
  production: false,
  apiUrl: 'http://my-uat-url'
};

// environment.prod.ts (Production)
export const environment = {
  production: true,
  apiUrl: 'http://my-prod-url'
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Utilizing Environments in Code:
&lt;/h2&gt;

&lt;p&gt;To utilize the API URL within your code:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Import environment.ts:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import { environment } from './environments/environment';&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Access API URL:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your service or component, inject the environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class MyService {
  constructor() {}
  apiUrl = environment.apiUrl;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Configuring &lt;code&gt;angular.json&lt;/code&gt; for Environment-Specific Builds:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target Configurations:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;angular.json&lt;/code&gt; and locate the "configurations" section under "build". This defines build configurations for different environments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"configurations": {
  "production": {
    // Rest of the configs
    "fileReplacements": [
      {
        "replace": "src/environments/environment.ts",
        "with": "src/environments/environment.prod.ts"
      }
    ]
  },

"staging": {
    // Rest of the configs
    "fileReplacements": [
      {
        "replace": "src/environments/environment.ts",
        "with": "src/environments/environment.test.ts"
      }
    ]
  },
  // ... other configurations
},

"defaultConfiguration": "production"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This instructs the Angular CLI to replace the default &lt;code&gt;environment.ts&lt;/code&gt; with &lt;code&gt;environment.prod.ts&lt;/code&gt;during production builds and &lt;code&gt;environment.test.ts&lt;/code&gt; for UAT builds.&lt;/p&gt;

&lt;p&gt;For serving the applications on multiple environments, you can add serve configurations inside &lt;code&gt;angular.json&lt;/code&gt; as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"serve": {
    "builder": "@angular-devkit/build-angular:dev-server",
    "options": { … },
    "configurations": {
      "development": {
        // Use the `development` configuration of the `build` target.
        "buildTarget": "my-app:build:development"
      },
      "staging": {
        // Use the `development` configuration of the `build` target.
        "buildTarget": "my-app:build:staging"
      },
      "production": {
        // Use the `production` configuration of the `build` target.
        "buildTarget": "my-app:build:production"
      }
    },
    "defaultConfiguration": "development"
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building and Serving for Specific Environments:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Build for production:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To build your application for production, use:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng build --configuration=production&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Use the name of the configuration defined in &lt;code&gt;angular.json&lt;/code&gt; (production, staging in our scenario)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Serve for UAT:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To serve your application for UAT, use:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng serve --configuration=staging&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Employing environment-specific configurations significantly enhances the developer experience. It offers a clean and maintainable approach, streamlining application deployment across diverse environments and ultimately reducing the mean time to production (MTTP). &lt;/p&gt;

&lt;p&gt;Also, if you enjoyed reading this article, you can &lt;a href="https://next-js-portfolio-two-ebon.vercel.app/en" rel="noopener noreferrer"&gt;learn more about me here.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
      <category>angular</category>
    </item>
    <item>
      <title>Angular 18.2: Enhanced Template Syntax and Streamlined Migrations</title>
      <dc:creator>Ingila Ejaz</dc:creator>
      <pubDate>Wed, 21 Aug 2024 23:43:15 +0000</pubDate>
      <link>https://dev.to/playfulprogramming-angular/angular-182-enhanced-template-syntax-and-streamlined-migrations-30c3</link>
      <guid>https://dev.to/playfulprogramming-angular/angular-182-enhanced-template-syntax-and-streamlined-migrations-30c3</guid>
      <description>&lt;p&gt;Angular 18.2 has arrived, and while it may be a minor release, it holds some valuable improvements that enhance developer experience. This article delves into these exciting features, focusing on the refined @let syntax and the introduction of new migration schematics.&lt;/p&gt;

&lt;h2&gt;
  
  
  @let Improves
&lt;/h2&gt;

&lt;p&gt;The @let syntax, a personal favorite among many Angular developers, continues to evolve in 18.2. It offers two distinct approaches to defining template variables, bringing greater flexibility and readability to your code:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamic @let:&lt;/strong&gt; Embrace the ability to utilize template reference variables within &lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt; and @if directives. Imagine effortlessly accessing a form's value within the template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input #myForm name="my-from" [maxlength]="maxLength" /&amp;gt;
@let formValue = myForm.value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Async @let:&lt;/strong&gt; In previous versions, accessing the latest value emitted from an observable required an ngIf directive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@if ({ tasks:  tasks$ | async }; as taskData) {

//shows the @if block before the 1st tasks$ emit
 @for (task of taskData.tasks; track task.id) {
    [...]
  } @empty {
    No Tasks pending.
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, achieve the same result with fewer lines and improved clarity&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@let tasks = tasks$ | async;
@for (task of tasks; track task.id) {
  [...]
}
@empty {
  No Tasks pending.
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember, @let variables are read-only and cannot be reassigned. However, their values will automatically update with each change detection cycle. While using the same names within the template and class component is technically possible, the long-term implications of this practice require further exploration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration Made Easy: New Schematics in Town
&lt;/h2&gt;

&lt;p&gt;Starting with Angular 17, we encountered three primary migration schematics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Template syntax control flow: &lt;code&gt;ng g @angular/core:control-flow&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;New app builder: &lt;code&gt;ng update @angular/cli --name use-application-builder&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Standalone components: &lt;code&gt;ng g @angular/core:standalone&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Angular 18.2 expands this toolkit with even more helpful tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standalone component route conversion:&lt;/strong&gt; Craft lazy-loaded routes from standalone components with ease using &lt;code&gt;ng g @angular/core:route-lazy-loading&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DI migration:&lt;/strong&gt; Simplify the transition from constructor-based dependency injection to the new functional inject() approach with &lt;code&gt;ng g @angular/core:inject-migration&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion and Looking Ahead
&lt;/h2&gt;

&lt;p&gt;Angular 18.2 may be a minor release, but it offers significant improvements for those who enjoy working with &lt;code&gt;@let&lt;/code&gt; and appreciate streamlined migration processes. As we eagerly anticipate the major features planned for Angular 19 in November 2024, version 18.3, arriving in the next six weeks, promises to be another exciting stepping stone. So, keep calm, keep coding, and embrace the advancements in Angular!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
