답변:
이것은 현재 나를 위해 일하고 있습니다 (2018-03, AoT가있는 angular 5.2, angular-cli 및 사용자 정의 웹 팩 빌드에서 테스트 됨).
먼저 창에 대한 참조를 제공하는 주입 가능한 서비스를 만듭니다.
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 제안으로 업데이트되었습니다. edit2 : angular 2.1.2에 대해 업데이트 됨 edit3 : AoT 노트 추가 edit4 : any
유형 해결 방법 note edit5 : 다른 빌드로 이전 솔루션을 사용할 때 발생하던 오류를 수정하는 WindowRefService를 사용하도록 업데이트 된 솔루션 edit6 : 예제 사용자 지정 창 타이핑 추가
@Inject
내가지고 있다고 No provider for Window
오류. 설명서가 필요하지 않은 것은 꽤 좋습니다 @Inject
!
@Inject(Window)
이 작업을 수행하려면
window
중이지만 그 사이에 서비스를 사용하면 window
단위 테스트에서 기본 항목을 스터 빙 할 수 있으며 SSR에 대해 언급 했듯이 서버에 대한 mock / noop 창을 노출하는 대체 서비스를 제공 할 수 있습니다. AOT를 언급 한 이유는 Angular가 업데이트 될 때 AOT에서 창을 래핑하는 초기 솔루션 중 일부입니다.
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
제공자를 설정 한 후에 주입 할 수 있습니다.
import {provide} from 'angular2/core';
bootstrap(..., [provide(Window, {useValue: window})]);
constructor(private window: Window) {
// this.window
}
window.var
페이지 내용을 변경해도 변경되지 않습니다
삽입 된 문서에서 창을 가져올 수 있습니다.
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);
}
}
Angular 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 }
@Component 선언 전에도 그렇게 할 수 있습니다.
declare var window: any;
컴파일러는 실제로 전역 창 변수에 액세스 할 수 있도록합니다. 이는 any 유형의 가정 된 전역 변수로 선언하기 때문입니다.
응용 프로그램의 모든 곳에서 창에 액세스하는 것은 권장하지 않습니다. 필요한 창 속성에 액세스 / 수정하는 서비스를 만들고 구성 요소에 해당 서비스를 삽입하여 창으로 할 수있는 작업을 수정하지 않고 범위를 지정해야합니다. 전체 창 개체.
'Window'문자열에 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_PROVIDERS
Angular 2.0.0-rc-4의 부트 스트랩에서 .
하지만 Angular 2.0.0-rc.5가 출시되면서 별도의 모듈을 만들어야합니다.
import { NgModule } from '@angular/core';
import { WINDOW_PROVIDERS } from './window';
@NgModule({
providers: [WINDOW_PROVIDERS]
})
export class WindowModule { }
내 메인의 imports 속성에 정의되어 있습니다. 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 {}
Angular 4는 InjectToken을 도입하고 DOCUMENT 라는 문서에 대한 토큰도 만듭니다 . 나는 이것이 공식적인 해결책이라고 생각하며 AoT에서 작동합니다.
동일한 논리를 사용하여 ngx-window-token 이라는 작은 라이브러리를 만들어이 작업을 계속해서 방지합니다.
다른 프로젝트에서 사용하고 문제없이 AoT에서 빌드했습니다.
다른 패키지 에서 사용한 방법은 다음과 같습니다.
여기 플런 커가 있습니다
모듈에서
imports: [ BrowserModule, WindowTokenModule ]
구성 요소에서
constructor(@Inject(WINDOW) _window) { }
오늘 (2016 년 4 월) 현재 이전 솔루션의 코드가 작동하지 않습니다. 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;
}
}
다음 defaultView
은 DOCUMENT
내장 토큰 에서 가져 와서 null을 확인하는 데 지친 후 최근에 나온 또 다른 솔루션입니다 .
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;
}
});
@Inject(WINDOW) private _window: any
Angular에서 제공하는 DOCUMENT 주입 토큰처럼 사용합니까?
할 정도로 충분합니다
export class AppWindow extends Window {}
그리고
{ provide: 'AppWindow', useValue: window }
AOT를 행복하게 만들기 위해
질문은 구성 요소에 창 개체를 주입하는 방법이지만 localStorage에 도달하기 위해 이것을 수행하는 것입니다. 정말로 localStorage를 원한다면 h5webstorage 와 같이 노출하는 서비스를 사용하지 마십시오 . 그런 다음 구성 요소가 코드를 더 읽기 쉽게 만드는 실제 종속성을 설명합니다.
이것은 Angular 4 AOT로 작업 한 가장 짧고 깨끗한 답변입니다.
출처 : 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);
}
}
DOCUMENT
를 선택 사항 으로 표시하는 것도 좋은 생각 입니다. Angular 문서에 따라 :
애플리케이션과 렌더링 컨텍스트가 동일하지 않은 경우 (예 : 애플리케이션을 웹 작업자로 실행하는 경우) 애플리케이션 컨텍스트에서 문서를 사용할 수 없습니다.
다음 DOCUMENT
은 브라우저에서 SVG 지원 여부를 확인 하기 위해 를 사용하는 예입니다 .
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
);
@maxisam ngx-window-token 주셔서 감사합니다 . 나는 비슷한 일을했지만 당신의 것으로 바꿨습니다. 이것은 창 크기 조정 이벤트를 듣고 구독자에게 알리는 서비스입니다.
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});
}
짧고 달콤하고 매력처럼 작동합니다.
DI (Dependency Injection)를 통해 창 개체를 가져 오는 것은 응용 프로그램 전체에서 전역 변수에 액세스 할 수있는 경우 좋은 생각이 아닙니다.
그러나 창 개체를 사용하지 않으려면 창 개체를 self
가리키는 키워드 도 사용할 수 있습니다 .
여러분, 간단하게 유지하세요!
export class HeroesComponent implements OnInit {
heroes: Hero[];
window = window;
}
<div>{{window.Object.entries({ foo: 1 }) | json}}</div>
실제로 여기 창 개체에 액세스하는 것이 매우 간단합니다. 기본 구성 요소이며 작동하는지 테스트했습니다.
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 );
}
}
ORIGINAL EXCEPTION: No provider for Window!
. 그러나 그것을 제거하면 문제가 해결되었습니다. 처음 2 개의 글로벌 라인 만 사용하면 충분했습니다.