mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
docs(docs-infra): Support linking to individual playground templates
This commit is contained in:
parent
a3639e2258
commit
6e7b3c2aa6
5 changed files with 29 additions and 9 deletions
|
|
@ -151,7 +151,7 @@ export type PlaygroundRouteData = {
|
|||
starterTemplate?: PlaygroundTemplate;
|
||||
};
|
||||
|
||||
export type PlaygroundTemplate = Required<Pick<NavigationItem, 'path' | 'label'>>;
|
||||
export type PlaygroundTemplate = Required<Pick<NavigationItem, 'path' | 'label'>> & {id: string};
|
||||
|
||||
// Note: only the fields being used are defined in this type
|
||||
export interface PackageJson {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export async function generatePlaygroundRoutes(
|
|||
const templates = Object.entries(configs).map(([path, config]) => ({
|
||||
path: `playground/${path}`,
|
||||
label: config.title,
|
||||
id: path,
|
||||
}));
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -12,17 +12,17 @@
|
|||
</div>
|
||||
|
||||
@if (embeddedEditorComponent) {
|
||||
<ng-container *ngComponentOutlet="embeddedEditorComponent" />
|
||||
<ng-container *ngComponentOutlet="embeddedEditorComponent" />
|
||||
}
|
||||
|
||||
<ng-template #templatesMenu>
|
||||
<ul class="adev-template-dropdown" cdkMenu>
|
||||
@for (template of templates; track template.path) {
|
||||
<li>
|
||||
<button cdkMenuItem type="button" (click)="changeTemplate(template)">
|
||||
<span>{{ template.label }}</span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button cdkMenuItem type="button" (click)="changeTemplate(template)">
|
||||
<span>{{ template.label }}</span>
|
||||
</button>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</ng-template>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {EmbeddedTutorialManager} from '../../editor';
|
|||
import {NodeRuntimeSandbox} from '../../editor/node-runtime-sandbox.service';
|
||||
|
||||
import TutorialPlayground from './playground.component';
|
||||
import {provideRouter} from '@angular/router';
|
||||
|
||||
describe('TutorialPlayground', () => {
|
||||
let component: TutorialPlayground;
|
||||
|
|
@ -28,6 +29,7 @@ describe('TutorialPlayground', () => {
|
|||
TestBed.configureTestingModule({
|
||||
imports: [TutorialPlayground],
|
||||
providers: [
|
||||
provideRouter([]),
|
||||
{
|
||||
provide: WINDOW,
|
||||
useValue: fakeWindow,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ import {
|
|||
EnvironmentInjector,
|
||||
PLATFORM_ID,
|
||||
Type,
|
||||
effect,
|
||||
inject,
|
||||
input,
|
||||
} from '@angular/core';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
import {CdkMenu, CdkMenuItem, CdkMenuTrigger} from '@angular/cdk/menu';
|
||||
|
|
@ -27,6 +29,7 @@ import {injectNodeRuntimeSandbox} from '../../editor/index';
|
|||
import type {NodeRuntimeSandbox} from '../../editor/node-runtime-sandbox.service';
|
||||
|
||||
import PLAYGROUND_ROUTE_DATA_JSON from '../../../../src/assets/tutorials/playground/routes.json';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'adev-playground',
|
||||
|
|
@ -36,12 +39,16 @@ import PLAYGROUND_ROUTE_DATA_JSON from '../../../../src/assets/tutorials/playgro
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export default class PlaygroundComponent {
|
||||
readonly templateId = input<string | undefined>();
|
||||
|
||||
private readonly changeDetectorRef = inject(ChangeDetectorRef);
|
||||
private readonly environmentInjector = inject(EnvironmentInjector);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly isServer = isPlatformServer(inject(PLATFORM_ID));
|
||||
private readonly router = inject(Router);
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
|
||||
readonly templates = PLAYGROUND_ROUTE_DATA_JSON.templates;
|
||||
readonly templates: PlaygroundTemplate[] = PLAYGROUND_ROUTE_DATA_JSON.templates;
|
||||
readonly defaultTemplate = PLAYGROUND_ROUTE_DATA_JSON.defaultTemplate;
|
||||
readonly starterTemplate = PLAYGROUND_ROUTE_DATA_JSON.starterTemplate;
|
||||
|
||||
|
|
@ -54,6 +61,11 @@ export default class PlaygroundComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
effect(() => {
|
||||
const foundTemplate = this.templates.find((t) => t.id === this.templateId());
|
||||
this.changeTemplate(foundTemplate ?? this.defaultTemplate);
|
||||
});
|
||||
|
||||
// If using `async-await`, `this` will be captured until the function is executed
|
||||
// and completed, which can lead to a memory leak if the user navigates away from
|
||||
// the playground component to another page.
|
||||
|
|
@ -66,7 +78,7 @@ export default class PlaygroundComponent {
|
|||
this.nodeRuntimeSandbox = nodeRuntimeSandbox;
|
||||
this.embeddedEditorComponent = embeddedEditorComponent;
|
||||
}),
|
||||
switchMap(() => this.loadTemplate(this.defaultTemplate.path)),
|
||||
switchMap(() => this.loadTemplate(this.selectedTemplate.path)),
|
||||
takeUntilDestroyed(this.destroyRef),
|
||||
)
|
||||
.subscribe(() => {
|
||||
|
|
@ -80,6 +92,11 @@ export default class PlaygroundComponent {
|
|||
}
|
||||
|
||||
async changeTemplate(template: PlaygroundTemplate): Promise<void> {
|
||||
this.router.navigate([], {
|
||||
relativeTo: this.route,
|
||||
queryParams: {templateId: template.id},
|
||||
replaceUrl: true,
|
||||
});
|
||||
this.selectedTemplate = template;
|
||||
await this.loadTemplate(template.path);
|
||||
await this.nodeRuntimeSandbox!.reset();
|
||||
|
|
|
|||
Loading…
Reference in a new issue