angular - Bir servise window nasıl enjekte edilir?


TypeScript ile Angular 2 servis yazıyorum ve bu servis localstorage kullanacak. Servisime tarayıcı window nesnesine bir referans enjekte etmek istiyorum çünkü Angular 1.x $window gibi herhangi bir global değişkene başvurmak istemiyorum.

Bunu nasıl yapabilirim?

Cevap #1

Bu şu anda benim için çalışıyor (2018-03, AoT ile Angular 5.2, angular-cli ve özel bir webpack yapısında test edildi):

Öncelikle, window’a bir referans sağlayan enjekte edilebilir bir servis oluşturun:

import { Injectable } from '@angular/core';

// This interface is optional, showing how you can add strong typings for custom globals.
// Just use "Window" as the type if you don't have custom global stuff
export interface ICustomWindow extends Window {
    __custom_global_stuff: string;
}

function getWindow (): any {
    return window;
}

@Injectable()
export class WindowRefService {
    get nativeWindow (): ICustomWindow {
        return getWindow();
    }
}

Şimdi, bu servisi kök AppModule'unuzda kaydedin ki her yere enjekte edilebilsin:

import { WindowRefService } from './window-ref.service';

@NgModule({        
  providers: [
    WindowRefService 
  ],
  ...
})
export class AppModule {}

ve sonra ileride window'u enjekte etmeniz gerektiğinde:

import { Component} from '@angular/core';
import { WindowRefService, ICustomWindow } from './window-ref.service';

@Component({ ... })
export default class MyCoolComponent {
    private _window: ICustomWindow;

    constructor (
        windowRef: WindowRefService
    ) {
        this._window = windowRef.nativeWindow;
    }

    public doThing (): void {
        let foo = this._window.XMLHttpRequest;
        let bar = this._window.__custom_global_stuff;
    }
...

Uygulamanızda bunları kullanıyorsanız, bu servise nativeDocument ve diğer global değişkenleri benzer bir şekilde eklemek isteyebilirsiniz.

düzenle: Truchainz önerisi ile güncellendi. düzenle2: Angular 2.1.2 için güncellendi düzenle3: AoT notları eklendi düzenle4: any türü geçici çözüm notu eklendi düzenle5: Önceki çözümü farklı bir derleme ile kullanırken aldığım hatayı düzelten WindowRefService kullanacak şekilde çözüm güncellendi düzenle6: örnek özel Window tipi ekleniyor

Cevap #2

Enjekte edilmiş belgeden pencereyi alabilirsiniz.

import { Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

export class MyClass {

  private window: Window;

  constructor(@Inject(DOCUMENT) private document: Document) {
     this.window = this.document.defaultView;
  }

  check() {
    console.log(this.document);
    console.log(this.window);
  }

}

Eğer window üzerinde özel özellikleriniz varsa, kendi arayüzünüzü kullanabilirsiniz

import { Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

export interface CustomWindow extends Window {
  $: JQuery
  customProperty: boolean;
}

export class MyClass {

  private window: CustomWindow;

  constructor(@Inject(DOCUMENT) private document: Document) {
     this.window = <any>this.document.defaultView;
  }

  check() {
    console.log(this.document);
    console.log(this.window);
  }

}

Cevap #3

Angular 2.0.0-rc.5 sürümünün yayınlanmasıyla NgModule tanıtıldı. Önceki çözüm benim için çalışmayı durdurdu. Bunu düzeltmek için yaptığım şey şuydu:

app.module.ts:

@NgModule({        
  providers: [
    { provide: 'Window',  useValue: window }
  ],
  declarations: [...],
  imports: [...]
})
export class AppModule {}

Bazı bileşenlerde:

import { Component, Inject } from '@angular/core';

@Component({...})
export class MyComponent {
    constructor (@Inject('Window') window: Window) {}
}

Ayrıca 'Window' stringi yerine bir OpaqueToken da kullanabilirsiniz

Düzenle :

AppModule, uygulamanızı main.ts içinde bu şekilde başlatmak için kullanılır:

import { platformBrowserDynamic  } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule)

NgModule hakkında daha fazla bilgi için Angular 2 belgelerini okuyun: https://angular.io/docs/ts/latest/guide/ngmodule.html

Cevap #4

Sadece sağlayıcıyı ayarladıktan sonra enjekte edebilirsiniz:

import {provide} from 'angular2/core';
bootstrap(..., [provide(Window, {useValue: window})]);

constructor(private window: Window) {
    // this.window
}

Cevap #5

Angular 2.1.1 üzerinde çalıştırmak için @Inject ile window'u bir string kullanarak enjekte etmem gerekti.

  constructor( @Inject('Window') private window: Window) { }

ve sonra onu böyle alay et

beforeEach(() => {
  let windowMock: Window = <any>{ };
  TestBed.configureTestingModule({
    providers: [
      ApiUriService,
      { provide: 'Window', useFactory: (() => { return windowMock; }) }
    ]
  });

ve normal @NgModule içinde bunu şöyle sağlıyorum

{ provide: 'Window', useValue: window }

Cevap #6

İşte yakın zamanda bulduğum başka bir çözüm; DOCUMENT yerleşik token'ından defaultView alıp bunun null olup olmadığını kontrol etmekten sıkıldıktan sonra:

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;
        }
    });

Not: Bu token'i birçok diğer global nesne erişicisi ile birlikte küçük bir kütüphane olarak burada yayınladım: https://github.com/ng-web-apis/common

Angular için diğer birçok yerel API kütüphanesi için organizasyonun geri kalanına göz atın: https://github.com/ng-web-apis

Cevap #7

@Component deklarasyonundan önce bunu da yapabilirsiniz,

declare var window: any;

Derleyici, onu any türünde varsayılan bir global değişken olarak ilan ettiğiniz için artık global window değişkenine gerçekten erişmenize izin verecektir.

Ancak uygulamanızın her yerinde window'a erişmenizi önermem. İhtiyacınız olan window özelliklerine erişen/değiştiren servisler oluşturmalı (ve bu servisleri bileşenlerinize enjekte etmelisiniz) ve window ile neler yapabileceğinizi sınırlamalısınız, böylece tüm window nesnesini değiştirmelerine izin vermemiş olursunuz.

Cevap #8

Angular RC4'te aşağıdakiler çalışır, bu yukarıdaki bazı cevapların bir kombinasyonudur, kök app.ts dosyanıza bunu providers olarak ekleyin:

@Component({
    templateUrl: 'build/app.html',
    providers: [
        anotherProvider,
        { provide: Window, useValue: window }
    ]
})

Sonra servisinizde vb. bunu yapıcıya enjekte edin

constructor(
      @Inject(Window) private _window: Window,
)

İlgili makaleler