Bir hizmete pencere nasıl enjekte edilir?


111

TypeScript'te yararlanacak bir Angular 2 servisi yazıyorum localstorage. windowAngular 1.x gibi herhangi bir global değişkene başvurmak istemediğim için hizmetime tarayıcı nesnesine bir referans eklemek istiyorum $window.

Bunu nasıl yaparım?

Yanıtlar:


135

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

İlk olarak, pencereye 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 hizmeti kök AppModule'ünüze kaydedin, böylece her yere enjekte edilebilir:

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

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

ve daha sonra enjekte etmeniz gereken yere 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;
    }
...

Bunları uygulamanızda nativeDocumentkullanırsanız, bu hizmete ve diğer küreselleri de benzer şekilde eklemek isteyebilirsiniz .


düzenleme: Truchainz önerisiyle güncellendi. edit2: Açısal 2.1.2 için güncellendi edit3: Eklendi AoT notları edit4: anyTip geçici çözüm notu ekleme edit5: Önceki çözümü farklı bir yapı düzenlemesiyle kullanırken aldığım bir hatayı düzelten bir WindowRefService kullanmak için güncellendi çözüm6: örnek özel Pencere yazma ekleme


1
Oluşturucu parametrelerinde @Inject'e sahip olmak, benim için ORIGINAL EXCEPTION: No provider for Window!. Ancak, onu kaldırmak benim için sorunu çözdü. Sadece ilk 2 global hattı kullanmak benim için yeterliydi.
TrieuNomad

^^ İlginç bir çift daha demo projelerde denemek zorunda kalacak - olmadan @Injectben başlamıştı No provider for Windowhataları. Kılavuza ihtiyaç duymamak çok güzel @Inject!
elwyn

2.1.2'de bunun @Inject(Window)işe yaraması için kullanmak zorunda kaldım
James Kleeh

1
angular.io/docs/ts/latest/guide/… . Oh benim hatam, dikkatli
okumadım

2
@Brian evet, hala erişiyor window, ancak aradaki hizmet, windowbirim testlerinde yerel öğelerin çıkmasına izin veriyor ve SSR için de belirttiğiniz gibi, sunucu için bir sahte / noop penceresi ortaya çıkaran alternatif bir hizmet sağlanabilir. AOT'den bahsetmemin nedeni, Angular güncellendiğinde AOT'de pencere sarma için ilk çözümlerin birkaçının kırılmasıdır.
elwyn

34

Açısal 2.0.0-rc.5 NgModule'un piyasaya sürülmesiyle birlikte tanıtıldı. Önceki çözüm benim için çalışmayı bıraktı. Bunu düzeltmek için yaptığım şey buydu:

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

'Pencere' dizesi yerine bir OpaqueToken da kullanabilirsiniz.

Düzenle:

AppModule, uygulamanızı main.ts'de şu şekilde önyüklemek 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


19

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
}

ancak window.varsayfanın içeriğini değiştirdiğimde değişmiyor
Ravinder Payal

6
Bu, Windows Enjekte Edilebilir olmadığından Safari'de çalışmadı. İstediğim Window özelliklerini içeren kendi Enjekte Edilebilir tipimi yaratmak zorunda kaldım. Diğer cevaplarda açıklandığı gibi bir hizmet oluşturmak daha iyi bir yaklaşım olabilirdi
daveb

Bu yaklaşım işe yaramaz, çünkü useValue aslında ona sağladığınız değerin bir kopyasını oluşturur. Bakınız: github.com/angular/angular/issues/10788#issuecomment-300614425 . UseFactory'yi kullanmak için değiştirirseniz ve geri aramadan değeri döndürürseniz, bu yaklaşım işe yarayacaktır.
Levi Lindsey

19

Enjekte edilen belgeden pencere alabilirsiniz.

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

Angular 2.1.1'de çalışmasını sağlamak @Injectiçin bir dizge kullanarak pencereye gitmem gerekiyordu

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

ve sonra böyle alay et

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

ve sıradan olarak @NgModulebunu böyle sağlıyorum

{ provide: 'Window', useValue: window }

10

Angular RC4'te, yukarıdaki cevaplardan bazılarının bir kombinasyonu olan aşağıdaki çalışır, kök uygulamanızda sağlayıcıları ekleyin:

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

Sonra hizmetinizde vb kurucuya enjekte edin

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

10

@Component bildiriminden önce bunu da yapabilirsiniz,

declare var window: any;

Herhangi bir tipte varsayılan bir global değişken olarak tanımladığınızdan, derleyici aslında global pencere değişkenine erişmenize izin verecektir.

Yine de uygulamanızın her yerinden pencereye erişmenizi önermem. Pencereyle yapabileceklerinizi, pencereyi değiştirmelerine izin vermeden kapsamak için gerekli pencere özniteliklerine erişen / bunları değiştiren (ve bileşenlerinize bu hizmetleri enjekte eden) hizmetler oluşturmalısınız. tüm pencere nesnesi.


Sunucu tarafı işleme yaparsanız, kodunuz kırılır çünkü srver tarafında herhangi bir pencere nesneniz yoktur ve kendinizinkini enjekte etmeniz gerekir.
Alex Nikulin

9

OpaqueToken'ı 'Pencere' dizesi için kullandım :

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: [] }),
];

Ve sadece WINDOW_PROVIDERSAngular 2.0.0-rc-4'te bootstrap'e aktarmak için kullanılır .

Ancak Angular 2.0.0-rc.5 sürümüyle birlikte ayrı bir modül oluşturmam gerekiyor:

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

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

ve sadece ana sayfamın ithal malında tanımlanmış 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 {}

7

