2016-02-19 7 views
8

Попытка вынести размер окна на окно изменения размера через поток в угловом 2 компонента с использованием в async трубы:«асинхронной» труба не оказывающего обновления потока

<h2>Size: {{size$ | async | json}}</h2>

const windowSize$ = new BehaviorSubject(getWindowSize()); 
Observable.fromEvent(window, 'resize') 
    .map(getWindowSize) 
    .subscribe(windowSize$); 

function getWindowSize() { 
    return { 
    height: window.innerHeight, 
    width: window.innerWidth 
    }; 
} 

@Component({ 
    selector: 'my-app', 
    providers: [], 
    template: ` 
    <div> 
     <h2>Size: {{size$ | async | json}}</h2> 
    </div> 
    `, 
    directives: [] 
}) 
export class App { 
    size$ = windowSize$.do(o => console.log('size:', o)); 
    constructor() { } 
} 

Но только компонент отображает исходное состояние и игнорирует обновления потока. Если вы откроете консоль, при изменении размера окна вы увидите обновления из того же потока.

Не могу понять, что мне не хватает здесь.

Вот plunker

ответ

9

Обработчик событий работает вне зоны угловой, так обнаружение Углового изменения не запускается, когда срабатывает событие. Поместите обработчик событий внутри вашего компонента, а затем он получит обезьянью заплату вместе со всеми другими асинхронными событиями, следовательно, Угловое обнаружение изменения будет выполняться после каждого события (и обновить вид):

ngOnInit() { 
    Observable.fromEvent(window, 'resize') 
    .map(getWindowSize) 
    .subscribe(windowSize$); 
} 

Plunker


Другой вариант, обсуждается в комментариях, является manually run change detection, когда вид модели обновляется:

import {Component, ChangeDetectorRef} from 'angular2/core' 
... 
export class App { 
    size$ = windowSize$.do(o => { 
    console.log('size:', o); 
    // since the resize event was not registered while inside the Angular zone, 
    // we need to manually run change detection so that the view will update 
    this._cdr.detectChanges(); 
    }); 

    constructor(private _cdr: ChangeDetectorRef) {} 
} 

Plunker

Обратите внимание, что вы можете вместо этого хотите попробовать запустить ApplicationRef.tick() один раз, скажем, в корневой компонент, который будет выполняться обнаружение изменений на все компоненты – а не работает ChangeDetectorRef.detectChanges() в каждом компоненте. (И вам, возможно, понадобится обернуть tick() внутри метода setTimeout(), чтобы убедиться, что все модели представления компонентов были обновлены ... Я не уверен, что все методы обратного вызова do() будут выполнены, то есть, если все они будут выполняться за один оборот JavaScript VM, или если задействовано несколько витков.)

+0

Хех, это определенно решает его. Чтение небольших зон просрочено. Но в моей реализации я хочу отвлечь поток размера в другом файле (без компонента, без ngOnInit) и выставить отображаемые высоты $ и ширину $ streams для всех компонентов, которые нужно прикрепить по мере необходимости. Как я могу сообщить зонам об этих обновлениях потока? – Birowsky

+2

@Birowsky, одним из способов было бы вручную запустить обнаружение изменений после изменения модели просмотра: http://stackoverflow.com/a/35106069/215945. Вот плункер, показывающий, что: http: // plnkr.co/edit/eiua3K9aRRFEyxV3vbtP? p = preview –

12

Поскольку моя цель состояла в том, чтобы иметь возможность абстрагировать потоки размера окна в другом модуле, по-видимому, просто обертывая потоки в классе, запечатавшем сделку:

"Это будущее" версия:

"Бабуля" ве rsion:

import {Observable, BehaviorSubject} from 'rxjs'; 

export class WindowSize { 
    width$: Observable<number>; 
    height$: Observable<number>; 

    constructor() { 
     let windowSize$ = new BehaviorSubject(getWindowSize()); 
     this.width$ = (windowSize$.pluck('width') as Observable<number>).distinctUntilChanged(); 
     this.height$ = (windowSize$.pluck('height') as Observable<number>).distinctUntilChanged(); 

     Observable.fromEvent(window, 'resize') 
      .map(getWindowSize) 
      .subscribe(windowSize$); 
    } 
} 

function getWindowSize() { 
    return { 
     height: window.innerHeight, 
     width: window.innerWidth 
    }; 
} 

Хотя я не хотел класса/услуги в этом модуле, просто очистить/независимую от платформы конструкции, это была единственным чистый способ, который работал на угловой без необходимости заботиться о запуске обновления зоны.

+1

Хорошее решение. Мне любопытно, что '@Injectable()' требуется, чтобы заставить это работать? –

+0

@MarkRajcok его необходимо, потому что он добавил его в массив бутстрапов как услугу. –

+2

@PankajParkar, если служба не зависит от других сервисов, декоратор '@Injectable()' не требуется (даже если вы его поместите внутри массива 'bootstrap()'). Поэтому мой вопрос все еще стоит. Мне все еще интересно/спрашивать, не является ли этот случай использования другим. Моя догадка заключается в том, что здесь не требуется '@ Injectable'. (Тем не менее, я знаю, что лучше всего всегда включать декоратор '@ Injectable'.) –