diff --git a/packages/router-component-store/src/lib/router-history-store/router-history-service-in-angular.spec.ts b/packages/router-component-store/src/lib/router-history-store/router-history-service-in-angular.spec.ts
new file mode 100644
index 0000000..24bc636
--- /dev/null
+++ b/packages/router-component-store/src/lib/router-history-store/router-history-service-in-angular.spec.ts
@@ -0,0 +1,77 @@
+import {
+ Event as NgRouterEvent,
+ NavigationEnd,
+ NavigationStart,
+} from '@angular/router';
+
+export const routerEvents: readonly NgRouterEvent[] = [
+ // 1. Navigate to the root path ‘/’, which redirects me to the homepage
+ // Current: Home
+ // Previous: None
+ // Next: None
+ new NavigationStart(1, '/', 'imperative', null),
+ new NavigationEnd(1, '/', '/home'),
+
+ // 2. Click a menu link to navigate to the About page
+ // Current: About
+ // Previous: Home
+ // Next: None
+ new NavigationStart(2, '/about', 'imperative', null),
+ new NavigationEnd(2, '/about', '/about'),
+
+ // 3. Click a menu link to navigate to the Company page
+ // Current: Company
+ // Previous About
+ // Next: None
+ new NavigationStart(3, '/company', 'imperative', null),
+ new NavigationEnd(3, '/company', '/company'),
+
+ // 4. Click the back button
+ // Current: About
+ // Previous: Home
+ // Next: Company
+ new NavigationStart(4, '/about', 'popstate', { navigationId: 2 }),
+ new NavigationEnd(4, '/about', '/about'),
+
+ // 5. Click a menu link to navigate to the Products page
+ // Current: Products
+ // Previous: About
+ // Next: None
+ new NavigationStart(5, '/products', 'imperative', null),
+ new NavigationEnd(5, '/products', '/products'),
+
+ // 6. Click a menu link to navigate to the Home page
+ // Current: Home
+ // Previous: Products
+ // Next: None
+ new NavigationStart(6, '/home', 'imperative', null),
+ new NavigationEnd(6, '/home', '/home'),
+
+ // 7. Click a menu link to navigate to the About page
+ // Current: About
+ // Previous: Home
+ // Next: None
+ new NavigationStart(7, '/about', 'imperative', null),
+ new NavigationEnd(7, '/about', '/about'),
+
+ // 8. Click the back button
+ // Current: Home
+ // Previous: Products
+ // Next: About
+ new NavigationStart(8, '/home', 'popstate', { navigationId: 6 }),
+ new NavigationEnd(8, '/home', '/home'),
+
+ // 9. Click the forward button
+ // Current: About
+ // Previous: Home
+ // Next: None
+ new NavigationStart(9, '/about', 'popstate', { navigationId: 7 }),
+ new NavigationEnd(9, '/about', '/about'),
+
+ // 10. Click the back button
+ // Current: Home
+ // Previous: Products
+ // Next: About
+ new NavigationStart(10, '/home', 'popstate', { navigationId: 8 }),
+ new NavigationEnd(10, '/home', '/home'),
+];
diff --git a/packages/router-component-store/src/lib/router-history-store/router-history.store.spec.ts b/packages/router-component-store/src/lib/router-history-store/router-history.store.spec.ts
index cd4d8d1..ea8fcd1 100644
--- a/packages/router-component-store/src/lib/router-history-store/router-history.store.spec.ts
+++ b/packages/router-component-store/src/lib/router-history-store/router-history.store.spec.ts
@@ -30,6 +30,14 @@ function createTestComponent(name: string, selector: string) {
>< Back
+ > Next
+
Home
About
Company
@@ -45,6 +53,11 @@ class TestAppComponent {
event.preventDefault();
this.routerHistory.onNavigateBack();
}
+
+ onNext(event: MouseEvent) {
+ event.preventDefault();
+ this.routerHistory.onNavigateForward();
+ }
}
describe(RouterHistoryStore.name, () => {
@@ -98,55 +111,176 @@ describe(RouterHistoryStore.name, () => {
}
it('the URLs behave like the History API when navigating using links', async () => {
- expect.assertions(2);
+ expect.assertions(3);
const { click, routerHistory } = await setup();
// At Home
+ // Previous: None
+ // Next: None
await click('#about-link');
// At About
+ // Previous: Home
+ // Next: None
await click('#company-link');
// At Company
+ // Previous: About
+ // Next: None
await click('#products-link');
// At Products
+ // Previous: Company
+ // Next: None
expect(await firstValueFrom(routerHistory.currentUrl$)).toBe('/products');
expect(await firstValueFrom(routerHistory.previousUrl$)).toBe('/company');
+ expect(await firstValueFrom(routerHistory.nextUrl$)).toBe(undefined);
});
it('the URLs behave like the History API when navigating back', async () => {
- expect.assertions(2);
+ expect.assertions(3);
+
+ const { click, routerHistory } = await setup();
+
+ // At Home
+ // Previous: None
+ // Next: None
+ await click('#about-link');
+ // At About
+ // Previous: Home
+ // Next: None
+ await click('#company-link');
+ // At Company
+ // Previous: About
+ // Next: None
+ await click('#back-link');
+ // At About
+ // Previous: Home
+ // Next: Company
+
+ expect(await firstValueFrom(routerHistory.currentUrl$)).toBe('/about');
+ expect(await firstValueFrom(routerHistory.previousUrl$)).toBe('/home');
+ expect(await firstValueFrom(routerHistory.nextUrl$)).toBe('/company');
+ });
+
+ it('the URLs behave like the History API when navigating back twice', async () => {
+ expect.assertions(3);
+
+ const { click, routerHistory } = await setup();
+
+ // At Home
+ // Previous: None
+ // Next: None
+ await click('#about-link');
+ // At About
+ // Previous: Home
+ // Next: None
+ await click('#company-link');
+ // At Company
+ // Previous: About
+ // Next: None
+ await click('#back-link');
+ // At About
+ // Previous: Home
+ // Next: Company
+ await click('#back-link');
+ // At Home
+ // Previous: None
+ // Next: About
+
+ expect(await firstValueFrom(routerHistory.currentUrl$)).toBe('/home');
+ expect(await firstValueFrom(routerHistory.previousUrl$)).toBe(undefined);
+ expect(await firstValueFrom(routerHistory.nextUrl$)).toBe('/about');
+ });
+
+ it('the URLs behave like the History API when navigating back twice then forward', async () => {
+ expect.assertions(3);
const { click, routerHistory } = await setup();
// At Home
+ // Previous: None
+ // Next: None
await click('#about-link');
// At About
+ // Previous: Home
+ // Next: None
await click('#company-link');
// At Company
+ // Previous: About
+ // Next: None
await click('#back-link');
// At About
+ // Previous: Home
+ // Next: Company
+ await click('#back-link');
+ // At Home
+ // Previous: None
+ // Next: About
+ await click('#forward-link');
+ // At About
+ // Previous: Home
+ // Next: Company
expect(await firstValueFrom(routerHistory.currentUrl$)).toBe('/about');
expect(await firstValueFrom(routerHistory.previousUrl$)).toBe('/home');
+ expect(await firstValueFrom(routerHistory.nextUrl$)).toBe('/company');
});
it('the URLs behave like the History API when navigating back then using links', async () => {
- expect.assertions(2);
+ expect.assertions(3);
const { click, routerHistory } = await setup();
// At Home
+ // Previous: None
+ // Next: None
await click('#about-link');
// At About
+ // Previous: Home
+ // Next: None
await click('#company-link');
// At Company
+ // Previous: About
+ // Next: None
await click('#back-link');
// At About
+ // Previous: Home
+ // Next: Company
await click('#products-link');
// At Products
+ // Previous: About
+ // Next: None
expect(await firstValueFrom(routerHistory.currentUrl$)).toBe('/products');
expect(await firstValueFrom(routerHistory.previousUrl$)).toBe('/about');
+ expect(await firstValueFrom(routerHistory.nextUrl$)).toBe(undefined);
+ });
+
+ it('the URLs behave like the History API when navigating back then forward', async () => {
+ expect.assertions(3);
+
+ const { click, routerHistory } = await setup();
+
+ // At Home
+ await click('#about-link');
+ // At About
+ // Previous: Home
+ // Next: None
+ await click('#company-link');
+ // At Company
+ // Previous: About
+ // Next: None
+ await click('#back-link');
+ // At About
+ // Previous: Home
+ // Next: Company
+ await click('#forward-link');
+ // At Company
+ // Previous: About
+ // Next: None
+
+ expect(await firstValueFrom(routerHistory.currentUrl$)).toBe('/company');
+ expect(await firstValueFrom(routerHistory.previousUrl$)).toBe('/about');
+ expect(await firstValueFrom(routerHistory.nextUrl$)).toBe(undefined);
});
});
diff --git a/packages/router-component-store/src/lib/router-history-store/router-history.store.ts b/packages/router-component-store/src/lib/router-history-store/router-history.store.ts
index 78499df..adb89fa 100644
--- a/packages/router-component-store/src/lib/router-history-store/router-history.store.ts
+++ b/packages/router-component-store/src/lib/router-history-store/router-history.store.ts
@@ -127,6 +127,38 @@ export class RouterHistoryStore extends ComponentStore {
this.#latestRouterNavigatedSequence$,
([, navigationEnd]) => navigationEnd.urlAfterRedirects
);
+ /**
+ * The next URL when taking `popstate` events into account.
+ *
+ * `undefined` is emitted when the current navigation is the last in the
+ * navigation history.
+ */
+ nextUrl$: Observable = this.select(
+ this.#history$,
+ this.#maxNavigatedId$,
+ (history, maxNavigatedId) => {
+ if (maxNavigatedId === 1) {
+ return undefined;
+ }
+
+ const [sourceNavigationStart] = this.#findSourceNavigatedSequence(
+ maxNavigatedId,
+ history
+ );
+
+ if (sourceNavigationStart.id === maxNavigatedId) {
+ return undefined;
+ }
+
+ const nextNavigationId = sourceNavigationStart.id + 1;
+ const [, nextNavigationEnd] = this.#findSourceNavigatedSequence(
+ nextNavigationId,
+ history
+ );
+
+ return nextNavigationEnd.urlAfterRedirects;
+ }
+ );
/**
* The previous URL when taking `popstate` events into account.
*