Angular 4, InjectToken'ı sunar ve ayrıca DOCUMENT adlı belge için bir belirteç oluşturur . Bence bu resmi çözüm ve AoT'de çalışıyor.

Bunu defalarca yapmamak için ngx-window-token adlı küçük bir kitaplık oluşturmak için aynı mantığı kullanıyorum .

Bunu başka bir projede kullandım ve AoT'de sorunsuz bir şekilde inşa ettim.

İşte diğer pakette nasıl kullandım

İşte dalgıç

Senin modülünde

imports: [ BrowserModule, WindowTokenModule ] Bileşeninizde

constructor(@Inject(WINDOW) _window) { }


6

Bugün (Nisan 2016) itibariyle, önceki çözümdeki kod çalışmıyor, pencereyi doğrudan Uygulamalara enjekte etmenin ve ardından ihtiyacınız olan değerleri Uygulamada küresel erişim için bir hizmette toplamanın mümkün olduğunu düşünüyorum, ancak Kendi hizmetinizi oluşturmayı ve enjekte etmeyi tercih ediyorsanız, bu daha basit bir çözümdür.

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

5

İşte defaultView, DOCUMENTyerleşik belirteçten almaktan ve boş olup olmadığını kontrol etmekten yorulduktan sonra yakın zamanda bulduğum başka bir çözüm :

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
Yani, bunu sağlayıcılar klasörüme (örneğin) koyuyorum ve sonra bileşenimin yapıcısında bu enjeksiyon belirtecini kullanıyorum? @Inject(WINDOW) private _window: anyve Angular tarafından sağlanan DOCUMENT enjeksiyon jetonu gibi kullanın.
Sparker73

Evet, hepsi bu kadar.
waterplea

Evet. Bu basit çözüm için tanklar mükemmel çalışıyor.
Sparker73

4

Yapman yeterli

export class AppWindow extends Window {} 

ve yap

{ provide: 'AppWindow', useValue: window } 

AOT'yi mutlu etmek için


4

Belge üzerinden pencerenin nesnesine doğrudan erişim imkanı var

document.defaultView == window

3

Sorunun pencere nesnesinin bir bileşene nasıl enjekte edileceğini biliyorum, ancak bunu sadece localStorage'a ulaşmak için yapıyorsunuz, öyle görünüyor. Gerçekten sadece localStorage istiyorsanız, neden h5webstorage gibi tam da bunu ortaya çıkaran bir hizmet kullanmıyorsunuz ? Daha sonra bileşeniniz, kodunuzu daha okunaklı hale getiren gerçek bağımlılıklarını tanımlayacaktır.


2
Bu bağlantı soruyu cevaplayabilirken, cevabın temel kısımlarını buraya eklemek ve referans için bağlantıyı sağlamak daha iyidir. Bağlantılı sayfa değişirse, yalnızca bağlantı yanıtları geçersiz hale gelebilir.
Tüm Çalışanlar Önemlidir

3

Bu, Angular 4 AOT ile çalışırken bulduğum en kısa / en temiz cevap

Kaynak: 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

NgZone'u Angular 4'te kullanabilirsiniz:

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

constructor(private zone: NgZone) {}

print() {
    this.zone.runOutsideAngular(() => window.print());
}

2

Ayrıca DOCUMENTisteğe bağlı olarak işaretlemek de iyi bir fikirdir . Angular belgelerine göre:

Uygulama ve Rendering Contexts aynı olmadığında (örneğin, uygulamayı bir Web Worker'da çalıştırırken), Belge Uygulama Bağlamında mevcut olmayabilir.

DOCUMENTTarayıcının SVG desteğine sahip olup olmadığını görmek için burada bir örnek verilmiştir :

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-window-token için teşekkürler . Ben de benzer bir şey yapıyordum ama seninkine geçtim. Bu, pencere yeniden boyutlandırma olaylarını dinlemek ve aboneleri bilgilendirmek için benim hizmetimdir.

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

Kısa ve tatlıdır ve bir cazibe gibi çalışır.


0

DI (Bağımlılık Enjeksiyonu) aracılığıyla pencere nesnesi almak, uygulama boyunca global değişkenlere erişilebilir olduğunda iyi bir fikir değildir.

Ancak pencere nesnesini kullanmak istemiyorsanız, pencere nesnesine selfde işaret eden anahtar sözcüğü de kullanabilirsiniz .


4
Bu iyi bir tavsiye değil. Bağımlılık Enjeksiyonu, sınıfların (bileşenler, yönergeler, hizmetler, borular, ...) test edilmesini (örneğin, tarayıcı olmadan bile) ve sunucu tarafı oluşturma veya Web Çalışanları gibi farklı platformlarda yeniden kullanımını kolaylaştırır. Bazıları için işe yarayabilir ve basitlik bir miktar çekiciliğe sahip olabilir, ancak DI kullanmanın cesaretini kırmak IMHO'yu kötü bir cevaptır.
Günter Zöchbauer

Sunucu tarafı işleme yaparsanız, kodunuz kırılır çünkü srver tarafında herhangi bir pencere nesneniz yoktur ve kendinizinkini enjekte etmeniz gerekir.
Alex Nikulin

-1

Basit tutun millet!

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

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

Sunucu tarafı işleme yaparsanız, kodunuz kırılır çünkü srver tarafında herhangi bir pencere nesneniz yoktur ve kendinizinkini enjekte etmeniz gerekir.
Alex Nikulin

-2

Aslında buradaki pencere nesnesine erişmek çok basit ve temel bileşenim ve çalıştığını test ettim

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

}

Sunucu tarafı işleme yaparsanız, kodunuz kırılır çünkü srver tarafında herhangi bir pencere nesneniz yoktur ve kendinizinkini enjekte etmeniz gerekir.
Alex Nikulin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.