diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 3cb7904..28a9d90 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -8,8 +8,8 @@ import { TanStackRouterDevtoolsComponent } from '../router/router-devtools';
template: `
Welcome to {{ title }}!
- Home | About |
- Parent 1
+ Home | About |
+ Parent 1
diff --git a/src/app/parent.component.ts b/src/app/parent.component.ts
index b7b62f6..ebbb386 100644
--- a/src/app/parent.component.ts
+++ b/src/app/parent.component.ts
@@ -20,9 +20,9 @@ export const Route = createRoute({
imports: [Outlet, Link],
template: `
Parent -
- Child |
- Child 1 |
- Child 2
+ Child |
+ Child 1 |
+ Child 2
diff --git a/src/router/create-router.ts b/src/router/create-router.ts
index c5a139a..95b01ae 100644
--- a/src/router/create-router.ts
+++ b/src/router/create-router.ts
@@ -108,7 +108,7 @@ export class NgRouter<
>
) {
super(options);
- this.load({ sync: true });
+ void this.load({ sync: true });
this.__store.subscribe(() => {
this.routerState.set(this.state);
});
diff --git a/src/router/link.ts b/src/router/link.ts
index 2ca444b..dd18190 100644
--- a/src/router/link.ts
+++ b/src/router/link.ts
@@ -4,32 +4,27 @@ import { injectRouter } from './router';
@Directive({
selector: 'a[link]',
+ exportAs: 'link',
host: {
'(click)': 'navigate($event)',
- '[attr.href]': '_href()',
+ '[attr.href]': 'hostHref()',
},
})
export class Link {
- to = input();
- from = input();
- params = input();
- search = input();
- hash = input();
- options = input();
+ toOptions = input.required<
+ | (Omit & { to: NonNullable })
+ | NonNullable
+ >({ alias: 'link' });
+
router = injectRouter();
- private navigateOptions = computed(() => {
- const options: NavigateOptions = {
- to: this.to(),
- from: this.from(),
- params: this.params(),
- search: this.search(),
- hash: this.hash(),
- ...this.options(),
- };
- return options;
+ private navigateOptions = computed(() => {
+ const to = this.toOptions();
+ if (typeof to === 'object') return to;
+ return { to };
});
- _href = computed(
+
+ protected hostHref = computed(
() => this.router.buildLocation(this.navigateOptions()).href
);
diff --git a/src/router/outlet.ts b/src/router/outlet.ts
index e7b61ea..f3db3ce 100644
--- a/src/router/outlet.ts
+++ b/src/router/outlet.ts
@@ -1,25 +1,26 @@
import {
+ ComponentRef,
+ DestroyRef,
Directive,
effect,
inject,
Type,
ViewContainerRef,
} from '@angular/core';
-
-import { AnyRoute } from '@tanstack/router-core';
-import { injectRouteContext, injectRouter } from './router';
+import { AnyRoute, RouterState } from '@tanstack/router-core';
import { context } from './context';
+import { injectRouteContext, injectRouter } from './router';
-@Directive({
- selector: 'outlet',
-})
+@Directive({ selector: 'outlet', exportAs: 'outlet' })
export class Outlet {
- private cmp!: Type;
private context? = injectRouteContext();
private router = injectRouter();
private vcr = inject(ViewContainerRef);
+ private cmp: Type | null = null;
+ private cmpRef: ComponentRef | null = null;
+
constructor() {
effect(() => {
const routerState = this.router.routerState();
@@ -47,19 +48,25 @@ export class Outlet {
if (this.cmp !== currentCmp) {
this.vcr.clear();
- this.vcr.createComponent(currentCmp, {
+ this.cmpRef = this.vcr.createComponent(currentCmp, {
injector,
environmentInjector,
});
this.cmp = currentCmp;
+ } else {
+ this.cmpRef?.changeDetectorRef.markForCheck();
}
});
+
+ inject(DestroyRef).onDestroy(() => {
+ this.vcr.clear();
+ this.cmp = null;
+ this.cmpRef = null;
+ });
}
- getMatch(matches: any[]): any {
+ getMatch(matches: RouterState['matches']) {
const idx = matches.findIndex((match) => match.id === this.context?.id);
- const matchesToRender = matches[idx + 1];
-
- return matchesToRender;
+ return matches[idx + 1];
}
}
diff --git a/src/router/route.ts b/src/router/route.ts
index 9325971..79f4796 100644
--- a/src/router/route.ts
+++ b/src/router/route.ts
@@ -189,17 +189,15 @@ export function createRoute<
TChildren
> {
if (options.loader) {
- const originalLoader = options.loader;
- options.loader = (...args: Parameters) => {
- const { context, route } = args[0];
- const routeInjector = (
- context as RouterContext
- ).getRouteInjector(route.id);
- return runInInjectionContext(
- routeInjector,
- originalLoader.bind(null, ...args)
- );
- };
+ options.loader = runFnInInjectionContext(options.loader);
+ }
+
+ if (options.shouldReload && typeof options.shouldReload === 'function') {
+ options.shouldReload = runFnInInjectionContext(options.shouldReload);
+ }
+
+ if (options.beforeLoad) {
+ options.beforeLoad = runFnInInjectionContext(options.beforeLoad);
}
return new Route<
@@ -292,6 +290,18 @@ export function createRootRoute<
unknown,
unknown
> {
+ if (options?.loader) {
+ options.loader = runFnInInjectionContext(options.loader);
+ }
+
+ if (options?.shouldReload && typeof options.shouldReload === 'function') {
+ options.shouldReload = runFnInInjectionContext(options.shouldReload);
+ }
+
+ if (options?.beforeLoad) {
+ options.beforeLoad = runFnInInjectionContext(options.beforeLoad);
+ }
+
return new RootRoute<
TSearchValidator,
TRouterContext,
@@ -358,3 +368,12 @@ export class NotFoundRoute<
});
}
}
+
+function runFnInInjectionContext any>(fn: TFn) {
+ const originalFn = fn;
+ return (...args: Parameters) => {
+ const { context, route } = args[0];
+ const routeInjector = context.getRouteInjector(route.id);
+ return runInInjectionContext(routeInjector, originalFn.bind(null, ...args));
+ };
+}