Logo

dev-resources.site

for different kinds of informations.

Fixing SSR Rendering Issues with Angular Resolver for Async Pipe Data

Published at
10/31/2024
Categories
angular
ssr
learning
frontend
Author
ryantehhoonmeng
Categories
4 categories in total
angular
open
ssr
open
learning
open
frontend
open
Author
15 person written this
ryantehhoonmeng
open
Fixing SSR Rendering Issues with Angular Resolver for Async Pipe Data

My blog site, still a WIP though, is an Angular build time pre-rendered static site. I chose to develop this blog as a static site as I don't foresee I need dynamic contents, helps with Search Engine Optimization (SEO), and the greatest factor, I can just host this blog site with firebase for free :D

Problem: Async Pipe Not Rendering on the Server Side

During development of this blog site with Angular SSR, I ran into a situation where the HTML returned from the dev server lacked blog content. Although the site was rendering on the server side, the actual content wasn’t visible in the HTML sent from the server; it was only populated on the client side. Here's how my initial setup looked:

Initial Component Code

blog.component.ts:

@Component({
  selector: 'app-blog',
  templateUrl: './blog.component.html',
  styleUrl: './blog.component.scss',
})
export class BlogComponent implements OnInit, OnDestroy {
  blog$ = new BehaviorSubject<Blog | undefined>(undefined);
  id$: Observable<string>;
  private subscriptions: Subscription[] = [];

  constructor(
    private readonly blogService: BlogService,
    route: ActivatedRoute
  ) {
    this.id$ = route.params.pipe(map((p) => p['id']));
  }

  ngOnInit(): void {
    this.id$
        .pipe(
            mergeMap((id) => {
                return this.blogService.getBlogById(id).pipe(
                    tap((blog) => {
                        this.blog$.next(blog);
                    })
                );
            })
        )
        .subscribe();
  }
}
Enter fullscreen mode Exit fullscreen mode

blog.component.html:

<h1 class="blog-title">{{ (blog$ | async)?.title }}</h1>
<div class="blog-content" [innerHTML]="(blog$ | async)?.content"></div>
Enter fullscreen mode Exit fullscreen mode

Resulting Rendered HTML

Upon viewing the rendered HTML from the server, I saw this:

<h1 class="blog-title"><!--ngEnt--></h1><div class="blog-content"><!--ngEnt--></div>
Enter fullscreen mode Exit fullscreen mode

The missing content meant that the async pipe was not rendering on the server side. After investigating, I found this helpful article, which explained that ngOnInit is rendered synchronously. The server completes rendering before the data is retrieved, causing the async pipe to return undefined.

Solution: Using Angular Resolver to Fetch Data Before Rendering

The solution to this issue is to leverage an Angular Resolver. Resolvers fetch the necessary data before Angular activates a route, allowing SSR to render the content directly without waiting for client-side data retrieval.

Updated Component with Resolver

With the resolver implemented, the updated component code looks like this:

blog.component.ts:

@Component({
  selector: 'app-blog',
  templateUrl: './blog.component.html',
  styleUrl: './blog.component.scss',
})
export class BlogComponent implements OnInit, OnDestroy {
  blog$ = new BehaviorSubject<BlogDto | undefined>(undefined);

  constructor(private readonly route: ActivatedRoute) {}

  ngOnInit(): void {
    this.subscriptions.push(
      this.route.data.subscribe((data) => {
        const blog: BlogDto = data['blog'];
        this.blog$.next(blog);
      })
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

Adding the Blog Resolver

The Blog Resolver ensures that the blog content is loaded before the component is activated. Here’s the resolver code:

blog-resolver.service.ts:

export class BlogResolverService implements Resolve<BlogDto | undefined> {
  constructor(private readonly blogService: BlogService) {}

  resolve(
    route: ActivatedRouteSnapshot,
    _: RouterStateSnapshot
  ): MaybeAsync<BlogDto | undefined> {
    const blogId = route.params['id'];
    return this.blogService.getBlogById(blogId);
  }
}
Enter fullscreen mode Exit fullscreen mode

Applying the Resolver to Routes

To apply the resolver to your routes, add it to your Angular router configuration. Here’s an example:

const routes: Routes = [
  {
    path: 'blog/:id',
    component: BlogComponent,
    resolve: { blog: BlogResolverService },
  },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
})
export class BlogRoutingModule {}
Enter fullscreen mode Exit fullscreen mode

Final Thoughts on Angular Resolver for SSR

Using Angular Resolver is an effective way to make sure that your SSR-rendered content is loaded and ready to be displayed. This not only improves the user experience but also ensures search engines can index your pre-rendered content. When implemented correctly, a resolver minimizes the need for post-rendering data fetching, making your application more efficient and SEO-friendly.

For further reading, check out the official Angular documentation on resolvers to deepen your understanding.

Link to the original blog post.

ssr Article's
30 articles in total
Favicon
Custom builder for Angular: My way
Favicon
Setting Up Dual Compilation (SSR + CSR) in ViteJS with vite-plugin-builder
Favicon
# Key New Features in React Router 7: Embracing the Remix Future
Favicon
Beginner SEO in React JS - React Helmet
Favicon
Setting up partial SSR for a React + TypeScript + webpack app from scratch
Favicon
Create an SSR Application with Vite, React, React Query and React Router
Favicon
Understanding Web Rendering: Performance Implications and Use Cases
Favicon
Make EditorJS work in Svelte(kit) SSR
Favicon
Client-side Rendering & Server-side Rendering
Favicon
A Practical Guide to CSR and SSR with React 19 and esbuild
Favicon
Fixing SSR Rendering Issues with Angular Resolver for Async Pipe Data
Favicon
Choosing Remix as a Server-Side Rendering (SSR) Framework
Favicon
Implementing Server-Side Rendering (SSR) with Next.js and Firebase for SEO-Friendly React Apps 🚀
Favicon
Do You Need to SSR Your Web Components?
Favicon
Web Components and SSR - 2024 Edition
Favicon
Dark side of Next.js - App Router
Favicon
How to achieve unified management of four types of global state data in Vue3?
Favicon
What do SSR, CSR, ISR and SSG mean? A complete guide for web developers
Favicon
Vue 3.5 “Tengen Toppa Gurren Lagann” Innovations: Advanced Features and Most Powerful Updates 🚀
Favicon
Inertiajs Server-side Rendering (SSR) For React (Vite Setup)
Favicon
Vaadin, the battery-included server-side AJAX framework
Favicon
How to add Supabase Auth to Astro
Favicon
Dive into Next.js Server-Side Rendering (SSR): From SPA to ISR
Favicon
Why do client components render as SSR in nextjs, marking components as "use client" still render its html as SSR why?
Favicon
Augmenting the client with Alpine.js
Favicon
Augmenting the client with Vue.js
Favicon
Server-Side Rendering (SSR): Uma Solução para SEO e Performance em Aplicações React
Favicon
SSR and CSR Explained
Favicon
A short history of AJAX and SSR
Favicon
How to Do Server-Side Rendering (SSR) in Next.js

Featured ones: