किसी सेवा में विंडो इंजेक्ट कैसे करें?


111

मैं टाइपस्क्रिप्ट में एक कोणीय 2 सेवा लिख ​​रहा हूं जो इसका उपयोग करेगा localstorage। मैं windowअपनी सेवा में ब्राउज़र ऑब्जेक्ट के संदर्भ को इंजेक्ट करना चाहता हूं क्योंकि मैं कोणीय 1.x जैसे किसी भी वैश्विक चर का संदर्भ नहीं देना चाहता हूं $window

मैं उसको कैसे करू?

जवाबों:


135

यह वर्तमान में मेरे लिए काम कर रहा है (2018-03, एओटी के साथ कोणीय 5.2, कोणीय-क्ली और एक कस्टम वेबपैक में परीक्षण किया गया):

सबसे पहले, एक इंजेक्टेबल सेवा बनाएं जो विंडो का संदर्भ प्रदान करती है:

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यदि आप अपने एप्लिकेशन में इनका उपयोग करते हैं तो आप इसी तरह से और अन्य ग्लोबल्स को भी इस सेवा में जोड़ना चाह सकते हैं ।


संपादित करें: ट्रूचेंज सुझाव के साथ अपडेट किया गया। edit2: कोणीय 2.1.2 के लिए अपडेट किया गया edit3: AoT नोट्स एडिट 4 जोड़ा गया: anyप्रकार वर्कअराउंड नोट edit5 को जोड़ना: एक WindowRefService का उपयोग करने के लिए अद्यतित समाधान जो एक त्रुटि को हल करता है जो मैं एक अलग बिल्ड edit6 के साथ पिछले समाधान का उपयोग करते समय प्राप्त कर रहा था: उदाहरण कस्टम विंडो टाइपिंग जोड़ना


1
कंस्ट्रक्टर के मापदंडों में @ इजेक्ट होने से मेरे लिए त्रुटियों का एक समूह बन गया है ORIGINAL EXCEPTION: No provider for Window!। हालांकि, इसे हटाने से मेरे लिए समस्या तय हो गई। सिर्फ पहली 2 वैश्विक लाइनों का उपयोग करना मेरे लिए पर्याप्त था।
TrieuNomad

दिलचस्प ^^ मुझे कुछ और डेमो प्रोजेक्ट्स में इसे आजमाना होगा - बिना @Injectमुझे No provider for Windowत्रुटियां हुए। यह मैनुअल की जरूरत नहीं है बहुत अच्छा है @Inject!
एल्विन

2.1.2 पर मुझे इसके लिए @Inject(Window)काम करना था
जेम्स क्लेह

1
angular.io/docs/ts/latest/guide/… । ओह मेरा बुरा, ध्यान से नहीं पढ़ा
Teedeez

2
@ ब्रायन हाँ, यह अभी भी एक्सेस कर रहा है window, लेकिन बीच में सेवा के साथ यह windowयूनिट परीक्षणों में देशी सामान को बाहर निकालने की अनुमति देता है , और जैसा कि आप एसएसआर के लिए उल्लेख करते हैं, एक वैकल्पिक सेवा प्रदान की जा सकती है जो सर्वर के लिए एक नकली / नोज विंडो को उजागर करती है। एओटी का उल्लेख करने का कारण यह है कि कोणीय अद्यतन किए जाने पर एओटी में टूटी खिड़की को तोड़ने के लिए शुरुआती समाधानों में से कई हैं।
एल्विन

34

कोणीय 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 का उपयोग कर सकते हैं

संपादित करें:

AppModule का उपयोग इस तरह से main.ts में आपके एप्लिकेशन को बूटस्ट्रैप करने के लिए किया जाता है:

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

platformBrowserDynamic().bootstrapModule(AppModule)

NgModule के बारे में अधिक जानकारी के लिए कोणीय 2 प्रलेखन पढ़ें: https://angular.io/docs/ts/latest/guide/ngmodule.html


19

प्रदाता को सेट करने के बाद आप इसे इंजेक्ट कर सकते हैं:

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

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

लेकिन जब मैं window.varपृष्ठ की सामग्री को बदलता हूं तो उसमें बदलाव नहीं होता है
रविन्दर पायल

6
यह सफारी में काम नहीं करता था क्योंकि विंडो इंजेक्टेबल नहीं है। मुझे अपना खुद का इंजेक्टेबल टाइप बनाना था जिसमें विंडो के गुण थे जो मुझे चाहिए थे। एक बेहतर तरीका यह हो सकता है कि अन्य जवाबों में बताई गई सेवा बनाने के लिए
daveb

यह दृष्टिकोण काम नहीं करता है, क्योंकि useValue वास्तव में इसके लिए आपके द्वारा प्रदान किए गए मूल्य की एक प्रति बनाता है। देखें: github.com/angular/angular/issues/10788#issuecomment-300614425 । यदि आप इसे उपयोग करने के लिए उपयोग करते हैं और कॉलबैक से मान वापस करते हैं तो यह दृष्टिकोण काम करेगा।
लेवी लिंडसे

18

आप इंजेक्ट किए गए दस्तावेज़ से विंडो प्राप्त कर सकते हैं।

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

export class MyClass {

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

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

}

15

कोणीय 2.1.1 पर काम करने के लिए मुझे @Injectएक स्ट्रिंग का उपयोग करके खिड़की करना था

  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 }

10

कोणीय RC4 में निम्नलिखित कार्य जो आपके उपरोक्त app.ts में से कुछ के उत्तर का संयोजन है, इसे प्रदाता जोड़ते हैं:

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

फिर आपकी सेवा आदि में इसे कंस्ट्रक्टर में इंजेक्ट करें

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

10

@Component घोषणा से पहले, आप भी ऐसा कर सकते हैं,

declare var window: any;

कंपाइलर वास्तव में आपको तब वैश्विक विंडो चर का उपयोग करने देगा, क्योंकि आप इसे किसी भी प्रकार के साथ एक वैश्विक वैश्विक चर घोषित करते हैं।

मैं आपके एप्लिकेशन में हर जगह विंडो एक्सेस करने का सुझाव नहीं दूंगा, लेकिन आपको ऐसी सेवाओं का निर्माण करना चाहिए जो आवश्यक विंडो विशेषताओं को एक्सेस / संशोधित करें (और अपने घटकों में उन सेवाओं को इंजेक्ट करें) जो आप उन्हें संशोधित करने की अनुमति दिए बिना विंडो के साथ कर सकते हैं। संपूर्ण विंडो ऑब्जेक्ट।


यदि आप सर्वर-साइड रेंडरिंग करते हैं, तो आप कोड को तोड़ देंगे। क्योंकि साइड-साइड पर कोई विंडो ऑब्जेक्ट नहीं है, और आपको अपना खुद का इंजेक्शन लगाना होगा।
एलेक्स निकुलिन

9

मैंने 'विंडो' स्ट्रिंग के लिए OpaqueToken का उपयोग किया :

import {unimplemented} from '@angular/core/src/facade/exceptions';
import {OpaqueToken, Provider} from '@angular/core/index';

function _window(): any {
    return window;
}

export const WINDOW: OpaqueToken = new OpaqueToken('WindowToken');

export abstract class WindowRef {
    get nativeWindow(): any {
        return unimplemented();
    }
}

export class BrowserWindowRef extends WindowRef {
    constructor() {
        super();
    }
    get nativeWindow(): any {
        return _window();
    }
}


export const WINDOW_PROVIDERS = [
    new Provider(WindowRef, { useClass: BrowserWindowRef }),
    new Provider(WINDOW, { useFactory: _window, deps: [] }),
];

और इसका इस्तेमाल सिर्फ WINDOW_PROVIDERSAngular 2.0.0-rc-4 में बूटस्ट्रैप में आयात करने के लिए किया जाता है ।

लेकिन कोणीय 2.0.0-rc.5 की रिहाई के साथ मुझे एक अलग मॉड्यूल बनाने की आवश्यकता है:

import { NgModule } from '@angular/core';
import { WINDOW_PROVIDERS } from './window';

@NgModule({
    providers: [WINDOW_PROVIDERS]
})
export class WindowModule { }

और सिर्फ मेरे मुख्य की आयात संपत्ति में परिभाषित किया गया है app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { WindowModule } from './other/window.module';

import { AppComponent } from './app.component';

@NgModule({
    imports: [ BrowserModule, WindowModule ],
    declarations: [ ... ],
    providers: [ ... ],
    bootstrap: [ AppComponent ]
})
export class AppModule {}

6

आज (अप्रैल 2016) के अनुसार, पिछले समाधान में कोड काम नहीं करता है, मुझे लगता है कि विंडो को सीधे App.ts में इंजेक्ट करना संभव है और फिर उन मूल्यों को इकट्ठा करें जिनकी आपको ऐप में वैश्विक पहुंच के लिए सेवा में आवश्यकता है, लेकिन यदि आप अपनी स्वयं की सेवा बनाना और इंजेक्ट करना पसंद करते हैं, तो एक सरल तरीका यह है।

https://gist.github.com/WilldelaVega777/9afcbd6cc661f4107c2b74dd6090cebf

//--------------------------------------------------------------------------------------------------
// Imports Section:
//--------------------------------------------------------------------------------------------------
import {Injectable} from 'angular2/core'
import {window} from 'angular2/src/facade/browser';

//--------------------------------------------------------------------------------------------------
// Service Class:
//--------------------------------------------------------------------------------------------------
@Injectable()
export class WindowService
{
    //----------------------------------------------------------------------------------------------
    // Constructor Method Section:
    //----------------------------------------------------------------------------------------------
    constructor(){}

    //----------------------------------------------------------------------------------------------
    // Public Properties Section:
    //----------------------------------------------------------------------------------------------
    get nativeWindow() : Window
    {
        return window;
    }
}

6

कोणीय 4 InjectToken पेश करते हैं, और वे भी दस्तावेज़ बुलाया के लिए एक टोकन बनाने डाक्यूमेंट । मुझे लगता है कि यह आधिकारिक समाधान है और यह एओटी में काम करता है।

मैं एक ही लॉजिक का उपयोग करता हूं, जिसे एक छोटी सी लाइब्रेरी बनाने के लिए कहा जाता है, जिसे ngx-window-token कहा जाता है ।

मैं इसे अन्य परियोजना में उपयोग किया है और मुद्दों के बिना एओटी में निर्माण।

यहां बताया गया है कि मैंने इसे दूसरे पैकेज में कैसे इस्तेमाल किया

यहां प्लंकर है

आपके मॉड्यूल में

imports: [ BrowserModule, WindowTokenModule ] आपके घटक में

constructor(@Inject(WINDOW) _window) { }



4

दस्तावेज़ के माध्यम से खिड़की की वस्तु तक सीधी पहुंच का अवसर है

document.defaultView == window

4

यहाँ एक और उपाय है जो मैंने हाल ही में बनाया था, जब मैं बिल्ट-इन टोकेन 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;
        }
    });

1
इसलिए, मैंने इसे अपने प्रदाताओं फ़ोल्डर (उदाहरण के लिए) में रखा और फिर मैं अपने घटक के निर्माता को इस इंजेक्शन टोकन का उपयोग करता हूं? @Inject(WINDOW) private _window: anyऔर इसका उपयोग Angular द्वारा प्रदान किए गए DOCUMENT इंजेक्शन टोकन की तरह करें?
स्पर्कर 73

हां, बस इतना ही है।
पानीपुलिया

हाँ। यह पूरी तरह से काम करता है, इस सरल समाधान के लिए टैंक।
Sparker73

3

मुझे पता है कि सवाल यह है कि विंडो ऑब्जेक्ट को एक घटक में कैसे इंजेक्ट किया जाए, लेकिन आप ऐसा कर रहे हैं कि ऐसा लगता है कि लोकलस्टोरेज के लिए। यदि आप वास्तव में लोकलस्टोरेज चाहते हैं, तो उस सेवा का उपयोग क्यों न करें, जो h5webstorage की तरह ही उजागर होती है । फिर आप घटक इसकी वास्तविक निर्भरता का वर्णन करेंगे जो आपके कोड को अधिक पठनीय बनाता है।


2
हालांकि यह लिंक प्रश्न का उत्तर दे सकता है, लेकिन उत्तर के आवश्यक भागों को शामिल करना और संदर्भ के लिए लिंक प्रदान करना बेहतर है। लिंक-केवल उत्तर अमान्य हो सकते हैं यदि लिंक किए गए पृष्ठ बदल जाते हैं।
सभी कामगार

3

यह सबसे छोटा / साफ जवाब है जो मैंने कोणीय 4 एओटी के साथ काम करते हुए पाया है

स्रोत: https://github.com/angular/angular/issues/12631#issuecomment-274260009

@Injectable()
export class WindowWrapper extends Window {}

export function getWindow() { return window; }

@NgModule({
  ...
  providers: [
    {provide: WindowWrapper, useFactory: getWindow}
  ]
  ...
})
export class AppModule {
  constructor(w: WindowWrapper) {
    console.log(w);
  }
}


2

DOCUMENTवैकल्पिक के रूप में चिह्नित करना भी एक अच्छा विचार है । प्रति कोणीय डॉक्स:

जब एप्लिकेशन और रेंडरिंग कॉन्टेक्ट्स एक जैसे नहीं होते हैं (जैसे कि वेब वर्कर में एप्लिकेशन को रन करते समय) एप्लिकेशन संदर्भ में दस्तावेज़ उपलब्ध नहीं हो सकता है।

यहां DOCUMENTयह देखने का उपयोग करने का एक उदाहरण है कि क्या ब्राउज़र में एसवीजी समर्थन है:

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

...

constructor(@Optional() @Inject(DOCUMENT) document: Document) {
   this.supportsSvg = !!(
   document &&
   document.createElementNS &&
   document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect
);

0

के लिए @maxisam धन्यवाद NGX खिड़की-टोकन । मैं भी कुछ ऐसा ही कर रहा था, लेकिन आपका स्विच ऑफ था। यह विंडो के आकार बदलने की घटनाओं और ग्राहकों को सूचित करने के लिए सुनने के लिए मेरी सेवा है।

import { Inject, Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import { WINDOW } from 'ngx-window-token';


export interface WindowSize {
    readonly width: number;
    readonly height: number;
}

@Injectable()
export class WindowSizeService {

    constructor( @Inject(WINDOW) private _window: any ) {
        Observable.fromEvent(_window, 'resize')
        .auditTime(100)
        .map(event => <WindowSize>{width: event['currentTarget'].innerWidth, height: event['currentTarget'].innerHeight})
        .subscribe((windowSize) => {
            this.windowSizeChanged$.next(windowSize);
        });
    }

    readonly windowSizeChanged$ = new BehaviorSubject<WindowSize>(<WindowSize>{width: this._window.innerWidth, height: this._window.innerHeight});
}

छोटा और मीठा और आकर्षण की तरह काम करता है।


0

DI के माध्यम से विंडो ऑब्जेक्ट प्राप्त करना (डिपेंडेंसी इंजेक्शन) एक अच्छा विचार नहीं है जब वैश्विक चर पूरे एप्लिकेशन तक पहुंच योग्य हों।

लेकिन अगर आप विंडो ऑब्जेक्ट का उपयोग नहीं करना चाहते हैं तो आप selfकीवर्ड का भी उपयोग कर सकते हैं जो विंडो ऑब्जेक्ट को भी इंगित करता है।


3
यह अच्छी सलाह नहीं है। निर्भरता इंजेक्शन कक्षाओं (घटकों, निर्देशों, सेवाओं, पाइप, ...) का परीक्षण करना आसान बनाता है (उदाहरण के लिए एक ब्राउज़र के बिना भी) और सर्वर-साइड रेंडरिंग या वेब वर्कर्स जैसे विभिन्न प्लेटफार्मों पर पुन: उपयोग करना आसान है। यह कुछ के लिए काम कर सकता है और सादगी में कुछ अपील हो सकती है, लेकिन DI का उपयोग करने को हतोत्साहित करना IMHO एक बुरा जवाब है।
गुंटर ज़ोचबॉयर

यदि आप सर्वर-साइड रेंडरिंग करते हैं, तो आप कोड को तोड़ देंगे। क्योंकि साइड-साइड पर कोई विंडो ऑब्जेक्ट नहीं है, और आपको अपना खुद का इंजेक्शन लगाना होगा।
एलेक्स निकुलिन

-1

इसे सरल रखें, दोस्तों!

export class HeroesComponent implements OnInit {
  heroes: Hero[];
  window = window;
}

<div>{{window.Object.entries({ foo: 1 }) | json}}</div>

यदि आप सर्वर-साइड रेंडरिंग करते हैं, तो आप कोड को तोड़ देंगे। क्योंकि साइड-साइड पर कोई विंडो ऑब्जेक्ट नहीं है, और आपको अपना खुद का इंजेक्शन लगाना होगा।
एलेक्स निकुलिन

-2

वास्तव में यहां विंडो ऑब्जेक्ट को एक्सेस करना बहुत ही सरल है, यह मेरा मूल घटक है और मैंने इसका परीक्षण किया है

import { Component, OnInit,Inject } from '@angular/core';
import {DOCUMENT} from '@angular/platform-browser';

@Component({
  selector: 'app-verticalbanners',
  templateUrl: './verticalbanners.component.html',
  styleUrls: ['./verticalbanners.component.css']
})
export class VerticalbannersComponent implements OnInit {

  constructor(){ }

  ngOnInit() {
    console.log(window.innerHeight );
  }

}

यदि आप सर्वर-साइड रेंडरिंग करते हैं, तो आप कोड को तोड़ देंगे। क्योंकि साइड-साइड पर कोई विंडो ऑब्जेक्ट नहीं है, और आपको अपना खुद का इंजेक्शन लगाना होगा।
एलेक्स निकुलिन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.