¿Cuál es la diferencia entre proporcionar e inyectar 'Ventana' frente a Ventana en Angular 8 y 9?

10

Tengo dos proyectos angulares que usan estas versiones:

  • 9.0.0-siguiente.6
  • 8.1.0

En la versión 9 usé esto para proporcionar e inyectar el windowobjeto:

@NgModule({
  providers: [
    {
      provide: Window,
      useValue: window
    },
  ]
})

export class TestComponent implements OnInit {
  constructor(@Inject(Window) private window: Window)
}

Que funciona bien.


Tomando este enfoque a la versión 8 arrojó advertencias y errores durante la compilación:

Advertencia: no se pueden resolver todos los parámetros para TestComponent ...

Lo resolví usando comillas simples, como esta:

@NgModule({
  providers: [
    {
      provide: 'Window',
      useValue: window
    },
  ]
})

export class TestComponent implements OnInit {
  constructor(@Inject('Window') private window: Window)
}

¿Cuál es la diferencia entre ambas versiones?
¿Cuál es la diferencia en Angular 8 y 9 que causa esto?

pantalla de lámpara
fuente
Espero que con la recompensa pueda obtener una respuesta de la cual yo y otras personas podamos aprender y comprender mejor cómo funcionan los proveedores y di en Angular y en diferentes versiones del marco.
pantalla

Respuestas:

6

Para que su aplicación funcione con Renderizado del lado del servidor, le sugiero que no solo use la ventana a través del token, sino que también cree este token de manera amigable con SSR, sin hacer referencia alguna window. Angular tiene DOCUMENTtoken incorporado para acceder document. Esto es lo que se me ocurrió para que mis proyectos lo usen a windowtravés de tokens:

import {DOCUMENT} from '@angular/common';
import {inject, InjectionToken} from '@angular/core';

export const WINDOW = new InjectionToken<Window>(
    'An abstraction over global window object',
    {
        factory: () => {
            const {defaultView} = inject(DOCUMENT);

            if (!defaultView) {
                throw new Error('Window is not available');
            }

            return defaultView;
        },
    },
);
Waterplea
fuente
Muchas gracias por su respuesta. Es muy útil y voy a usar una solución como esta en el futuro.
pantalla
5

Teniendo en cuenta la ValueProviderinterfaz:

export declare interface ValueProvider extends ValueSansProvider {
    /**
     * An injection token. Typically an instance of `Type` or `InjectionToken`, but can be `any`.
     */
    provide: any;
    /**
     * When true, injector returns an array of instances. This is useful to allow multiple
     * providers spread across many files to provide configuration information to a common token.
     */
    multi?: boolean;
}

La providepropiedad es de tipo any. Eso significa que cualquier objeto (incluido el Windowconstructor) puede ir dentro de él. El objeto en realidad no importa, solo la referencia importa para identificar qué proveedor debe usarse para inyectar un parámetro en un constructor.

No debe considerarse una buena práctica utilizar el Windowconstructor nativo como token de inyección. Falla en tiempo de compilación porque Windowexiste en tiempo de ejecución en un entorno de navegador, también existe como TypeScript declarepero el compilador Angular 8 no puede hacer análisis de código estático para correlacionar los parámetros Windowen los proveedores y Windowen los parámetros de un constructor, ya que la asignación de Windowse realiza por el navegador, no por el código. Sin embargo, no estoy seguro de por qué funciona en Angular 9 ...

Debe crear su propio token de inyección que represente al proveedor de dependencia. Esta ficha de inyección debe ser:

  • Una cadena dedicada (como hiciste con 'Window')
  • Un dedicado InjectionToken. Por ejemploexport const window = new InjectionToken<Window>('window');

Además, el código angular debe ser independiente de la plataforma (debe ser ejecutable en un navegador y también en un servidor Node.js), por lo que sería mejor usar una fábrica que devuelva windowo undefined/ null, luego manejar el caso undefined/ nullen los componentes.

Guerric P
fuente
1
Muchas gracias por tu respuesta detallada. Ayudó mucho
pantalla
1
¡Muy bien! Gracias. Acabo de comprobar los documentos angulares (v8 y v9) y no encontré un solo ejemplo en el que usan cadenas. :( ¡Realmente deberían explicar esto en los documentos!
Zaphoid