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,
)
