WYSIWYG markdown Editor 🍼 Milkdown for Angular out of box, only supports Angular 17+. Allow you to use native Angular components to create nodeView/pluginView/widgetView, and provide corresponding examples.
ng-milkdown is only supported by Angular version >=17.0.0.
ng-milkdown-crepe only supports Angular version >=18.0.0.
| Angular | ng-milkdown |
|---|---|
| 17.0.0+ | 0.0.3 |
| 18.0.0+ | 0.1.0-beta0 |
You can run this example by:
git clone https://github.com/ousc/ng-milkdown.git
cd ng-milkdown
npm install
npm run starthttps://ousc.github.io/ng-milkdown
Angular adapter for ProseMirror, only supports Angular 17+.
https://github.com/ousc/ng-prosemirror-adapter
-
theme-nord(preset) -
preset-commonmark(preset) -
plugin-listener(preset) -
preset-gfm(supported) -
plugin-history(supported) -
plugin-shiki(supported) -
plugin-clipboard(supported) -
plugin-cursor(supported) -
plugin-latex(supported) -
plugin-block(supported) -
plugin-indent(supported) -
plugin-tooltip(supported) -
plugin-slash(working) -
plugin-diagram(working) -
plugin-emoji(working) -
plugin-cursor(supported) -
plugin-trailing(supported) -
plugin-upload(supported) -
plugin-collab(working) -
plugin-copilot(working)
usage of plugins can be found in example;
npm install ng-milkdown -S<ng-milkdown-provider>
<ng-milkdown-crepe
[(ngModel)]="value"
[plugins]="plugins"
[features]="features"
[featureConfigs]="featureConfigs"
[(loading)]="loading"
[spinner]="spinner"
(beforeReady)="beforeReady($event)"
(ngModelChange)="onChange($event)"
/>
</ng-milkdown-provider>import "@milkdown/crepe/theme/common/style.css";
import "@milkdown/crepe/theme/nord.css";
import {NgMilkdownCrepeEditor} from "./ng-milkdown.type";
@Component({...})
export class CrepeEditorComponent {
features = {
[Crepe.Feature.Placeholder]: false
}
beforeReady({crepe}: NgMilkdownCrepeEditor) {
editor.config(ctx => {
ctx.set(blockquoteAttr.key, () => ({
class: "border-l-4 border-nord10 pl-4 dark:border-nord8",
}));
ctx.set(inlineCodeAttr.key, () => ({
class: "font-mono text-nord10 tracking-tight dark:text-nord8",
}));
});
}
plugins = [imageInlineComponent];
value = 'Hello, World!';
onChange(markdownText: string) {
console.log({markdownText});
}
}<ng-milkdown-provider>
<ng-milkdown
[(ngModel)]="value"
[plugins]="plugins"
[(loading)]="loading"
[spinner]="spinner"
(beforeReady)="beforeReady($event)"
(ngModelChange)="onChange($event)"
/>
</ng-milkdown-provider>const tooltip = tooltipFactory('text-tooltip')
@Component({...})
export class WorkGroundComponent {
@ViewChild(NgMilkdownProvider, {static: true}) provider: NgMilkdownProvider;
beforeReady({editor}: NgMilkdownEditor) {
editor.config(ctx => {
ctx.set(editorViewOptionsCtx, {
attributes: {
class: "prose dark:prose-invert outline-none mx-auto px-2 py-4 max-w-full box-border milkdown-theme-nord editor",
spellcheck: "false",
},
});
ctx.set(blockquoteAttr.key, () => ({
class: "border-l-4 border-nord10 pl-4 dark:border-nord8",
}));
ctx.set(inlineCodeAttr.key, () => ({
class: "font-mono text-nord10 tracking-tight dark:text-nord8",
}));
});
}
plugins = [
commonmark,
link,
history,
imageInlineComponent,
iframeComponent,
trailing,
block,
indent,
milkShiki,
$nodeView(codeBlockSchema.node, {component: CodeBlock}),
$pluginView(block.key, {component: Block}),
$nodeView(listItemSchema.node, {component: ListItem}),
tooltip,
$pluginView(tooltip.key, {component: Tooltip}),
$prosePlugin({component: Size}),
latex,
];
value = 'Hello, World!';
onChange(markdownText: string) {
console.log({markdownText});
}
}| Property | Description | Type | Default |
|---|---|---|---|
[classList] |
editor element class names | string[] |
[] |
[plugins] |
milkdown plugin to use | NgMilkdownPlugin[] |
[] |
[editor] |
pass in a fully controlled editor object | (HTMLElement) => Editor |
- |
[loading] |
set the loading status of editor | boolean |
true |
[spinner] |
custom spinner | TemplateRef<any> |
- |
[ngModel] |
current value , double binding | DefaultValue |
- |
(ngModelChange) |
callback when markdown change | EventEmitter<string> |
- |
(onReady) |
A callback function, can be executed when editor has bean created | Editor |
- |
@Component({
template: `
<button (click)="setBold($event)">
Bold
</button>
`,
...
})
export class ImageTooltipComponent extends NgMilkdownTooltip {
setBold(e: MouseEvent) {
e.preventDefault();
this.action(callCommand(toggleStrongCommand.key));
}
}@Component({
selector: 'block',
template: `
<div class="w-6 bg-slate-200 rounded hover:bg-slate-300 cursor-grab">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width={1.5} stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 12.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 18.75a.75.75 0 110-1.5.75.75 0 010 1.5z" />
</svg>
</div>
`,
styles:[],
standalone: true
})
export class BlockComponent extends NgMilkdownBlock {}More detailed examples and more plugins can be found in example;