diff --git a/packages/content/src/lib/content.spec.ts b/packages/content/src/lib/content.spec.ts index 828382ce4..4be71ad3e 100644 --- a/packages/content/src/lib/content.spec.ts +++ b/packages/content/src/lib/content.spec.ts @@ -11,6 +11,7 @@ import { Observable, of } from 'rxjs'; import { CONTENT_FILES_TOKEN } from './content-files-token'; import { injectContent } from './content'; import { ContentFile } from './content-file'; +import { RenderTaskService } from './render-task.service'; describe('injectContent', () => { type TestAttributes = { @@ -236,6 +237,7 @@ Test agx Content`), ) { TestBed.configureTestingModule({ providers: [ + RenderTaskService, { provide: ActivatedRoute, useValue: { diff --git a/packages/content/src/lib/content.ts b/packages/content/src/lib/content.ts index 5479fd288..6a0a52feb 100644 --- a/packages/content/src/lib/content.ts +++ b/packages/content/src/lib/content.ts @@ -3,12 +3,13 @@ import { inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable, of } from 'rxjs'; -import { map, switchMap } from 'rxjs/operators'; +import { map, switchMap, tap } from 'rxjs/operators'; import { ContentFile } from './content-file'; import { CONTENT_FILES_TOKEN } from './content-files-token'; import { parseRawContentFile } from './parse-raw-content-file'; import { waitFor } from './utils/zone-wait-for'; +import { RenderTaskService } from './render-task.service'; function getContentFile< Attributes extends Record = Record @@ -88,6 +89,8 @@ export function injectContent< fallback = 'No Content Found' ): Observable>> { const contentFiles = inject(CONTENT_FILES_TOKEN); + const renderTaskService = inject(RenderTaskService); + const task = renderTaskService.addRenderTask(); if (typeof param === 'string' || 'param' in param) { const prefix = typeof param === 'string' ? '' : `${param.subdirectory}/`; @@ -110,7 +113,8 @@ export function injectContent< attributes: {}, content: fallback, }); - }) + }), + tap(() => renderTaskService.clearRenderTask(task)) ); } else { return getContentFile( @@ -118,6 +122,6 @@ export function injectContent< '', param.customFilename, fallback - ); + ).pipe(tap(() => renderTaskService.clearRenderTask(task))); } } diff --git a/packages/content/src/lib/inject-content-files.spec.ts b/packages/content/src/lib/inject-content-files.spec.ts index c590c622c..3102ad6a3 100644 --- a/packages/content/src/lib/inject-content-files.spec.ts +++ b/packages/content/src/lib/inject-content-files.spec.ts @@ -6,6 +6,7 @@ import { injectContentFiles, InjectContentFilesFilterFunction, } from './inject-content-files'; +import { RenderTaskService } from './render-task.service'; describe('injectContentFiles', () => { it('should provide empty files if no files provided', () => { @@ -78,6 +79,9 @@ describe('injectContentFiles', () => { contentFiles: ContentFile[] = [], filterFn?: InjectContentFilesFilterFunction ) { + TestBed.configureTestingModule({ + providers: [RenderTaskService], + }); TestBed.overrideProvider(CONTENT_FILES_LIST_TOKEN, { useValue: contentFiles, }); diff --git a/packages/content/src/lib/inject-content-files.ts b/packages/content/src/lib/inject-content-files.ts index d9046ebcb..c0c13812a 100644 --- a/packages/content/src/lib/inject-content-files.ts +++ b/packages/content/src/lib/inject-content-files.ts @@ -1,19 +1,24 @@ import { ContentFile } from './content-file'; import { inject } from '@angular/core'; import { CONTENT_FILES_LIST_TOKEN } from './content-files-list-token'; +import { RenderTaskService } from './render-task.service'; export function injectContentFiles>( filterFn?: InjectContentFilesFilterFunction ): ContentFile[] { + const renderTaskService = inject(RenderTaskService); + const task = renderTaskService.addRenderTask(); const allContentFiles = inject( CONTENT_FILES_LIST_TOKEN ) as ContentFile[]; + renderTaskService.clearRenderTask(task); if (filterFn) { const filteredContentFiles = allContentFiles.filter(filterFn); return filteredContentFiles; } + return allContentFiles; } diff --git a/packages/content/src/lib/markdown-content-renderer.service.ts b/packages/content/src/lib/markdown-content-renderer.service.ts index dfdb4e21f..2ce6a8217 100644 --- a/packages/content/src/lib/markdown-content-renderer.service.ts +++ b/packages/content/src/lib/markdown-content-renderer.service.ts @@ -1,18 +1,12 @@ -import { - inject, - Injectable, - InjectionToken, - PLATFORM_ID, - Provider, -} from '@angular/core'; +import { inject, Injectable, InjectionToken, Provider } from '@angular/core'; import { getHeadingList } from 'marked-gfm-heading-id'; import { ContentRenderer, TableOfContentItem } from './content-renderer'; import { MarkedSetupService } from './marked-setup.service'; +import { RenderTaskService } from './render-task.service'; @Injectable() export class MarkdownContentRendererService implements ContentRenderer { - platformId = inject(PLATFORM_ID); #marked = inject(MarkedSetupService, { self: true }); async render(content: string): Promise { @@ -38,6 +32,8 @@ export function withMarkdownRenderer( options?: MarkdownRendererOptions ): Provider { return [ + MarkedSetupService, + RenderTaskService, { provide: ContentRenderer, useFactory: () => new MarkdownContentRendererService(), @@ -55,7 +51,7 @@ export function withMarkdownRenderer( } export function provideContent(...features: Provider[]) { - return [...features, MarkedSetupService]; + return [...features]; } export const MERMAID_IMPORT_TOKEN = new InjectionToken< diff --git a/packages/content/src/lib/markdown.component.ts b/packages/content/src/lib/markdown.component.ts index f06801ac1..47e11f8a6 100644 --- a/packages/content/src/lib/markdown.component.ts +++ b/packages/content/src/lib/markdown.component.ts @@ -1,7 +1,6 @@ import { AsyncPipe, isPlatformBrowser } from '@angular/common'; import { AfterViewChecked, - ChangeDetectorRef, Component, Input, NgZone, @@ -48,7 +47,7 @@ export default class AnalogMarkdownComponent }); private mermaid: typeof import('mermaid') | undefined; - public content$: Observable = of(''); + public content$: Observable = this.getContentSource(); @Input() content!: string | object | undefined | null; @Input() classes = 'analog-markdown'; @@ -79,15 +78,19 @@ export default class AnalogMarkdownComponent const componentRef = this.container.createComponent(this.content as any); componentRef.changeDetectorRef.detectChanges(); } else { - this.content$ = this.route.data.pipe( - map((data) => this.content ?? data['_analogContent']), - mergeMap((contentString) => this.renderContent(contentString)), - map((content) => this.sanitizer.bypassSecurityTrustHtml(content)), - catchError((e) => of(`There was an error ${e}`)) - ); + this.content$ = this.getContentSource(); } } + getContentSource() { + return this.route.data.pipe( + map((data) => this.content ?? data['_analogContent']), + mergeMap((contentString) => this.renderContent(contentString)), + map((content) => this.sanitizer.bypassSecurityTrustHtml(content)), + catchError((e) => of(`There was an error ${e}`)) + ); + } + async renderContent(content: string): Promise { return this.contentRenderer.render(content); } diff --git a/packages/content/src/lib/render-task.service.ts b/packages/content/src/lib/render-task.service.ts new file mode 100644 index 000000000..7eeca8a5b --- /dev/null +++ b/packages/content/src/lib/render-task.service.ts @@ -0,0 +1,18 @@ +import { + Injectable, + inject, + ɵPendingTasks as PendingTasks, +} from '@angular/core'; + +@Injectable() +export class RenderTaskService { + #pendingTasks = inject(PendingTasks); + + addRenderTask() { + return this.#pendingTasks.add(); + } + + clearRenderTask(id: number) { + this.#pendingTasks.remove(id); + } +}