Câu trả lời:
Điều này hiện đang hoạt động đối với tôi (2018-03, angle 5.2 với AoT, được thử nghiệm trong angle-cli và bản dựng webpack tùy chỉnh):
Đầu tiên, tạo một dịch vụ có thể tiêm cung cấp tham chiếu đến cửa sổ:
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();
}
}
Bây giờ, hãy đăng ký dịch vụ đó với AppModule gốc của bạn để nó có thể được đưa vào mọi nơi:
import { WindowRefService } from './window-ref.service';
@NgModule({
providers: [
WindowRefService
],
...
})
export class AppModule {}
và sau đó là nơi bạn cần tiêm 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;
}
...
Bạn cũng có thể muốn thêm nativeDocument
và các hình cầu khác vào dịch vụ này theo cách tương tự nếu bạn sử dụng chúng trong ứng dụng của mình.
chỉnh sửa: Đã cập nhật với gợi ý của Truchainz. edit2: Cập nhật cho góc 2.1.2 chỉnh sửa3: Thêm ghi chú AoT chỉnh sửa4: Thêm any
loại ghi chú giải pháp chỉnh sửa5: Giải pháp cập nhật để sử dụng WindowRefService khắc phục lỗi tôi gặp phải khi sử dụng giải pháp trước đó với một bản chỉnh sửa bản dựng khác6: thêm ví dụ nhập Window tùy chỉnh
@Inject
tôi không gặp No provider for Window
lỗi. Điều đó khá tốt không cần hướng dẫn sử dụng @Inject
!
@Inject(Window)
cái này để hoạt động
window
, nhưng với dịch vụ ở giữa, nó cho phép khai thác window
nội dung gốc trong các bài kiểm tra đơn vị và như bạn đề cập đối với SSR, một dịch vụ thay thế có thể được cung cấp để hiển thị cửa sổ giả / noop cho máy chủ. Lý do tôi đề cập đến AOT là một số giải pháp ban đầu để gói cửa sổ bị hỏng trong AOT khi Angular cập nhật.
Với việc phát hành NgModule 2.0.0-rc.5 góc cạnh đã được giới thiệu. Giải pháp trước đó đã ngừng hoạt động đối với tôi. Đây là những gì tôi đã làm để sửa nó:
app.module.ts:
@NgModule({
providers: [
{ provide: 'Window', useValue: window }
],
declarations: [...],
imports: [...]
})
export class AppModule {}
Trong một số thành phần:
import { Component, Inject } from '@angular/core';
@Component({...})
export class MyComponent {
constructor (@Inject('Window') window: Window) {}
}
Bạn cũng có thể sử dụng OpaqueToken thay vì chuỗi 'Window'
Biên tập:
AppModule được sử dụng để khởi động ứng dụng của bạn trong main.ts như sau:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
Để biết thêm thông tin về NgModule, hãy đọc tài liệu Angular 2: https://angular.io/docs/ts/latest/guide/ngmodule.html
Bạn chỉ có thể tiêm nó sau khi đã đặt nhà cung cấp:
import {provide} from 'angular2/core';
bootstrap(..., [provide(Window, {useValue: window})]);
constructor(private window: Window) {
// this.window
}
window.var
nội dung của trang không thay đổi
Bạn có thể lấy cửa sổ từ tài liệu được chèn.
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);
}
}
Để làm cho nó hoạt động trên Angular 2.1.1, tôi phải mở @Inject
cửa sổ bằng một chuỗi
constructor( @Inject('Window') private window: Window) { }
và sau đó chế nhạo nó như thế này
beforeEach(() => {
let windowMock: Window = <any>{ };
TestBed.configureTestingModule({
providers: [
ApiUriService,
{ provide: 'Window', useFactory: (() => { return windowMock; }) }
]
});
và thông thường @NgModule
tôi cung cấp nó như thế này
{ provide: 'Window', useValue: window }
Trong Angular RC4, các hoạt động sau đây là sự kết hợp của một số câu trả lời ở trên, trong ứng dụng gốc của bạn. Hãy thêm nó các nhà cung cấp:
@Component({
templateUrl: 'build/app.html',
providers: [
anotherProvider,
{ provide: Window, useValue: window }
]
})
Sau đó, trong dịch vụ của bạn, v.v. đưa nó vào hàm tạo
constructor(
@Inject(Window) private _window: Window,
)
Trước khi khai báo @Component, bạn cũng có thể làm điều đó,
declare var window: any;
Trình biên dịch thực sự sẽ cho phép bạn truy cập biến cửa sổ toàn cầu ngay bây giờ vì bạn khai báo nó như một biến toàn cục giả định với kiểu bất kỳ.
Tuy nhiên, tôi sẽ không đề xuất truy cập cửa sổ ở mọi nơi trong ứng dụng của bạn, Bạn nên tạo các dịch vụ truy cập / sửa đổi các thuộc tính cửa sổ cần thiết (và đưa các dịch vụ đó vào các thành phần của bạn) để phạm vi những gì bạn có thể làm với cửa sổ mà không cho phép họ sửa đổi toàn bộ đối tượng cửa sổ.
Tôi đã sử dụng OpaqueToken cho chuỗi 'Cửa sổ':
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: [] }),
];
Và được sử dụng chỉ để nhập WINDOW_PROVIDERS
vào bootstrap trong Angular 2.0.0-rc-4.
Nhưng với việc phát hành Angular 2.0.0-rc.5, tôi cần tạo một mô-đun riêng:
import { NgModule } from '@angular/core';
import { WINDOW_PROVIDERS } from './window';
@NgModule({
providers: [WINDOW_PROVIDERS]
})
export class WindowModule { }
và vừa được xác định trong thuộc tính nhập khẩu của 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 {}
Kể từ hôm nay (tháng 4 năm 2016), mã trong giải pháp trước đó không hoạt động, tôi nghĩ có thể đưa cửa sổ trực tiếp vào App.ts và sau đó thu thập các giá trị bạn cần vào một dịch vụ để truy cập toàn cầu trong Ứng dụng, nhưng nếu bạn thích tạo và đưa vào dịch vụ của riêng mình, một cách đơn giản hơn là giải pháp này.
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 giới thiệu InjectToken và họ cũng tạo một mã thông báo cho tài liệu có tên là DOCUMENT . Tôi nghĩ đây là giải pháp chính thức và nó hoạt động trong AoT.
Tôi sử dụng logic tương tự để tạo một thư viện nhỏ có tên ngx-window-token để ngăn việc làm này lặp đi lặp lại.
Tôi đã sử dụng nó trong dự án khác và xây dựng trong AoT mà không gặp vấn đề gì.
Đây là cách tôi sử dụng nó trong gói khác
Đây là plunker
Trong mô-đun của bạn
imports: [ BrowserModule, WindowTokenModule ]
Trong thành phần của bạn
constructor(@Inject(WINDOW) _window) { }
Nó là đủ để làm
export class AppWindow extends Window {}
và làm
{ provide: 'AppWindow', useValue: window }
để làm cho rất nhiều hạnh phúc
Có cơ hội để truy cập trực tiếp vào đối tượng của window thông qua tài liệu
document.defaultView == window
Đây là một giải pháp tôi đã đưa ra thời gian gần đây sau khi tôi đã mệt mỏi nhận được defaultView
từ DOCUMENT
built-in thẻ và kiểm tra nó cho 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
và sử dụng nó như mã thông báo tiêm DOCUMENT do Angular cung cấp?
Tôi biết câu hỏi là làm thế nào để đưa đối tượng cửa sổ vào một thành phần nhưng bạn đang làm điều này chỉ để truy cập localStorage. Nếu bạn thực sự chỉ muốn localStorage, tại sao không sử dụng một dịch vụ chỉ tiết lộ điều đó, như h5webstorage . Sau đó, thành phần của bạn sẽ mô tả các phụ thuộc thực sự của nó để làm cho mã của bạn dễ đọc hơn.
Đây là câu trả lời ngắn gọn / rõ ràng nhất mà tôi thấy khi làm việc với Angular 4 AOT
Nguồn: 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);
}
}
Bạn có thể sử dụng NgZone trên Angular 4:
import { NgZone } from '@angular/core';
constructor(private zone: NgZone) {}
print() {
this.zone.runOutsideAngular(() => window.print());
}
Bạn cũng nên đánh dấu DOCUMENT
là tùy chọn. Theo tài liệu Angular:
Tài liệu có thể không khả dụng trong Bối cảnh ứng dụng khi Ứng dụng và Khung kết xuất không giống nhau (ví dụ: khi chạy ứng dụng vào Web Worker).
Dưới đây là một ví dụ về việc sử dụng DOCUMENT
để xem liệu trình duyệt có hỗ trợ SVG hay không:
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 cảm ơn vì ngx-window-token . Tôi đã làm một cái gì đó tương tự nhưng chuyển sang của bạn. Đây là dịch vụ của tôi để lắng nghe các sự kiện thay đổi kích thước cửa sổ và thông báo cho người đăng ký.
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});
}
Ngắn gọn và ngọt ngào và hoạt động như một sự quyến rũ.
Lấy đối tượng cửa sổ thông qua DI (Dependency Injection) không phải là một ý tưởng hay khi các biến toàn cục có thể truy cập được trong toàn bộ ứng dụng.
Nhưng nếu bạn không muốn sử dụng đối tượng cửa sổ thì bạn cũng có thể sử dụng self
từ khóa cũng trỏ đến đối tượng cửa sổ.
Hãy giữ nó đơn giản, các bạn!
export class HeroesComponent implements OnInit {
heroes: Hero[];
window = window;
}
<div>{{window.Object.entries({ foo: 1 }) | json}}</div>
Trên thực tế, nó rất đơn giản để truy cập đối tượng cửa sổ ở đây là thành phần cơ bản của tôi và tôi đã thử nghiệm hoạt động của nó
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!
. Tuy nhiên, việc loại bỏ nó đã khắc phục sự cố cho tôi. Tôi chỉ sử dụng 2 dòng toàn cầu đầu tiên là đủ.