Playgrounds
A playground renders a component, a directive or a pipe next to an inspector with a control for each of its inputs. Readers change the values and see the result and its code at once, without writing a demo for every state.
See it
Playground
Settings
The demo sits on the left, and the inspector on the right. When the playground is narrower than 640 pixels, the inspector moves below the demo.
- Settings has a row for each input: its name, a chip with the type of its value, and a control. Hover the name to read the input's description, taken from its doc comment.
- Recreate creates the demo again every time an input changes, instead of updating the inputs of the same instance. Use it for components that read an input only once.
- Reset appears as soon as a value differs from its default, and sets every input back.
- The code button beside the demo shows, below it, the template of the current state.
Use it
Add the playground to the
playgroundsof the page. The key is the name of the playground:ng-doc.page.tsTypeScriptimport {NgDocPage }from '@ng-doc/core'; import {NgDocTagComponent }from '@ng-doc/ui-kit'; const MyPage:NgDocPage = { title: 'Tag', mdFile: './index.md', playgrounds: { TagPlayground: { target:NgDocTagComponent , template: `<ng-doc-selector>Tag Label</ng-doc-selector>`, }, }, }; export default MyPage;Render it in the Markdown with the
playgroundaction:index.mdTwig{{ NgDocActions.playground("TagPlayground") }}
target is the class to play with. template is the Angular template that renders it, and <ng-doc-selector> stands for the target's selector. Prefer it to the real selector: the playground keeps working when you rename the selector. A standalone target needs no imports; for a target declared in an NgModule, add the module to the imports of the page.
The template is static text: it can't read variables or call functions of the page. Use data for values (see below).
Inputs and controls
NgDoc reads the inputs of the target when it builds the page: signal inputs ( and , required or not), properties, and the inputs of its base classes. An alias becomes the name of the row. The type of the input chooses the control:
| Type of the input | Control |
|---|---|
string | A text field. |
number | A number field. |
boolean | A checkbox. |
| A union of literals, or an alias of it | A list of the values, such as 'small' | 'medium' | 'large'. |
| A type with a type control | Your control ( |
| Any other type | No control. In development, the browser console names the skipped input. |
For a signal input, the type is the type argument: size = gets the list of . An input with a transform, such as rounded = , gets the control of its first type argument, here a checkbox. An optional or nullable string, number or boolean input, such as label = , gets the control of its type too.
The list of a union shows the values in the order the type is written. is written 'small' | 'medium' | 'large', so its list starts with small. The default value of the input is marked in the list.
Multiple selectors
When the target has several selectors, NgDoc renders a demo for each of them. To render only some, set selectors in the configuration or in the action:
{{ NgDocActions.playground("ButtonPlayground", { selectors: "button[ng-doc-button-flat]" }) }}Inputs set in the template
An input that the template sets gets no control. So you can bind an input to something that a control can't edit, such as a reference to another element:
import { NgDocPage } from '@ng-doc/core';
import { TooltipComponent } from './tooltip.component';
const MyPage: NgDocPage = {
title: 'Tooltip',
mdFile: './index.md',
playgrounds: {
TooltipPlayground: {
target: TooltipComponent,
template: `
<ng-doc-selector [anchor]="anchor">Hello</ng-doc-selector>
<input #anchor value="Hover me" />
`,
},
},
};
export default MyPage;To start with a value and keep the control, use the inputs or defaults options instead (see "Options").
Optional content
content adds toggles for parts of the template, for example the content that a component projects. Each key is a slot: write {{ content.<key> }} where it goes in the template. The inspector lists the slots under Content:
import { NgDocPage } from '@ng-doc/core';
import { NgDocIconComponent , NgDocTagComponent } from '@ng-doc/ui-kit';
const MyPage: NgDocPage = {
title: 'Tag',
mdFile: './index.md',
imports: [NgDocIconComponent ],
playgrounds: {
TagIconPlayground: {
target: NgDocTagComponent ,
template: `
<ng-doc-selector>
{{ content.icon }}
Tag Label
</ng-doc-selector>`,
content: {
icon: {
label: 'email icon',
template: '<ng-doc-icon icon="at-sign" [size]="16"></ng-doc-icon>',
},
},
},
},
};
export default MyPage;A component in a slot template must be in the imports of the page.
Playground
Settings
Content
Data
data passes values to the template, which reads them as data:
import { NgDocPage } from '@ng-doc/core';
import { NgDocTagComponent } from '@ng-doc/ui-kit';
const MyPage: NgDocPage = {
title: 'Tag',
mdFile: './index.md',
playgrounds: {
TagDataPlayground: {
target: NgDocTagComponent ,
template: `<ng-doc-selector>{{ data.array | json }}</ng-doc-selector>`,
data: {
array: [1, 2, 3],
},
},
},
};
export default MyPage;Playground
Settings
Directives and pipes
A directive works like a component. The template applies it to an element:
import { NgDocPage } from '@ng-doc/core';
import { NgDocRotatorDirective } from '@ng-doc/ui-kit';
const MyPage: NgDocPage = {
title: 'Rotator',
mdFile: './index.md',
playgrounds: {
RotatorPlayground: {
target: NgDocRotatorDirective ,
template: `<button ngDocRotator>Button</button>`,
},
},
};
export default MyPage;Playground
Settings
For a pipe, the parameters of transform after the value are the inputs. Take this pipe:
import { Pipe , PipeTransform } from '@angular/core';
@Pipe ({
name: 'formatDate ',
standalone: true,
})
export class FormatDatePipe implements PipeTransform {
/**
* Transform pipe method
* @param value - ISO date string
* @param display - Display format
* @param utc - Determines if the date should be displayed in UTC
*/
transform(
value: string,
display: 'date' | 'time' | 'datetime' = 'datetime',
utc: boolean,
): string {
const date: Date = new Date(value);
const timeZone: 'UTC' | undefined = utc ? 'UTC' : undefined;
if (display === 'date') {
return date.toLocaleDateString('en-US', { timeZone });
} else if (display === 'time') {
return date.toLocaleTimeString('en-US', { timeZone });
} else {
return date.toLocaleString('en-US', { timeZone });
}
}
}The template applies the pipe to a value, without parameters: NgDoc binds them to the controls.
import { NgDocPage } from '@ng-doc/core';
import { FormatDatePipe } from './format-date.pipe';
const MyPage: NgDocPage = {
title: 'Format date',
mdFile: './index.md',
playgrounds: {
DatePipePlayground: {
target: FormatDatePipe,
template: `{{ '2023-06-05T08:00:00.000Z' | formatDate }}`,
},
},
};
export default MyPage;Playground
Settings
The descriptions come from the @param tags of transform, and the defaults from its default parameter values.
Options
Set options in the configuration of the playground, or as the second argument of the action. The action's options win. They follow :
| Option | Type | Default | Description |
|---|---|---|---|
selectors | string | string[] | All | The selectors to render a demo for. |
expanded | boolean | false | Shows the code under the demo when the playground opens. |
hideSidePanel | boolean | false | Hides the inspector and shows only the demo. |
inspectorPosition | 'right' | 'bottom' | 'right' | Puts the inspector right of the demo, or below it so the demo gets the full width. |
inputs | Record<string, unknown> | โ | Values for the inputs when the playground opens. Reset returns to the defaults. |
defaults | Record<string, unknown> | โ | Defaults for the controls, instead of the target's own. Reset returns to them. |
hiddenInputs | string[] | โ | Inputs that get no control. |
data | Record<string, unknown> | โ | Values for the template. The action's data extends the configuration's. |
This playground hides the inspector, and starts with a rounded button:
{{ NgDocActions.playground("ButtonPlayground", { hideSidePanel: true, selectors: "button[ng-doc-button-flat]", inputs: { rounded: true }, data: { label: "Rounded Button" } }) }}For a large component, put the inspector below the demo. The demo gets the full width, and the controls flow into columns:
{{ NgDocActions.playground("TagPlayground", { inspectorPosition: "bottom" }) }}Playground
Settings
Controls for other inputs
NgDoc doesn't see inputs declared in the inputs of the decorator. Add them with controls, keyed by property name. The value is the type of the input, or an :
import { NgDocPage } from '@ng-doc/core';
import { BadgeComponent } from './badge.component';
const MyPage: NgDocPage = {
title: 'Badge',
mdFile: './index.md',
playgrounds: {
BadgePlayground: {
target: BadgeComponent,
template: `<ng-doc-selector>New</ng-doc-selector>`,
controls: {
label: 'string',
count : { type: 'number', alias: 'badgeCount', description: 'The number to show.' },
size: { type: 'NgDocTypeAlias', options: ['small', 'medium', 'large'] },
},
},
},
};
export default MyPage;An entry in controls also replaces the control that NgDoc chose for an input of the same name. The type NgDocTypeAlias shows a list of options; a type registered with shows your control.
Gotchas
WarningIn the action's options, put a space between two closing braces, such as
{ inputs: { a: 1 } }. Without it, Nunjucks reads}}as the end of the expression.
NoteThe legacy builders list the values of a union in the order the TypeScript checker created them, which can differ from the written order (
Upgrade to 22.0 [7. The new engine] ).
Next: