Skip to content

Commit

Permalink
feat(lib): add templating capabilities to strings
Browse files Browse the repository at this point in the history
  • Loading branch information
gund committed Aug 10, 2019
1 parent c73e5a7 commit 9cd5c7c
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 27 deletions.
133 changes: 106 additions & 27 deletions projects/ngx-router-meta/src/lib/router-meta.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import {
import {
isDataWithMeta,
MetaContext,
RouteMeta,
RouteMetaTemplates,
RouterMetaConfig,
RouterMetaInterpolation,
} from './router-meta';
Expand Down Expand Up @@ -140,68 +142,121 @@ export class RouterMetaService implements OnDestroy {
}

private updateAllMeta(data: Data, ctx: ProcessedMetaContext = {}) {
const {
_templates_: defaultTemplates,
...defaultAllMeta
} = this.defaultMeta;
const { title: _, ...defaultOtherMeta } = defaultAllMeta;

if (!isDataWithMeta(data)) {
this.resetAllMeta(ctx);
this.resetAllMeta(ctx, defaultOtherMeta);
return;
}

const { meta } = data;
const { title, ...otherMeta } = meta;

this.updateTitle(title, ctx);

Object.keys(otherMeta).forEach(name =>
this.updateMeta(name, otherMeta[name], ctx),
const { _templates_, ...allMeta } = meta;
const { title, ...otherMeta } = allMeta;

const allMetaDefaulted = { ...defaultAllMeta, ...allMeta };
const otherMetaDefaulted = { ...defaultOtherMeta, ...otherMeta };
const templates = {
...defaultTemplates,
..._templates_,
} as RouteMetaTemplates;

const processedMeta = this.processContext(allMetaDefaulted);

this.updateTitle(title, ctx, templates, processedMeta);

Object.keys(otherMetaDefaulted).forEach(name =>
this.updateMeta(
name,
otherMetaDefaulted[name],
ctx,
templates,
processedMeta,
),
);

// Cleanup leftover meta tags
Object.keys(this.metaTags)
.filter(name => name in otherMeta === false)
.forEach(name => this.resetMeta(name, ctx));
.filter(name => name in otherMetaDefaulted === false)
.forEach(name => this.resetMeta(name, ctx, templates, processedMeta));
}

private resetAllMeta(ctx: ProcessedMetaContext) {
this.resetTitle(ctx);
Object.keys(this.metaTags).forEach(name => this.resetMeta(name, ctx));
private resetAllMeta(ctx: ProcessedMetaContext, defaultMeta?: RouteMeta) {
this.resetTitle(ctx, this.defaultMeta._templates_);

const metaDefaulted = { ...defaultMeta, ...this.metaTags };

Object.keys(metaDefaulted).forEach(name =>
this.resetMeta(name, ctx, this.defaultMeta._templates_),
);
}

private updateTitle(title?: string, ctx?: ProcessedMetaContext) {
private updateTitle(
title?: string,
ctx?: ProcessedMetaContext,
templates?: RouteMetaTemplates,
meta?: ProcessedMetaContext,
) {
if (title) {
this.title.setTitle(this.templateStr(title, ctx));
this.title.setTitle(
this.templateStr(title, ctx, { name: 'title', templates, meta }),
);
} else {
this.resetTitle(ctx);
this.resetTitle(ctx, templates, meta);
}
}

private resetTitle(ctx?: ProcessedMetaContext) {
private resetTitle(
ctx?: ProcessedMetaContext,
templates?: RouteMetaTemplates,
meta?: ProcessedMetaContext,
) {
this.title.setTitle(
this.templateStr(this.defaultMeta.title || this.originalTitle, ctx),
this.templateStr(this.defaultMeta.title || this.originalTitle, ctx, {
name: 'title',
meta,
templates,
}),
);
}

private updateMeta(
name: string,
content?: string | MetaDefinition,
ctx?: ProcessedMetaContext,
templates?: RouteMetaTemplates,
meta?: ProcessedMetaContext,
) {
if (content) {
const meta: MetaDefinition =
const metaDef: MetaDefinition =
typeof content === 'string' ? { name, content } : content;

meta.content = this.templateStr(meta.content, ctx);
metaDef.content = this.templateStr(metaDef.content, ctx, {
name,
meta,
templates,
});

this.meta.updateTag(meta);
this.meta.updateTag(metaDef);
this.metaTags[name] = true;
} else {
this.resetMeta(name, ctx);
this.resetMeta(name, ctx, templates, meta);
}
}

private resetMeta(name: string, ctx?: MetaContext) {
private resetMeta(
name: string,
ctx?: MetaContext,
templates?: RouteMetaTemplates,
meta?: ProcessedMetaContext,
) {
const defaultMeta = this.defaultMeta[name];

if (defaultMeta) {
this.updateMeta(name, defaultMeta, ctx);
this.updateMeta(name, defaultMeta, ctx, templates, meta);
} else {
this.meta.removeTag(`name="${name}"`);
delete this.metaTags[name];
Expand Down Expand Up @@ -232,14 +287,38 @@ export class RouterMetaService implements OnDestroy {
));
}

private templateStr(str?: string, data?: ProcessedMetaContext) {
private templateStr(
str?: string,
data?: ProcessedMetaContext,
extras?: {
name?: string;
templates?: RouteMetaTemplates;
meta?: ProcessedMetaContext;
},
) {
if (!str || !data) {
return str || '';
}

return Object.keys(data).reduce(
(s, key) => s.replace(data[key].replace, data[key].value),
str,
let template = str;
let ctx = data;

if (extras) {
template =
extras.templates && extras.name
? extras.templates[extras.name] || template
: template;
ctx = { ...extras.meta, ...ctx };

// Recover lost `str` value under provided `extras.name` key in context
if (extras.name && extras.name in ctx === false) {
ctx = { ...ctx, ...this.processContext({ [extras.name]: str }) };
}
}

return Object.keys(ctx).reduce(
(s, key) => s.replace(ctx[key].replace, ctx[key].value),
template,
);
}

Expand Down
3 changes: 3 additions & 0 deletions projects/ngx-router-meta/src/lib/router-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ export interface RouterMetaConfig {

export interface MetaContext extends Indexable<any> {}

export interface RouteMetaTemplates extends Indexable<string> {}

export interface RouteMeta
extends Indexable<string | MetaDefinition | undefined> {
_templates_?: RouteMetaTemplates;
title?: string;
description?: string;
}
Expand Down

0 comments on commit 9cd5c7c

Please sign in to comment.