Відповіді:
Наразі це працює для мене (2018-03, кутовий 5.2 з AoT, випробуваний у 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();
}
}
Тепер зареєструйте цю службу у вашому root 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 редагування3: Додано примітки AoT edit4: Додавання any
типу обхідної примітки edit5: оновлене рішення для використання WindowRefService, яке виправляє помилку, яку я отримував при використанні попереднього рішення з іншою збіркою edit6: додавання прикладу користувацького введення вікна
@Inject
мене не виникали No provider for Window
помилки. Це дуже приємно, що не потрібна інструкція @Inject
!
@Inject(Window)
для цього роботу
window
, але завдяки сервісі між ними він дозволяє виконувати стимуляцію натільних window
речей в тестових одиницях, і, як ви згадуєте для SSR, може бути надана альтернативна послуга, яка виявляє макет / noop вікно для сервера. Причина, яку я згадую про AOT, - це декілька ранніх рішень для обгортання вікон, які вийшли з ладу 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 замість рядка "Вікно"
Редагувати:
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 }
У Angular RC4 наступні роботи, що є комбінацією деяких з вищезазначених відповідей, у вашому root 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.
Я б не пропонував доступ до вікна скрізь у вашій програмі. Ви повинні створити сервіси, які отримують доступ / змінюють необхідні атрибути вікна (і вводять ці служби у ваші компоненти), щоб охоплювати все, що ви можете зробити з цим вікном, не дозволяючи їм змінювати цілий предмет вікна.
Я використовував OpaqueToken для рядка "Window":
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 { }
і щойно визначений у моєму імпорті головного 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 {}
Станом на сьогодні (квітень 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;
}
}
Кутовий 4 вводить InjectToken, і вони також створюють маркер для документа під назвою ДОКУМЕНТ . Я думаю, що це офіційне рішення, і воно працює в AoT.
Я використовую ту саму логіку, щоб створити невелику бібліотеку під назвою ngx-window-token, щоб не робити цього знову і знову.
Я використовував його в іншому проекті і без проблем будував AoT.
Ось як я використовував його в іншій упаковці
Ось плункер
У вашому модулі
imports: [ BrowserModule, WindowTokenModule ]
У вашому компоненті
constructor(@Inject(WINDOW) _window) { }
Ось ще рішення , яке я придумав недавно після того, як я втомився отримувати 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;
}
});
@Inject(WINDOW) private _window: any
і використовувати його як DOKUMENT маркер для ін'єкцій, наданий Angular?
Це досить зробити
export class AppWindow extends Window {}
і робити
{ provide: 'AppWindow', useValue: window }
зробити АОТ щасливим
Є можливість прямого доступу до об’єкта вікна через документ
document.defaultView == window
Я знаю, питання полягає в тому, як вставити об’єкт вікна в компонент, але ви робите це лише для того, щоб дістатися до localStorage. Якщо ви справді просто хочете localStorage, чому б не скористатись службою, яка виставляє саме це, як h5webstorage . Потім компонент опише його реальні залежності, що зробить ваш код більш читабельним.
Це найкоротший / найчистіший відповідь, який я знайшов, працюючи з Angular 4 AOT
Джерело: https://github.com/angular/angular/isissue/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);
}
}
Ви можете використовувати NgZone на Angular 4:
import { NgZone } from '@angular/core';
constructor(private zone: NgZone) {}
print() {
this.zone.runOutsideAngular(() => window.print());
}
Також непогано позначити DOCUMENT
як необов'язкове. За кутовими документами:
Документ може бути недоступним у контексті програми, коли контексти програми та візуалізації не однакові (наприклад, під час запуску програми у веб-роботодавця).
Ось приклад використання значка, 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 глобальних ліній було мені достатньо.