content
Define an injectable token or service that provides a list of the files markdown files scanned from the src/content
folder in an array that includes frontmatter.
Example structure below
import { inject, InjectionToken } from '@angular/core';
export interface Frontmatter {
title: string;
description: string;
publishedDate: string;
slug: string;
published: boolean;
meta: string[];
[name: string]: any; // extra metadata
}
export interface AnalogContentMetadata {
filename: string;
content: string;
frontmatter: Frontmatter;
}
export const CONTENT_FILES_TOKEN = new InjectionToken<AnalogContentMetadata[]>('@analogjs/content Content Files', {
providedIn: 'root',
factory() {
const rawContentFiles = import.meta.glob('/src/content/**/*.md', {
eager: true, // maybe support lazy loading here
as: 'raw',
});
const contentFiles = Object.keys(rawContentFiles)
.map((contentFile) => {
const metadata = frontmatter<Frontmatter>(rawContentFiles[contentFile]);
return {
filename: contentFile,
content: metadata.body,
frontmatter: {
...metadata.attributes,
title: metadata.attributes.title,
description: metadata.attributes.description,
slug: metadata.attributes.slug,
publishedDate: metadata.attributes.publishedDate,
published: metadata.attributes.published,
}
};
});
return contentFiles;
},
});
export function injectContentMetadata() {
return inject(CONTENT_FILES_TOKEN);
}
Usage
import { Component } from '@angular/core';
import { injectContentMetadata } from '@analogjs/content';
@Component({
selector: 'blog-posts',
standalone: true,
imports: [NgFor],
template: `
<div *ngFor="let post of posts">
{{ post.frontmatter.title }} <br/>
</div>
`,
})
export default class BlogComponent {
posts = injectContentMetadata(); // AnalogContentMetadata[]
}
This could be used to list blog posts for example and could be filtered further based on needs, such as only listing published
posts as an example.
No response
What do you think about having a more flexible result, so attributes type can be provided based on the project needs? This will also provide the ability to set proper typing for plain markdown content without frontmatter.
export interface ContentFile<
Attributes extends Record<string, any> = Record<string, any>
> {
filename: string;
content: string;
attributes: Attributes;
}
const CONTENT_FILES_TOKEN = new InjectionToken<ContentFile[]>(
'@analogjs/content Content Files',
{
providedIn: 'root',
factory() {
const rawContentFiles = import.meta.glob('/src/content/**/*.md', {
eager: true,
as: 'raw',
});
return Object.keys(rawContentFiles).map((filename) => {
const { body, attributes } = fm<Record<string, any>>(
rawContentFiles[filename]
);
return {
filename,
content: body,
attributes,
};
});
},
}
);
export function injectContentFiles<
Attributes extends Record<string, any>
>(): ContentFile<T>[] {
return inject(CONTENT_FILES_TOKEN) as ContentFile<Attributes>[];
}
// usage:
interface PostAttributes {
title: string;
coverSrc: string;
published: boolean;
}
@Component({
selector: 'app-blog',
standalone: true,
imports: [NgFor],
template: `
<article *ngFor="let post of publishedPosts">
<h2>{{ post.attributes.title }}</h2>
<img [src]="post.attributes.coverSrc" [alt]="post.attributes.title" />
</article>
`,
})
export default class BlogComponent {
private readonly posts = injectContentFiles<PostAttributes>();
readonly publishedPosts = this.posts.filter(
(post) => post.attributes.published
);
}
Changes:
injectContentMetadata
is renamed to injectContentFiles
.frontmatter
is renamed to attributes
.AnalogContentMetadata
is renamed to ContentFile
and Frontmatter
is removed, so attributes can be typed via generic.@markostanimirovic @brandonroberts I can implement as laid out here: #222 (comment)
If you guys are not already working on this and want to assign this issue to me
Owner Name | analogjs |
Repo Name | analog |
Full Name | analogjs/analog |
Language | TypeScript |
Created Date | 2022-07-06 |
Updated Date | 2023-03-28 |
Star Count | 885 |
Watcher Count | 18 |
Fork Count | 67 |
Issue Count | 33 |
Issue Title | Created Date | Updated Date |
---|