diff --git a/apps/blog-app/src/app/routes/blog/[slug].ts b/apps/blog-app/src/app/routes/blog/[slug].ts index 9e7250d24..36b9f3845 100644 --- a/apps/blog-app/src/app/routes/blog/[slug].ts +++ b/apps/blog-app/src/app/routes/blog/[slug].ts @@ -1,15 +1,19 @@ import { injectContent, MarkdownComponent } from '@analogjs/content'; -import { AsyncPipe } from '@angular/common'; +import { AsyncPipe, NgIf } from '@angular/common'; import { Component } from '@angular/core'; +import { BlogAttributes } from '../../../lib/blog-attributes'; @Component({ selector: 'blog-post', standalone: true, - imports: [MarkdownComponent, AsyncPipe], + imports: [MarkdownComponent, AsyncPipe, NgIf], template: ` - + +

{{ cf.attributes.title }}

+ +
`, }) export default class BlogPostComponent { - content$ = injectContent(); + public contentFile$ = injectContent(); } diff --git a/apps/blog-app/src/content/2022-12-27-my-first-post.md b/apps/blog-app/src/content/2022-12-27-my-first-post.md index 9d315fa0d..744ac6a8d 100644 --- a/apps/blog-app/src/content/2022-12-27-my-first-post.md +++ b/apps/blog-app/src/content/2022-12-27-my-first-post.md @@ -3,6 +3,4 @@ title: My First Post slug: 2022-12-27-my-first-post --- -## My First Post - Hello diff --git a/apps/blog-app/src/content/2022-12-31-my-second-post.md b/apps/blog-app/src/content/2022-12-31-my-second-post.md index 8108542c0..0a9ede668 100644 --- a/apps/blog-app/src/content/2022-12-31-my-second-post.md +++ b/apps/blog-app/src/content/2022-12-31-my-second-post.md @@ -3,8 +3,6 @@ title: My Second Post slug: 2022-12-31-my-second-post --- -## My Second Post - - [Home](/) - [Blog](http://localhost:3000/blog) - [About](/about) diff --git a/apps/blog-app/src/lib/blog-attributes.ts b/apps/blog-app/src/lib/blog-attributes.ts new file mode 100644 index 000000000..66babdff2 --- /dev/null +++ b/apps/blog-app/src/lib/blog-attributes.ts @@ -0,0 +1,4 @@ +export interface BlogAttributes { + title: string; + slug: string; +} diff --git a/packages/content/src/lib/content.spec.ts b/packages/content/src/lib/content.spec.ts index 2621e69ff..b6e4e35dc 100644 --- a/packages/content/src/lib/content.spec.ts +++ b/packages/content/src/lib/content.spec.ts @@ -2,46 +2,53 @@ import { fakeAsync, flushMicrotasks, TestBed } from '@angular/core/testing'; import { ActivatedRoute, convertToParamMap } from '@angular/router'; import { expect } from 'vitest'; import { injectContent } from './content'; -import { of } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { CONTENT_FILES_TOKEN } from './content-files-token'; import { ContentFile } from './content-file'; describe('injectContent', () => { - it("should provide the fallback 'No Content Found' message when no match between slug and files and no custom fallback provided", fakeAsync(() => { + type TestAttributes = { + slug: string; + }; + it("should return ContentFile object with empty filename, empty attributes, and default fallback 'No Content Found' as content when no match between slug and files and no custom fallback provided", fakeAsync(() => { const { injectContent } = setup({ routeParams: { slug: 'test' }, }); - let content; injectContent().subscribe((c) => { - content = c; + expect(c.content).toMatch('No Content Found'); + expect(c.attributes).toEqual({}); + expect(c.filename).toEqual(''); }); flushMicrotasks(); - expect(content).toMatch('No Content Found'); })); - it("should provide the custom fallback 'Custom Fallback' message when no match between slug and files and custom fallback 'Custom Fallback' provided", fakeAsync(() => { + it("should return ContentFile object with empty filename, empty attributes, and the custom fallback 'Custom Fallback' as content when no match between slug and files and custom fallback 'Custom Fallback' provided", fakeAsync(() => { const customFallback = 'Custom Fallback'; const routeParams = { slug: 'test' }; const { injectContent } = setup({ routeParams, customFallback }); - let content; injectContent().subscribe((c) => { - content = c; + expect(c.content).toMatch(customFallback); + expect(c.attributes).toEqual({}); + expect(c.filename).toEqual(''); }); flushMicrotasks(); - expect(content).toMatch(customFallback); })); - it('should provide the content of the file when match between slug and files', fakeAsync(() => { + it('should return ContentFile object with correct filename, correct attributes, and the correct content of the file when match between slug and files', fakeAsync(() => { const routeParams = { slug: 'test' }; const contentFiles = [ { filename: '/src/content/dont-match.md', - attributes: {}, + attributes: { + slug: 'dont-match', + }, content: 'Dont Match', }, { filename: '/src/content/test.md', - attributes: {}, + attributes: { + slug: 'test', + }, content: 'Test Content', }, ]; @@ -49,26 +56,30 @@ describe('injectContent', () => { routeParams, contentFiles, }); - let content; injectContent().subscribe((c) => { - content = c; + expect(c.content).toMatch('Test Content'); + expect(c.attributes).toEqual({ slug: 'test' }); + expect(c.filename).toEqual('/src/content/test.md'); }); flushMicrotasks(); - expect(content).toMatch('Test Content'); })); - it('should provide the content of the file when match between custom param and files', fakeAsync(() => { + it('should return ContentFile object with correct filename, correct attributes, and the correct content of the file when match between custom param and files', fakeAsync(() => { const customParam = 'customSlug'; - const routeParams = { customSlug: 'custom-test' }; - const contentFiles = [ + const routeParams = { customSlug: 'custom-slug-test' }; + const contentFiles: ContentFile[] = [ { filename: '/src/content/dont-match.md', - attributes: {}, + attributes: { + slug: 'dont-match', + }, content: 'Dont Match', }, { - filename: '/src/content/custom-test.md', - attributes: {}, + filename: '/src/content/custom-slug-test.md', + attributes: { + slug: 'custom-slug-test', + }, content: 'Test Content', }, ]; @@ -77,12 +88,12 @@ describe('injectContent', () => { routeParams, contentFiles, }); - let content; injectContent().subscribe((c) => { - content = c; + expect(c.content).toMatch('Test Content'); + expect(c.attributes).toEqual({ slug: 'custom-slug-test' }); + expect(c.filename).toEqual('/src/content/custom-slug-test.md'); }); flushMicrotasks(); - expect(content).toMatch('Test Content'); })); function setup( @@ -90,7 +101,7 @@ describe('injectContent', () => { customParam: string; customFallback: string; routeParams: { [key: string]: any }; - contentFiles: ContentFile[]; + contentFiles: ContentFile[]; }> ) { TestBed.configureTestingModule({ @@ -109,9 +120,11 @@ describe('injectContent', () => { useValue: args.contentFiles ?? [], }); return { - injectContent: () => + injectContent: (): Observable< + ContentFile> + > => TestBed.runInInjectionContext(() => - injectContent(args.customParam, args.customFallback) + injectContent(args.customParam, args.customFallback) ), }; } diff --git a/packages/content/src/lib/content.ts b/packages/content/src/lib/content.ts index af1768608..9a859f654 100644 --- a/packages/content/src/lib/content.ts +++ b/packages/content/src/lib/content.ts @@ -4,6 +4,8 @@ import { inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { map } from 'rxjs/operators'; import { injectContentFiles } from './inject-content-files'; +import { Observable } from 'rxjs'; +import { ContentFile } from './content-file'; /** * Retrieves the static content using the provided param @@ -11,15 +13,25 @@ import { injectContentFiles } from './inject-content-files'; * @param param route parameter (default: 'slug') * @param fallback fallback text if content file is not found (default: 'No Content Found') */ -export function injectContent(param = 'slug', fallback = 'No Content Found') { +export function injectContent< + Attributes extends Record = Record +>( + param = 'slug', + fallback = 'No Content Found' +): Observable>> { const route = inject(ActivatedRoute); - const contentFiles = injectContentFiles(); + const contentFiles = injectContentFiles>(); return route.paramMap.pipe( map((params) => params.get(param)), map((slug) => { return ( - contentFiles.find((file) => file.filename === `/src/content/${slug}.md`) - ?.content || fallback + contentFiles.find( + (file) => file.filename === `/src/content/${slug}.md` + ) || { + attributes: {}, + filename: '', + content: fallback, + } ); }) );