3

Problem

I have an angular 18 application with ssr enabled.

On this application I have a component that show information based on a boolean (whether the user is logged or not).

This boolean is never true on server side, so it will only be displayed on client side.

To simplify the debug I am currently using a boolean that is true when it's client side, and is defined inside the constructor of my component

 isBrowserSide:boolean;

  constructor(
    @Inject(PLATFORM_ID) platformId: Object,
  ) {
    this.isBrowserSide = isPlatformBrowser(platformId);
  }

Inside the html template it's used like this:

Is Browser = {{isBrowserSide}}
@if(isBrowserSide){
  Inside if: browser bool = {{isBrowserSide}}
} @else{
  Inside else: browser bool = {{isBrowserSide}}
}

The problem is that when I load the page both if and else are rendered, and then it will be good after some seconds. html result

What did I found

On an other components I have a defer with a placeholder minium, and my if will be right only when the defer is finished (Originaly the minimum is 2.5s but I increased it to 20s to confirm the origin).

<app-navbar></app-navbar> <-- thats where the if is

@defer{
    test
} @placeholder(minimum 20.5s){
}

In the console I get two messages:

NG0506: Angular hydration expected the ApplicationRef.isStable() to emit `true`, but it didn't happen within 10000ms. Angular hydration logic depends on the application becoming stable as a signal to complete hydration process. Find more at https://angular.dev/errors/NG0506

And the second:

Angular hydrated 29 component(s) and 354 node(s), 0 component(s) were skipped. Learn more at https://angular.dev/guide/hydration.

Thats when the second show up that the wrong condition is removed from the html.

Also when I look the html sent by the server, the conditions are right. So it's like the browser run the other condition but keep the server side condition until it's stable.

Do you have any idea why it's doing this ? And how to fix it ?

I also created a stackflitz with exacly the same code as above: https://stackblitz.com/edit/angular-18-ssr-zozj4bpz

0

2 Answers 2

0

I would describe that a "work as designed".

That placeholder with the minimum timer is meant to keep the app unstable.

Hydration clean-up only removes nodes after stability is reached.

The core issue is that your client and your server render something different which is not recommended (the issue you're having is one of reason why).

Sign up to request clarification or add additional context in comments.

2 Comments

I guess this is designed that way, but that's kinda counterintuitive, since for me defering a component would mean that the app does not need the component and will work as it's not there, and load it later. As you suggested I will propably open a discussion on the angular repo. I also found a workaround that I posted.
@GregoryBoutte SGTM
0

From my understanding, making a defer that wait a certain timing. Either based on the trigger or the placeholder.

As @Matthieu Riegler explained, the main cause is that my application SSR does not render the same as the client side. Fow now I can't do anything about it.

So here my goal is to make the timing between the answer received from the server and the stability as low as possible.

I found a workaround which was to create my own defer.

In the component where I have a defer I added the following code inside the ngOnInit:

  constructor(
    private appRef: ApplicationRef, 
    private cd: ChangeDetectorRef
  ) {}

  ngOnInit() {

    this.appRef.isStable.pipe( first((isStable) => isStable) ).subscribe((isStable) => {
      setTimeout(() => {
        this.shouldShow = true;
        this.cd.markForCheck();
      },10000);
    });

  }

This is waiting for the application to be stable, once it is, it start a timer (in the example 10seconds), and then change the boolean that is used inside the html as a condition to show the elements.

<app-child /> 


@if(shouldShow){
 defered components
}@else{
  placeholder
}

It's not perfect since it's still doing the same, but the delay between is smaller as this is not waiting for the defered components to be rendered.

Edit:

I created an issue on the angular repo, and a fix was made. It was merged. It seems that it will be published on angular 19.2.

Issue link: https://github.com/angular/angular/issues/60373

The PR: https://github.com/angular/angular/pull/60392

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.