Skip to content
This repository has been archived by the owner on Jan 6, 2025. It is now read-only.

Commit

Permalink
fix(matchMediaObservable): expose observable for rxjs operators
Browse files Browse the repository at this point in the history
* rename token to 'ObservableMediaService'
* rename MatchMediaObservableProvider to ObservableMediaServiceProvider
* add method `.asObservable()` to MediaService

fixes #125.
  • Loading branch information
ThomasBurleson committed Jan 26, 2017
1 parent 0ca7d07 commit 8ca159e
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {Subscription} from "rxjs/Subscription";
import 'rxjs/add/operator/filter';

import {MediaChange} from "../../../lib/media-query/media-change";
import {MatchMediaObservable} from "../../../lib/media-query/match-media";
import {ObservableMediaService} from "../../../lib/media-query/match-media";

@Component({
selector: 'demo-responsive-flex-directive',
Expand Down Expand Up @@ -32,7 +32,7 @@ export class DemoResponsiveFlexDirectives implements OnInit, OnDestroy {
private _watcher : Subscription;
public activeMediaQuery = "";

constructor(@Inject(MatchMediaObservable) private _media$) { }
constructor(@Inject(ObservableMediaService) private _media$) { }

ngOnInit() {
this._watcher = this.watchMQChanges();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {Subscription} from "rxjs/Subscription";
import 'rxjs/add/operator/filter';

import {MediaChange} from "../../../lib/media-query/media-change";
import {MatchMediaObservable} from "../../../lib/media-query/match-media";
import {ObservableMediaService} from "../../../lib/media-query/match-media";

@Component({
selector: 'demo-responsive-flex-order',
Expand Down Expand Up @@ -44,7 +44,7 @@ export class DemoResponsiveFlexOrder implements OnInit, OnDestroy {
public activeMediaQuery = "";
private _watcher : Subscription;

constructor(@Inject(MatchMediaObservable) private _media$) { }
constructor(@Inject(ObservableMediaService) private _media$) { }

ngOnInit() {
this._watcher = this.watchMQChanges();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {Subscription} from "rxjs/Subscription";
import 'rxjs/add/operator/filter';

import {MediaChange} from "../../../lib/media-query/media-change";
import {MatchMediaObservable} from "../../../lib/media-query/match-media";
import {ObservableMediaService} from "../../../lib/media-query/match-media";

@Component({
selector: 'demo-responsive-row-column',
Expand Down Expand Up @@ -64,7 +64,7 @@ export class DemoResponsiveRows implements OnDestroy {

isVisible = true;

constructor(@Inject(MatchMediaObservable) private _media$) {
constructor(@Inject(ObservableMediaService) private _media$) {
this._watcher = this._media$
.subscribe((e:MediaChange) => {
this._activeMQC = e;
Expand Down
5 changes: 3 additions & 2 deletions src/lib/flexbox/api/hide.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {CommonModule} from '@angular/common';
import {ComponentFixture, TestBed} from '@angular/core/testing';

import {MockMatchMedia} from '../../media-query/mock/mock-match-media';
import {MatchMedia, MatchMediaObservable} from '../../media-query/match-media';
import {MatchMedia} from '../../media-query/match-media';
import {ObservableMediaService} from '../../media-query/observable-media-service';
import {BreakPointsProvider} from '../../media-query/providers/break-points-provider';
import {BreakPointRegistry} from '../../media-query/breakpoints/break-point-registry';

Expand Down Expand Up @@ -258,7 +259,7 @@ export class TestHideComponent implements OnInit {
isHidden = true;
menuHidden = true;

constructor(@Inject(MatchMediaObservable) private media) {
constructor(@Inject(ObservableMediaService) private media) {
}

toggleMenu() {
Expand Down
5 changes: 3 additions & 2 deletions src/lib/flexbox/api/show.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import {Component, OnInit, Inject} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ComponentFixture, TestBed} from '@angular/core/testing';

import {MatchMedia} from '../../media-query/match-media';
import {MockMatchMedia} from '../../media-query/mock/mock-match-media';
import {MatchMedia, MatchMediaObservable} from '../../media-query/match-media';
import {ObservableMediaService} from '../../media-query/observable-media-service';
import {BreakPointsProvider} from '../../media-query/providers/break-points-provider';
import {BreakPointRegistry} from '../../media-query/breakpoints/break-point-registry';
import {FlexLayoutModule} from '../_module';
Expand Down Expand Up @@ -214,7 +215,7 @@ export class TestShowComponent implements OnInit {
isHidden = false;
menuOpen: boolean = true;

constructor(@Inject(MatchMediaObservable) private media) {
constructor(@Inject(ObservableMediaService) private media) {
}

toggleMenu() {
Expand Down
4 changes: 2 additions & 2 deletions src/lib/media-query/_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {BreakPointsProvider} from "./providers/break-points-provider";

import {MatchMedia} from './match-media';
import {MediaMonitor} from './media-monitor';
import {MatchMediaObservableProvider} from './providers/match-media-observable-provider';
import {ObservableMediaServiceProvider} from './providers/observable-media-service-provider';

/**
* *****************************************************************
Expand All @@ -27,7 +27,7 @@ import {MatchMediaObservableProvider} from './providers/match-media-observable-p
MediaMonitor, // MediaQuery monitor service observes all known breakpoints
BreakPointRegistry, // Registry of known/used BreakPoint(s)
BreakPointsProvider, // Supports developer overrides of list of known breakpoints
MatchMediaObservableProvider // easy subscription injectable `media$` matchMedia observable
ObservableMediaServiceProvider // easy subscription injectable `media$` matchMedia observable
]
})
export class MediaQueriesModule {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/media-query/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/
export * from './breakpoints/break-point-registry';
export * from './providers/break-points-provider';
export * from './providers/match-media-observable-provider';
export * from './providers/observable-media-service-provider';
export * from './match-media';
export * from './media-change';
export * from './media-monitor';
Expand Down
11 changes: 6 additions & 5 deletions src/lib/media-query/match-media.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ import {BreakPoint} from './breakpoints/break-point';
import {MockMatchMedia} from './mock/mock-match-media';
import {BreakPointRegistry} from './breakpoints/break-point-registry';
import {BreakPointsProvider} from './providers/break-points-provider';
import {MatchMedia, MatchMediaObservable} from './match-media';
import {MatchMediaObservableProvider} from './providers/match-media-observable-provider';
import {MatchMedia} from './match-media';
import {ObservableMediaService} from './observable-media-service';
import {ObservableMediaServiceProvider} from './providers/observable-media-service-provider';

describe('match-media', () => {
let matchMedia: MockMatchMedia;
Expand Down Expand Up @@ -130,14 +131,14 @@ describe('match-media-observable', () => {
BreakPointsProvider, // Supports developer overrides of list of known breakpoints
BreakPointRegistry, // Registry of known/used BreakPoint(s)
{provide: MatchMedia, useClass: MockMatchMedia},
MatchMediaObservableProvider // injectable `media$` matchMedia observable
ObservableMediaServiceProvider // injectable `media$` matchMedia observable
]
});
});

// Single async inject to save references; which are used in all tests below
beforeEach(async(inject(
[MatchMediaObservable, MatchMedia, BreakPointRegistry],
[ObservableMediaService, MatchMedia, BreakPointRegistry],
(_media$_, _matchMedia_, _breakPoints_) => {
matchMedia = _matchMedia_; // inject only to manually activate mediaQuery ranges
breakPoints = _breakPoints_;
Expand Down Expand Up @@ -204,7 +205,7 @@ describe('match-media-observable', () => {
});

/**
* Only the MatchMediaObservable ignores de-activations;
* Only the ObservableMediaService ignores de-activations;
* MediaMonitor and MatchMedia report both activations and de-activations!
*/
it('ignores mediaQuery de-activations', () => {
Expand Down
11 changes: 0 additions & 11 deletions src/lib/media-query/match-media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {OpaqueToken} from '@angular/core';
import {Injectable, NgZone} from '@angular/core';

import {BehaviorSubject} from 'rxjs/BehaviorSubject';
Expand Down Expand Up @@ -35,16 +34,6 @@ export interface MediaQueryList {
removeListener(listener: MediaQueryListListener): void;
}

/**
* Opaque Token unique to the flex-layout library.
* Note: Developers must use this token when building their own custom
* `MatchMediaObservableProvider` provider.
*
* @see ./providers/match-media-observable-provider.ts
*/
// tslint:disable-next-line:variable-name
export const MatchMediaObservable: OpaqueToken = new OpaqueToken('fxObservableMatchMedia');


/**
* MediaMonitor configures listeners to mediaQuery changes and publishes an Observable facade to
Expand Down
142 changes: 142 additions & 0 deletions src/lib/media-query/observable-media-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {OpaqueToken} from '@angular/core'; // tslint:disable-line:no-unused-variable

import {Subscription} from 'rxjs/Subscription';
import {Observable, Subscribable} from "rxjs/Observable";
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/filter';

import {BreakPointRegistry} from './breakpoints/break-point-registry';

import {MediaChange} from './media-change';
import {MatchMedia} from './match-media';
import {mergeAlias} from './../utils/add-alias';
import {BreakPoint} from './breakpoints/break-point';


/**
* Opaque Token unique to the flex-layout library.
* Note: Developers must use this token when building their own custom
* `ObservableMediaServiceProvider` provider.
*
* @see ./providers/match-media-observable-provider.ts
*/
// tslint:disable-next-line:variable-name
export const ObservableMediaService: OpaqueToken = new OpaqueToken('flex-layout-media-service');


/**
* Class internalizes a MatchMedia service and exposes an Subscribable and Observable interface.
* This an Observable with that exposes a feature to subscribe to mediaQuery
* changes and a validator method (`isActive(<alias>)`) to test if a mediaQuery (or alias) is
* currently active.
*
* !! Only mediaChange activations (not de-activations) are announced by the ObservableMediaService
*
* This class uses the BreakPoint Registry to inject alias information into the raw MediaChange
* notification. For custom mediaQuery notifications, alias information will not be injected and
* those fields will be ''.
*
* !! This is not an actual Observable. It is a wrapper of an Observable used to publish additional
* methods like `isActive(<alias>). To access the Observable and use RxJS operators, use
* `.asObservable()` with syntax like media.asObservable().map(....).
*
*/
export class MediaService implements Subscribable<MediaChange> {
private observable$: Observable<MediaChange>;

constructor(private mediaWatcher: MatchMedia,
private breakpoints: BreakPointRegistry) {
this._registerBreakPoints();
this.observable$ = this._buildObservable();
}

/**
* Test if specified query/alias is active.
*/
isActive(alias): boolean {
let query = this._toMediaQuery(alias);
return this.mediaWatcher.isActive(query);
};

/**
* Proxy to the Observable subscribe method
*/
subscribe(next?: (value: MediaChange) => void,
error?: (error: any) => void,
complete?: () => void): Subscription {
return this.observable$.subscribe(next, error, complete);
};

/**
* Access to observable for use with operators like
* .filter(), .map(), etc.
*/
asObservable(): Observable<MediaChange> {
return this.observable$;
}

// ************************************************
// Internal Methods
// ************************************************

/**
* Register all the mediaQueries registered in the BreakPointRegistry
* This is needed so subscribers can be auto-notified of all standard, registered
* mediaQuery activations
*/
private _registerBreakPoints() {
this.breakpoints.items.forEach((bp: BreakPoint) => {
this.mediaWatcher.registerQuery(bp.mediaQuery);
return bp;
});
}

/**
* Prepare internal observable
* NOTE: the raw MediaChange events [from MatchMedia] do not contain important alias information
* these must be injected into the MediaChange
*/
private _buildObservable() {
return this.mediaWatcher.observe()
.filter((change: MediaChange) => {
// Only pass/announce activations (not de-activations)
return change.matches === true;
})
.map((change: MediaChange) => {
// Inject associated (if any) alias information into the MediaChange event
return mergeAlias(change, this._findByQuery(change.mediaQuery));
});
}

/**
* Breakpoint locator by alias
*/
private _findByAlias(alias) {
return this.breakpoints.findByAlias(alias);
};

/**
* Breakpoint locator by mediaQuery
*/
private _findByQuery(query) {
return this.breakpoints.findByQuery(query);
};

/**
* Find associated breakpoint (if any)
*/
private _toMediaQuery(query) {
let bp: BreakPoint = this._findByAlias(query) || this._findByQuery(query);
return bp ? bp.mediaQuery : query;
};

}
Loading

0 comments on commit 8ca159e

Please sign in to comment.