From 887d32a9bf0546fe309445e5e88ef3c2bba66baf Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Wed, 1 Mar 2017 11:23:47 +0000 Subject: [PATCH] feat(aio): add location service --- aio/src/app/shared/location.service.spec.ts | 122 ++++++++++++++++++++ aio/src/app/shared/location.service.ts | 22 ++++ 2 files changed, 144 insertions(+) create mode 100644 aio/src/app/shared/location.service.spec.ts create mode 100644 aio/src/app/shared/location.service.ts diff --git a/aio/src/app/shared/location.service.spec.ts b/aio/src/app/shared/location.service.spec.ts new file mode 100644 index 0000000000..a0607f2b35 --- /dev/null +++ b/aio/src/app/shared/location.service.spec.ts @@ -0,0 +1,122 @@ +import { ReflectiveInjector } from '@angular/core'; +import { Location, LocationStrategy } from '@angular/common'; +import { MockLocationStrategy } from '@angular/common/testing'; +import { LocationService } from './location.service'; + +fdescribe('LocationService', () => { + + let injector: ReflectiveInjector; + + beforeEach(() => { + injector = ReflectiveInjector.resolveAndCreate([ + LocationService, + Location, + { provide: LocationStrategy, useClass: MockLocationStrategy } + ]); + }); + + describe('urlStream', () => { + it('should emit the latest url at the time it is subscribed to', () => { + + const location: MockLocationStrategy = injector.get(LocationStrategy); + + location.simulatePopState('/initial-url1'); + location.simulatePopState('/initial-url2'); + location.simulatePopState('/initial-url3'); + + const service: LocationService = injector.get(LocationService); + + location.simulatePopState('/next-url1'); + location.simulatePopState('/next-url2'); + location.simulatePopState('/next-url3'); + + let initialUrl; + service.currentUrl.subscribe(url => initialUrl = url); + expect(initialUrl).toEqual('/next-url3'); + }); + + it('should emit all location changes after it has been subscribed to', () => { + const location: MockLocationStrategy = injector.get(LocationStrategy); + const service: LocationService = injector.get(LocationService); + + location.simulatePopState('/initial-url1'); + location.simulatePopState('/initial-url2'); + location.simulatePopState('/initial-url3'); + + const urls = []; + service.currentUrl.subscribe(url => urls.push(url)); + + location.simulatePopState('/next-url1'); + location.simulatePopState('/next-url2'); + location.simulatePopState('/next-url3'); + + expect(urls).toEqual([ + '/initial-url3', + '/next-url1', + '/next-url2', + '/next-url3' + ]); + }); + + it('should pass only the latest and later urls to each subscriber', () => { + const location: MockLocationStrategy = injector.get(LocationStrategy); + const service: LocationService = injector.get(LocationService); + + location.simulatePopState('/initial-url1'); + location.simulatePopState('/initial-url2'); + location.simulatePopState('/initial-url3'); + + const urls1 = []; + service.currentUrl.subscribe(url => urls1.push(url)); + + location.simulatePopState('/next-url1'); + location.simulatePopState('/next-url2'); + + const urls2 = []; + service.currentUrl.subscribe(url => urls2.push(url)); + + location.simulatePopState('/next-url3'); + + expect(urls1).toEqual([ + '/initial-url3', + '/next-url1', + '/next-url2', + '/next-url3' + ]); + + expect(urls2).toEqual([ + '/next-url2', + '/next-url3' + ]); + }); + }); + + describe('go', () => { + it('should update the location', () => { + const location: MockLocationStrategy = injector.get(LocationStrategy); + const service: LocationService = injector.get(LocationService); + + service.go('some-new-url'); + + expect(location.internalPath).toEqual('some-new-url'); + expect(location.path(true)).toEqual('some-new-url'); + }); + + it('should emit the new url', () => { + const location: MockLocationStrategy = injector.get(LocationStrategy); + const service: LocationService = injector.get(LocationService); + + service.go('some-initial-url'); + + const urls = []; + service.currentUrl.subscribe(url => urls.push(url)); + + service.go('some-new-url'); + + expect(urls).toEqual([ + 'some-initial-url', + 'some-new-url' + ]); + }); + }); +}); diff --git a/aio/src/app/shared/location.service.ts b/aio/src/app/shared/location.service.ts new file mode 100644 index 0000000000..bc27aee3e8 --- /dev/null +++ b/aio/src/app/shared/location.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { Location } from '@angular/common'; +import { Observable } from 'rxjs/Observable'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +@Injectable() +export class LocationService { + + private urlSubject: BehaviorSubject; + get currentUrl() { return this.urlSubject.asObservable(); } + + constructor(private location: Location) { + this.urlSubject = new BehaviorSubject(location.path(true)); + this.location.subscribe(state => this.urlSubject.next(state.url)); + } + + go(url: string) { + url = this.location.normalize(url); + this.location.go(url); + this.urlSubject.next(url); + } +}