diff --git a/src/cdk/layout/breakpoints-observer.spec.ts b/src/cdk/layout/breakpoints-observer.spec.ts index 296d09142e00..4bb2d9ab5049 100644 --- a/src/cdk/layout/breakpoints-observer.spec.ts +++ b/src/cdk/layout/breakpoints-observer.spec.ts @@ -34,7 +34,7 @@ describe('BreakpointObserver', () => { }); it('retrieves the whether a query is currently matched', () => { - let query = 'everything starts as true in the FakeMediaMatcher'; + const query = 'everything starts as true in the FakeMediaMatcher'; expect(breakpointManager.isMatched(query)).toBeTruthy(); }); @@ -61,15 +61,15 @@ describe('BreakpointObserver', () => { }); it('accepts an array of queries', () => { - let queries = ['1 query', '2 query', 'red query', 'blue query']; + const queries = ['1 query', '2 query', 'red query', 'blue query']; breakpointManager.observe(queries); expect(mediaMatcher.queryCount).toBe(queries.length); }); it('completes all events when the breakpoint manager is destroyed', () => { - let firstTest = jasmine.createSpy('test1'); + const firstTest = jasmine.createSpy('test1'); breakpointManager.observe('test1').subscribe(undefined, undefined, firstTest); - let secondTest = jasmine.createSpy('test2'); + const secondTest = jasmine.createSpy('test2'); breakpointManager.observe('test2').subscribe(undefined, undefined, secondTest); expect(firstTest).not.toHaveBeenCalled(); @@ -82,8 +82,8 @@ describe('BreakpointObserver', () => { }); it('emits an event on the observable when values change', () => { - let query = '(width: 999px)'; - let queryMatchState: boolean = false; + const query = '(width: 999px)'; + let queryMatchState = false; breakpointManager.observe(query).subscribe((state: BreakpointState) => { queryMatchState = state.matches; }); @@ -93,14 +93,31 @@ describe('BreakpointObserver', () => { expect(queryMatchState).toBeFalsy(); }); + it('emits an event on the observable with the matching state of all queries provided', () => { + const queryOne = '(width: 999px)'; + const queryTwo = '(width: 700px)'; + let state: BreakpointState = {matches: false, breakpoints: {}}; + breakpointManager.observe([queryOne, queryTwo]).subscribe((breakpoint: BreakpointState) => { + state = breakpoint; + }); + + mediaMatcher.setMatchesQuery(queryOne, false); + mediaMatcher.setMatchesQuery(queryTwo, false); + expect(state.breakpoints).toEqual({[queryOne]: false, [queryTwo]: false}); + + mediaMatcher.setMatchesQuery(queryOne, true); + mediaMatcher.setMatchesQuery(queryTwo, false); + expect(state.breakpoints).toEqual({[queryOne]: true, [queryTwo]: false}); + }); + it('emits a true matches state when the query is matched', () => { - let query = '(width: 999px)'; + const query = '(width: 999px)'; mediaMatcher.setMatchesQuery(query, true); expect(breakpointManager.isMatched(query)).toBeTruthy(); }); it('emits a false matches state when the query is not matched', () => { - let query = '(width: 999px)'; + const query = '(width: 999px)'; mediaMatcher.setMatchesQuery(query, false); expect(breakpointManager.isMatched(query)).toBeTruthy(); }); @@ -130,7 +147,7 @@ export class FakeMediaQueryList implements MediaQueryList { @Injectable() export class FakeMediaMatcher { /** A map of match media queries. */ - private queries: Map = new Map(); + private queries = new Map(); /** The number of distinct queries created in the media matcher during a test. */ get queryCount(): number { @@ -139,7 +156,7 @@ export class FakeMediaMatcher { /** Fakes the match media response to be controlled in tests. */ matchMedia(query: string): FakeMediaQueryList { - let mql = new FakeMediaQueryList(true, query); + const mql = new FakeMediaQueryList(true, query); this.queries.set(query, mql); return mql; } diff --git a/src/cdk/layout/breakpoints-observer.ts b/src/cdk/layout/breakpoints-observer.ts index 31dee92a619e..1e476c0d63ff 100644 --- a/src/cdk/layout/breakpoints-observer.ts +++ b/src/cdk/layout/breakpoints-observer.ts @@ -16,10 +16,25 @@ import {coerceArray} from '@angular/cdk/coercion'; export interface BreakpointState { /** Whether the breakpoint is currently matching. */ matches: boolean; + /** + * A key boolean pair for each query provided to the observe method, + * with its current matched state. + */ + breakpoints: { + [key: string]: boolean; + }; +} + +/** The current state of a layout breakpoint. */ +interface InternalBreakpointState { + /** Whether the breakpoint is currently matching. */ + matches: boolean; + /** The media query being to be matched */ + query: string; } interface Query { - observable: Observable; + observable: Observable; mql: MediaQueryList; } @@ -27,9 +42,9 @@ interface Query { @Injectable({providedIn: 'root'}) export class BreakpointObserver implements OnDestroy { /** A map of all media queries currently being listened for. */ - private _queries: Map = new Map(); + private _queries = new Map(); /** A subject for all other observables to takeUntil based on. */ - private _destroySubject: Subject<{}> = new Subject(); + private _destroySubject = new Subject(); constructor(private mediaMatcher: MediaMatcher, private zone: NgZone) {} @@ -59,10 +74,16 @@ export class BreakpointObserver implements OnDestroy { const queries = splitQueries(coerceArray(value)); const observables = queries.map(query => this._registerQuery(query).observable); - return combineLatest(observables).pipe(map((breakpointStates: BreakpointState[]) => { - return { - matches: breakpointStates.some(state => state && state.matches) + return combineLatest(observables).pipe(map((breakpointStates: InternalBreakpointState[]) => { + const response: BreakpointState = { + matches: false, + breakpoints: {}, }; + breakpointStates.forEach((state: InternalBreakpointState) => { + response.matches = response.matches || state.matches; + response.breakpoints[state.query] = state.matches; + }); + return response; })); } @@ -90,11 +111,11 @@ export class BreakpointObserver implements OnDestroy { .pipe( takeUntil(this._destroySubject), startWith(mql), - map((nextMql: MediaQueryList) => ({matches: nextMql.matches})) + map((nextMql: MediaQueryList) => ({query, matches: nextMql.matches})) ); // Add the MediaQueryList to the set of queries. - const output = {observable: queryObservable, mql: mql}; + const output = {observable: queryObservable, mql}; this._queries.set(query, output); return output; }