Programing

서비스에 창을 삽입하는 방법은 무엇입니까?

crosscheck 2020. 8. 9. 09:48
반응형

서비스에 창을 삽입하는 방법은 무엇입니까?


.NET을 사용할 TypeScript에서 Angular 2 서비스를 작성하고 있습니다 localstorage. windowAngular 1.x와 같은 전역 변수를 참조하고 싶지 않기 때문에 브라우저 개체에 대한 참조를 서비스 에 삽입하고 싶습니다 $window.

어떻게하나요?


이것은 현재 나를 위해 일하고 있습니다 (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 : 예제 사용자 지정 창 입력 추가


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

문자열 'Window'대신 OpaqueToken을 사용할 수도 있습니다.

편집하다:

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
}

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 }

삽입 된 문서에서 창을 가져올 수 있습니다.

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

}

'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_PROVIDERSAngular 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 RC4에서는 위의 답변 중 일부가 조합 된 다음 작업이 루트 app.ts에서 공급자를 추가합니다.

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

그런 다음 서비스 등에서 생성자에 삽입하십시오.

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

@Component 선언 전에도 그렇게 할 수 있습니다.

declare var window: any;

컴파일러는 실제로 전역 창 변수에 액세스 할 수 있도록합니다. 이는 any 유형의 가정 된 전역 변수로 선언하기 때문입니다.

응용 프로그램의 모든 곳에서 창에 액세스하는 것은 권장하지 않습니다. 필요한 창 속성에 액세스 / 수정하는 서비스를 만들고 구성 요소에 해당 서비스를 삽입하여 창으로 수행 할 수있는 작업을 수정하지 않고 범위를 지정해야합니다. 전체 창 개체.


오늘 (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;
    }
}

Angular 4는 InjectToken을 도입했으며 DOCUMENT 라는 문서에 대한 토큰도 만듭니다 . 이것이 공식 솔루션이라고 생각하며 AoT에서 작동합니다.

동일한 논리를 사용하여 ngx-window-token 이라는 작은 라이브러리를 만들어이 작업을 반복적 으로 방지합니다.

다른 프로젝트에서 사용하고 문제없이 AoT에서 빌드했습니다.

다른 패키지 에서 사용한 방법은 다음과 같습니다.

여기 플런 커가 있습니다

모듈에서

imports: [ BrowserModule, WindowTokenModule ] 구성 요소에서

constructor(@Inject(WINDOW) _window) { }


문서를 통해 창 개체에 직접 액세스 할 수있는 기회가 있습니다.

document.defaultView == window

질문은 구성 요소에 창 개체를 삽입하는 방법이지만 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);
  }
}

할 정도로 충분합니다

export class AppWindow extends Window {} 

그리고

{ provide: 'AppWindow', useValue: window } 

AOT를 행복하게 만들기 위해


Angular 4에서 NgZone을 사용할 수 있습니다.

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

constructor(private zone: NgZone) {}

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

DOCUMENT을 선택 사항 으로 표시하는 것도 좋은 생각 입니다. Angular 문서에 따라 :

Document might not be available in the Application Context when Application and Rendering Contexts are not the same (e.g. when running the application into a Web Worker).

Here's an example of using the DOCUMENT to see whether the browser has SVG support:

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 thanks for ngx-window-token. I was doing something similar but switched to yours. This is my service for listening to window resize events and notifying subscribers.

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

Short and sweet and works like a charm.


Getting window object via DI(Dependency Injection) is not a good idea when global variables are accessible throughout the application.

But if you don't want to use window object then you can also use self keyword which also points to window object.


Here's another solution I came up recently after I got tired of getting defaultView from DOCUMENT built-in token and checking it for 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;
        }
    });

Keep it simple, folks!

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

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

Actually its very simple to access window object here is my basic component and i tested it its working

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

}

참고URL : https://stackoverflow.com/questions/34177221/how-to-inject-window-into-a-service

반응형