Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Jonathan Gamble
Jonathan Gamble

Posted on

     

Forcing Angular SSR to Wait in 2024

Angular has had a built-in way to wait on your functions to load, and you didn't know about it!

In the past...

You needed to import the hidden function...

import{ɵPendingTasksasPendingTasks}from'@angular/core';
Enter fullscreen modeExit fullscreen mode

Notice the greek letter that you wouldn't normally find with autocomplete.

Today

It is experimental, but you will soon be able to just importPendingTasks.

import{ExperimentalPendingTasksasPendingTasks}from'@angular/core';
Enter fullscreen modeExit fullscreen mode

Setup

I use myuseAsyncTransferState function for hydration. This ensures an async call, a fetch in this case, only runs once, and on the server.

exportconstuseAsyncTransferState=async<T>(name:string,fn:()=>T)=>{conststate=inject(TransferState);constkey=makeStateKey<T>(name);constcache=state.get(key,null);if(cache){returncache;}constdata=awaitfn()asT;state.set(key,data);returndata;};
Enter fullscreen modeExit fullscreen mode

Token

We need reusable tokens for theREQUEST object.

// request.token.tsimport{InjectionToken}from"@angular/core";importtype{Request,Response}from'express';exportconstREQUEST=newInjectionToken<Request>('REQUEST');exportconstRESPONSE=newInjectionToken<Response>('RESPONSE');
Enter fullscreen modeExit fullscreen mode

We must pass the request object as a provider in our render function.

// main.server.tsexportdefaultasyncfunctionrender(url:string,document:string,{req,res}:{req:Request;res:Response}){consthtml=awaitrenderApplication(bootstrap,{document,url,platformProviders:[{provide:REQUEST,useValue:req},{provide:RESPONSE,useValue:res},],});returnhtml;}
Enter fullscreen modeExit fullscreen mode

Angular is currently in the process ofadding all of these features, and potentially endpoints!!!!! 😀 😀 😀 🗼 🎆

Fetch Something

Because endpoints are not currently there, I am testing this with Analog. Here is ahello endpoint that takes 5 seconds to load.

import{defineEventHandler}from'h3';exportdefaultdefineEventHandler(async()=>{constx=newPromise((resolve)=>setTimeout(()=>{resolve({message:"loaded from the server after 5 seconds!"});},5000));returnawaitx;});
Enter fullscreen modeExit fullscreen mode

Test Component

Here we use therequest in order to get the host URL. Then we useuseAsyncTransferState to ensure things only run on the server, and only once. Finally, we usependingTasks to ensure the component is not fully rendered until the async completes.

import{AsyncPipe}from'@angular/common';import{Component,ExperimentalPendingTasksasPendingTasks,inject,isDevMode}from'@angular/core';import{REQUEST}from'@lib/request.token';import{useAsyncTransferState}from'@lib/utils';@Component({selector:'app-home',standalone:true,imports:[AsyncPipe],template:`    <div>      <p>{{ data | async }}</p>    </div>  `})exportdefaultclassHomeComponent{privatependingTasks=inject(PendingTasks);protectedreadonlyrequest=inject(REQUEST);data=this.getData();// fetch data, will only run on serverprivateasync_getData(){constschema=isDevMode()?'http://':'https://';consthost=this.request.headers.host;consturl=schema+host+'/api/hello';constr=awaitfetch(url,{headers:{'Content-Type':'application/json',}});constx=awaitr.json();returnx.message;}// fetch data with pending task and transfer stateasyncgetData(){consttaskCleanup=this.pendingTasks.add();constr=awaituseAsyncTransferState('pending',async()=>awaitthis._getData());taskCleanup();returnr;}}
Enter fullscreen modeExit fullscreen mode

Pending Task

Pending Task is very simple.

// create a new taskconsttaskCleanup=this.pendingTasks.add();// do something asyncconstr=awaitfn();// let Angular know it can rendertaskCleanup();
Enter fullscreen modeExit fullscreen mode

Thats it! Bingo Shabongo!

Repo:GitHub
Demo:Vercel Edge - Takes 5s to load!

Should you use this?

Nope! Seriously, don't use this.

After going down the rabbit hole for years on Angular async rendering (read the old posts in this chain), it is definitely best practice to put ALL async functions in aresolver. The resolver MUST load before a component, which is a much better development environment. The only exception would be@defer IMHO.

However, there are some edge cases where it makes sense for your app. The is particularly evident when you don't want to rewrite your whole application to use resolvers. Either way, you need to be aware of your options!

J

Top comments(2)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
yusuf_musa_98d2309e6dbba2 profile image
Yusuf Musa
  • Joined

Hello, All this look alien to me because its my first time getting into server side rendering. How easy is this SSR of Angular when it comes deployment? There are no many articles or guides on how to do this?

How effective is SSR of Angular so far when it comes to SEO?

CollapseExpand
 
jdgamble555 profile image
Jonathan Gamble
My main job is not in IT, but I do have a CS degree and have been programming for over 20 years (only born in 1984). I hate dealing with Servers, and want Graph Databases to be the new norm.
  • Location
    Louisiana
  • Education
    LSU, Oregon State, Middlebury
  • Joined

Look into Analog. Angular by itself is terrible with SSR deployment. -analogjs.org/

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

My main job is not in IT, but I do have a CS degree and have been programming for over 20 years (only born in 1984). I hate dealing with Servers, and want Graph Databases to be the new norm.
  • Location
    Louisiana
  • Education
    LSU, Oregon State, Middlebury
  • Joined

More fromJonathan Gamble

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp