ionic-team / ionic-framework

A powerful cross-platform UI toolkit for building native-quality iOS, Android, and Progressive Web Apps with HTML, CSS, and JavaScript.
https://ionicframework.com
MIT License
51.13k stars 13.5k forks source link

bug: angular, mode always md when doing ssr #22121

Open agustinhaller opened 4 years ago

agustinhaller commented 4 years ago

Bug Report

Ionic version: [ ] 4.x [x] 5.x

Current behavior: When using @ionic/angular-server the platform mode seems to be hardcoded to 'md'. It won’t render the specific platform mode inferred from the user agent like it does when using browser rendering.

Also, there’s no way to specify a custom mode for the IonicServerModule (something similar to the config option available with IonicModule.forRoot({...}))

Expected behavior: Should be able to specify a mode to render the app in the IonicServerModule.

Steps to reproduce:

Scenario 1: No custom config on the IonicModule.forRoot({...})

  1. Enable (SSR with Angular Universal And Ionic)[https://ionicframework.com/blog/ssr-with-angular-universal-and-ionic/]
  2. Run npm run dev:ssr
  3. Open Chrome dev tools and emulate an iOS device
  4. The app doesn't recognize the iOS device and always renders with 'md' mode

Scenario 2: Hardcoding 'ios' mode on the IonicModule.forRoot({...}) config

  1. Enable (SSR with Angular Universal And Ionic)[https://ionicframework.com/blog/ssr-with-angular-universal-and-ionic/]
  2. Run npm run dev:ssr
  3. Open Chrome dev tools and emulate an iOS device, also disable javascript (in order to properly test SSR)
  4. The app doesn't recognize the hardcoded 'ios' mode and always renders with 'md' mode

Note: In Scenario 2 if we enable javascript to allow the transition between SSR and the browser, we first get the app with 'md' mode and then it transitions to 'ios' mode. Having an option to config the mode in the IonicServerModule would avoid this flick.

Other information: I know there are some SSR gotchas to be aware of. There's no way to access the window object or the navigation user agent to detect the correct mode.

However there are ways to detect the user agent in the server side using for example the request user-agent headers.

By adding a way to specify the Ionic config through a provider (instead or in addition to the IonicModule.forRoot({...})) it would be possible to define the proper mode in SSR.

I'm thinking something like the APP_INITIALIZER

server.ts (option 1)

We can set a custom header in the response object and get that value in the app.module.ts using the RESPONSE injection token.

server.get('*', (req, res) => {
  // Add custom header to the response we send to our Angular app. We will get this custom header in the AppModule APP_INITIALIZER
  res.set('mobile-device', 'ios');

  res.render(
    indexHtml,
    {
      req,
      res
    }
  );
});

server.ts (option 2)

Or we can override a new provider that handles the Ionic config directly in the server.ts. For this approach we would need to create a new InjectionToken for the NEW_IONIC_CONFIG.

server.get('*', (req, res) => {
  res.render(
    indexHtml,
    {
      req,
      res,
      providers: [
        {
          provide: NEW_IONIC_CONFIG,
          useValue: {
            mode: 'ios'
          }
        }
      ]
    }
  );
});

app.modeule.ts

Then in the app.module.ts we can easily get the config from the angular RESPONSE injection token or directly from the new NEW_IONIC_CONFIG token.

{
  provide: APP_INITIALIZER,
  useFactory: (platformId: object, response: any) => {
    return () => {
      // In the server.ts we added a custom response header with information about the device requesting the app
      if (isPlatformServer(platformId)) {
        if (response && response !== null) {
          // Get custom header from the response sent from the server.ts
          const mobileDeviceHeader = response.get('mobile-device');
          console.log('mobileDeviceHeader', mobileDeviceHeader);

          // Set Ionic config mode?
        }
      } else {
        console.log('not server');
      }
    };
  },
  deps: [PLATFORM_ID, [new Optional(), RESPONSE]],
  multi: true
}

Ionic info:

Ionic:

   Ionic CLI                     : 6.11.8 (/Users/agustin/.npm-global/lib/node_modules/@ionic/cli)
   Ionic Framework               : @ionic/angular 5.3.3
   @angular-devkit/build-angular : 0.1001.2
   @angular-devkit/schematics    : 10.1.2
   @angular/cli                  : 10.1.2
   @ionic/angular-toolkit        : 2.3.3

Capacitor:

   Capacitor CLI   : 2.4.1
   @capacitor/core : 2.4.1

Utility:

   cordova-res (update available: 0.15.1) : 0.11.0
   native-run                             : not installed

System:

   NodeJS : v12.14.1 (/usr/local/bin/node)
   npm    : 6.14.8
   OS     : macOS Catalina

@mhartington I know you have been working with the IonicServerModule. Can you point me in the right direction?

archevis commented 3 years ago

Same issue here... did you ever find a solution?

harivigneshkn commented 2 years ago

We are also facing the same problem. Is there any solution for this?