-
-
Notifications
You must be signed in to change notification settings - Fork 267
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(router): add ability to provide meta tags via RouteMeta
- Loading branch information
1 parent
19beed1
commit dfed3e3
Showing
9 changed files
with
215 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { inject } from '@angular/core'; | ||
import { Meta, MetaDefinition as NgMetaTag } from '@angular/platform-browser'; | ||
import { ActivatedRouteSnapshot, NavigationEnd, Router } from '@angular/router'; | ||
import { filter } from 'rxjs/operators'; | ||
|
||
export const ROUTE_META_TAGS_KEY = Symbol( | ||
'@analogjs/router Route Meta Tags Key' | ||
); | ||
|
||
const CHARSET_KEY = 'charset'; | ||
const HTTP_EQUIV_KEY = 'httpEquiv'; | ||
// httpEquiv selector key needs to be in kebab case format | ||
const HTTP_EQUIV_SELECTOR_KEY = 'http-equiv'; | ||
const NAME_KEY = 'name'; | ||
const PROPERTY_KEY = 'property'; | ||
const CONTENT_KEY = 'content'; | ||
|
||
export type MetaTag = | ||
| (CharsetMetaTag & ExcludeRestMetaTagKeys<typeof CHARSET_KEY>) | ||
| (HttpEquivMetaTag & ExcludeRestMetaTagKeys<typeof HTTP_EQUIV_KEY>) | ||
| (NameMetaTag & ExcludeRestMetaTagKeys<typeof NAME_KEY>) | ||
| (PropertyMetaTag & ExcludeRestMetaTagKeys<typeof PROPERTY_KEY>); | ||
|
||
type CharsetMetaTag = { [CHARSET_KEY]: string }; | ||
type HttpEquivMetaTag = { [HTTP_EQUIV_KEY]: string; [CONTENT_KEY]: string }; | ||
type NameMetaTag = { [NAME_KEY]: string; [CONTENT_KEY]: string }; | ||
type PropertyMetaTag = { [PROPERTY_KEY]: string; [CONTENT_KEY]: string }; | ||
|
||
type MetaTagKey = | ||
| typeof CHARSET_KEY | ||
| typeof HTTP_EQUIV_KEY | ||
| typeof NAME_KEY | ||
| typeof PROPERTY_KEY; | ||
type ExcludeRestMetaTagKeys<Key extends MetaTagKey> = { | ||
[K in Exclude<MetaTagKey, Key>]?: never; | ||
}; | ||
|
||
type MetaTagSelector = | ||
| typeof CHARSET_KEY | ||
| `${ | ||
| typeof HTTP_EQUIV_SELECTOR_KEY | ||
| typeof NAME_KEY | ||
| typeof PROPERTY_KEY}="${string}"`; | ||
type MetaTagMap = Record<MetaTagSelector, MetaTag>; | ||
|
||
export function updateMetaTagsOnNavigationEnd(): void { | ||
const router = inject(Router); | ||
const metaService = inject(Meta); | ||
|
||
router.events | ||
.pipe(filter((event) => event instanceof NavigationEnd)) | ||
.subscribe(() => { | ||
const metaTagMap = getMetaTagMap(router.routerState.snapshot.root); | ||
setMetaTags(metaTagMap, metaService); | ||
}); | ||
} | ||
|
||
function setMetaTags(metaTagMap: MetaTagMap, metaService: Meta): void { | ||
for (const metaTagSelector in metaTagMap) { | ||
const metaTag = metaTagMap[metaTagSelector as MetaTagSelector] as NgMetaTag; | ||
|
||
const updatedMetaTag = metaService.updateTag(metaTag, metaTagSelector); | ||
if (!updatedMetaTag) { | ||
metaService.addTag(metaTag); | ||
} | ||
} | ||
} | ||
|
||
function getMetaTagMap(route: ActivatedRouteSnapshot): MetaTagMap { | ||
const metaTagMap = {} as MetaTagMap; | ||
let currentRoute: ActivatedRouteSnapshot | null = route; | ||
|
||
while (currentRoute) { | ||
const metaTags: MetaTag[] = currentRoute.data[ROUTE_META_TAGS_KEY] ?? []; | ||
for (const metaTag of metaTags) { | ||
metaTagMap[getMetaTagSelector(metaTag)] = metaTag; | ||
} | ||
|
||
currentRoute = currentRoute.firstChild; | ||
} | ||
|
||
return metaTagMap; | ||
} | ||
|
||
function getMetaTagSelector(metaTag: MetaTag): MetaTagSelector { | ||
if (metaTag.name) { | ||
return `${NAME_KEY}="${metaTag.name}"`; | ||
} | ||
|
||
if (metaTag.property) { | ||
return `${PROPERTY_KEY}="${metaTag.property}"`; | ||
} | ||
|
||
if (metaTag.httpEquiv) { | ||
return `${HTTP_EQUIV_SELECTOR_KEY}="${metaTag.httpEquiv}"`; | ||
} | ||
|
||
return CHARSET_KEY; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { | ||
ENVIRONMENT_INITIALIZER, | ||
EnvironmentProviders, | ||
makeEnvironmentProviders, | ||
Provider, | ||
} from '@angular/core'; | ||
import { provideRouter, RouterFeatures } from '@angular/router'; | ||
|
||
import { routes } from './routes'; | ||
import { updateMetaTagsOnNavigationEnd } from './meta-tags'; | ||
|
||
/** | ||
* Sets up providers for the Angular router, and registers | ||
* file-based routes. Additional features can be provided | ||
* to further configure the behavior of the router. | ||
* | ||
* @param features | ||
* @returns Providers and features to configure the router with routes | ||
*/ | ||
export function provideFileRouter( | ||
...features: RouterFeatures[] | ||
): EnvironmentProviders { | ||
return makeEnvironmentProviders([ | ||
// TODO: remove type casting after Angular >=15.1.1 upgrade | ||
// https://github.com/angular/angular/pull/48720 | ||
( | ||
provideRouter(routes, ...features) as unknown as { | ||
ɵproviders: Provider[]; | ||
} | ||
).ɵproviders, | ||
{ | ||
provide: ENVIRONMENT_INITIALIZER, | ||
multi: true, | ||
useValue: () => updateMetaTagsOnNavigationEnd(), | ||
}, | ||
]); | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { RedirectRouteMeta, RouteConfig, RouteMeta } from './models'; | ||
import { ROUTE_META_TAGS_KEY } from './meta-tags'; | ||
|
||
export function toRouteConfig(routeMeta: RouteMeta | undefined): RouteConfig { | ||
if (!routeMeta) { | ||
return {}; | ||
} | ||
|
||
if (isRedirectRouteMeta(routeMeta)) { | ||
return routeMeta; | ||
} | ||
|
||
const { metaTags, ...routeConfig } = routeMeta; | ||
if (!metaTags) { | ||
return routeConfig; | ||
} | ||
|
||
return Array.isArray(metaTags) | ||
? { | ||
...routeConfig, | ||
data: { ...(routeConfig.data ?? {}), [ROUTE_META_TAGS_KEY]: metaTags }, | ||
} | ||
: { | ||
...routeConfig, | ||
resolve: { | ||
...(routeConfig.resolve ?? {}), | ||
[ROUTE_META_TAGS_KEY]: metaTags, | ||
}, | ||
}; | ||
} | ||
|
||
function isRedirectRouteMeta( | ||
routeMeta: RouteMeta | ||
): routeMeta is RedirectRouteMeta { | ||
return !!routeMeta.redirectTo; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters