angular - איך להזריק את window לשירות?


אני כותב שירות ב-Angular 2 ב-TypeScript שישתמש ב-localstorage. אני רוצה להזריק הפניה לאובייקט window של הדפדפן לתוך השירות שלי מכיוון שאני לא רוצה להפנות לכל משתנה גלובלי כמו ב-Angular 1.x $window.

איך אני עושה את זה?

תשובה #1

זה עובד בשבילי כרגע (2018-03, אנגולר 5.2 עם AoT, נבדק ב-angular-cli ובבניית webpack מותאמת אישית):

ראשית, צור שירות ניתן להזרקה שמספק הפניה ל-window:

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

עכשיו, רשם את השירות הזה עם AppModule השורשי שלך כך שהוא יוכל להיות מוזרק בכל מקום:

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

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

ואז מאוחר יותר כאשר אתה צריך להזריק את window:

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

ייתכן שתרצה גם להוסיף את nativeDocument ועולמיים נוספים לשירות זה באותה צורה אם אתה משתמש בהם ביישום שלך.

עריכה: עודכן בהתאם להמלצת Truchainz. עריכה2: עודכן עבור angular 2.1.2 עריכה3: נוספו הערות AoT עריכה4: הוספת הערת פתרון מקביל לסוג any עריכה5: פתרון עודכן לשימוש ב-WindowRefService אשר מתקנת שגיאה שקיבלתי כששמשתי בפתרון הקודם עם בנייה שונה עריכה6: הוספת דוגמה להקלדת חלון מותאמת אישית

תשובה #2

אתה יכול לקבל חלון מהמסמך המוזרק.

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

}

אם יש לך מאפיינים מותאמים אישית על החלון, אתה יכול להשתמש בממשק משלך

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

}

תשובה #3

עם שחרור של Angular 2.0.0-rc.5 הוצג NgModule. הפתרון הקודם הפסיק לעבוד עבורי. זה מה שעשיתי כדי לתקן את זה:

app.module.ts:

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

בחלק מסוים:

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

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

אתה יכול גם להשתמש ב-OpaqueToken במקום במחרוזת 'Window'

עריכה :

מודול האפליקציה (AppModule) משמש לאתחול האפליקציה שלך בקובץ main.ts כך:

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

platformBrowserDynamic().bootstrapModule(AppModule)

למידע נוסף על NgModule קרא את תיעוד Angular 2: https://angular.io/docs/ts/latest/guide/ngmodule.html

תשובה #4

אתה יכול פשוט להזריק את זה אחרי שהגדרת את הספק:

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

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

תשובה #5

כדי שזה יעבוד על Angular 2.1.1 הייתי צריך @Inject את window באמצעות מחרוזת

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

ואז ללעוג לזה ככה

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

וב@NgModule הרגיל אני מספק את זה כך

{ provide: 'Window', useValue: window }

תשובה #6

הנה פתרון נוסף שעלה לי לאחרונה אחרי שנמאס לי לקבל את defaultView מה-DOCUMENT טוקן המובנה ולבדוק אם הוא ריק:

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

הערה: שחררתי את הטוקן הזה יחד עם הרבה אחרים שמאפשרים גישה לאובייקטים גלובליים כספרייה קטנה זמינה כאן: https://github.com/ng-web-apis/common

בדוק את שאר הארגון עבור ספריות API מקוריות רבות אחרות עבור Angular: https://github.com/ng-web-apis

תשובה #7

לפני ההצהרה של @Component, אתה גם יכול לעשות את זה,

declare var window: any;

המְחַבֵּר יאפשר לך בעצם לגשת כעת למשתנה הגלובלי window מכיוון שאתה מכריז עליו כמשתנה גלובלי מובן מאליו עם סוג any.

עם זאת, אני לא ממליץ לגשת ל-window בכל מקום באפליקציה שלך. במקום זאת, עליך ליצור שירותים שמגששים/משנים את התכונות הנדרשות של window (ולהזריק את השירותים הללו לרכיבים שלך) כדי להגביל מה שאתה יכול לעשות עם window מבלי לאפשר להם לשנות את אובייקט ה-window כולו.

תשובה #8

ב-Angular RC4 הדברים הבאים עובדים, שזה שילוב של כמה מהתשובות למעלה, בקובץ app.ts הראשי שלך הוסף את זה ל-providers:

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

ואז בשירות שלך וכו' הזרק אותו לבנאי

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