'How execute a piece of code only in the Browser using Angular Universal?

I have a piece of code that I want to execute only in the Browser, I know about the PlatformId so I made a Service:

I'm using Angular 12.2.13

import { isPlatformBrowser, isPlatformServer } from '@angular/common';
import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable()
export class ApplicationService {

  public applicationLoaded$ = new BehaviorSubject<boolean>(undefined as any);

  constructor(@Inject(PLATFORM_ID) private platformId: Object) {}

  get isBrowser(): boolean {
    return isPlatformBrowser(this.platformId);
  }

  get isServer(): boolean {
    return isPlatformServer(this.platformId);
  }

  set currentApplicationLoadedValue(loaded: boolean) {
    this.applicationLoaded$.next(loaded);
  }

  get currentApplicationLoadedValue(): boolean {
    return this.applicationLoaded$.value;
  }
}

The problem is everytime I go to a page the ngOnInit execute 100% on Server and if I use something like this:

ngOnInit(): void {
  if(this.appService.isBrowser) {
    console.log("START");
  }
}

ngAfterViewInit(): void {
  if(this.appService.isBrowser) {
    console.log("START");
  }
}

ngAfterViewChecked(): void {
  if(this.appService.isBrowser) {
    console.log("START");
  }
}

The name from my component is RestaurantsComponent, this is the first page that is loaded, so my ngOnInit, ngAfterViewInit and ngAfterViewChecked, when I test it, my page is loaded with no problem but I can't have this console showing console.log("START"); in my Browser, because they execute only on server.

This is my app.module.ts

@NgModule({
  declarations: [AppComponent, AppBrownserRenderer, AppServerRenderer],
  imports: [
    BrowserModule.withServerTransition({ appId: 'menap-app' }),
    BrowserAnimationsModule,
    CommonModule,
    /* GoogleTagManagerModule, */
    AppRoutingModule,
    HttpClientModule,
    MatSnackBarModule,
/*     SplashScreenModule,
    NgbModalModule,
    FormsModule,
    ReactiveFormsModule,
    MatSnackBarModule,
    MatBottomSheetModule,
    ScrollToModule.forRoot(), */
  ],
  providers: [
    ApplicationGuard,
    SnackBarService,
    CartService,
    LocationService,
    CategoryService,
    CompanyService,
    UtilsService,
    ApplicationService,
    StorageService,
    AuthStorageService,
    UserService,
    OrderService,
/*     {
      provide: APP_INITIALIZER,
      useFactory: appInitializer,
      multi: true,
      deps: [AuthService, CompanyService, ApplicationService],
    }, */
/*     {
      provide: 'googleTagManagerId',
      useFactory: GoogleTagManagerId,
      deps: [TrafficService],
    }, */
/*     { provide: MatPaginatorIntl, useFactory: getPaginatorI18n },
    { provide: ErrorHandler, useClass: GlobalErrorHandler },
    { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true },
    { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
    { provide: LOCALE_ID, useValue: 'pt' }, */
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

So in app.component.html for testing purpose I do this:

<!-- <app-splash-screen></app-splash-screen> -->
<!-- <router-outlet></router-outlet> -->

<div *AppServerRenderer>
  LOADING....
</div>

<div *AppBrownserRenderer>
  FINISH LOADING....
</div>

So when my application got to app.component.ts they never change to BrowserMode so the load stop in LOADING...

What is happening? I made this same code in a clean Angular Project and works fine!

enter image description here



Solution 1:[1]

The method you have followed here is right. The conditions will only be activated once you are on the browser. But now as the code is running on browser you will not get any console output for server. Rather the output will be shown on the browser console.

Updated

I ran the codes you have given. I changed the @Injectable() to @Injectable({ providedIn: 'root' }) in the ApplicationService. Then provided it to the module in which the component is declared. Afterwards injected to service to the component.

ApplicationService

import { isPlatformBrowser, isPlatformServer } from '@angular/common';
import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ApplicationService {

  public applicationLoaded$ = new BehaviorSubject<boolean>(undefined as any);

  constructor(@Inject(PLATFORM_ID) private platformId: Object) {}

  get isBrowser(): boolean {
    return isPlatformBrowser(this.platformId);
  }

  get isServer(): boolean {
    return isPlatformServer(this.platformId);
  }

  set currentApplicationLoadedValue(loaded: boolean) {
    this.applicationLoaded$.next(loaded);
  }

  get currentApplicationLoadedValue(): boolean {
    return this.applicationLoaded$.value;
  }
}

Provided the service in the component's module

 providers: [
    ApplicationService
  ],

Component

constructor(private appService: ApplicationService) { }

  ngOnInit(): void {
    if(this.appService.isBrowser) {
      console.log("On Init");
    }
  }

  ngAfterViewInit(): void {
    if(this.appService.isBrowser) {
      console.log("On After View Init");
    }
  }

  ngAfterViewChecked(): void {
    if(this.appService.isBrowser) {
      console.log("After view Checked");
    }
  }

All 3 outputs are now working. Got all 3 consoles on the browser

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1