Angular速成4 搭建一个markdown编辑器
搭建一个markdown编辑器
https://ace.c9.io 是一个挺好的文本编辑器
用 https://marked.js.org 编译markdown
用 https://highlightjs.org 做语法高亮
ViewChild
https://angular.io/api/core/ViewChild
ViewChild修饰符可以让父组件获取子组件的值,或者说把子组件的值传给父组件。
是父子组件传数据的方法之一。是一种引用或者说关联。
需要提供三个参数。
selector,read和static。默认只需传selector。
可以用比如@ViewChild(“editor”)修饰一个组件类的成员a。
配和模板<div #editor>
这样如果a发生改变,模板也会改变。组件就可以对html元素进行控制。
html元素里用#是定义一个模板变量。比如<div #editor>
https://angular.io/guide/template-reference-variables
selector还可以直接传类等数据。这里不深入。
如果我们用@ViewChild做了关联,并且要加一些初始化逻辑,那么应该在ngAfterViewInit()做。
如果在ngOnInit()里做可能会出问题,因为ngAfterViewInit()的时机更晚,可能并没有实际初始化完毕。
ace编辑器
ng new
起一个干净的项目
ng add ace-builds
安装ace最新的1.4.13版本
模板改为
<div class="app-ace-editor" #editor style="width: 48%;height: 400px;"></div>
定义一个模板变量editor
组件改为
import { Component, AfterViewInit, ElementRef, ViewChild } from '@angular/core';
import * as ace from "ace-builds";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements AfterViewInit {
title = 'ng-test3';
@ViewChild("editor") private editor!: ElementRef;
private aceEditor: any;
constructor() {
}
ngAfterViewInit(): void {
ace.config.set("fontSize", "18px");
ace.config.set('basePath', 'https://cdn.jsdelivr.net/npm/ace-builds@1.4.13/src-min-noconflict');
this.aceEditor = ace.edit(this.editor.nativeElement);
this.aceEditor.session.setValue("<p>wtf电池内部恶势力包括</p>");
this.aceEditor.setTheme('ace/theme/github');
this.aceEditor.session.setMode('ace/mode/markdown');
}
}
引入了AfterViewInit,ElementRef,ViewChild,ace-builds。
ViewChild关联模板里的editor
@ViewChild(“editor”) private editor!: ElementRef;
editor后面如果不加!,ts会报错,要求初始化。
加!告诉ts自己有把握,不用检查。
ngAfterViewInit里对editor初始化。
做font、mode、path、theme等的配置。
此时ng serve后已经可以编辑文本了。
marked
ng add marked
安装了4.0.4版本
引入 import marked from "marked"
;
会报错
error TS2307: Cannot find module 'marked' or its corresponding type declarations.
因为缺少type的定义。对比项目下/node_modules/ace-builds和/node_modules/marked。
ace-builds里提供了ace-modules.d.ts和ace.d.ts,而marked没有。
npm i @types/marked
可以安装对应的type,然后组件里import { marked } from "marked";
可以编过了。
暂时不太清楚这个type的来龙去脉和约定。
按 https://angular.io/guide/using-libraries
一般,库会包含.d.ts。
如果没有,就得找@types对应的库。
如果再没有,就得自己手动添加。
模板里添加
<div #finalHtml style="width: 48%;height: 600px; float:right; overflow:scroll"></div>
组件
import { Component, AfterViewInit, ElementRef, ViewChild } from '@angular/core';
import * as ace from "ace-builds";
import { marked } from "marked";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements AfterViewInit {
title = 'ng-test3';
@ViewChild("editor") private editor!: ElementRef;
@ViewChild('finalHtml') private finalHtml!: ElementRef;
private aceEditor: any;
constructor() {
}
ngAfterViewInit(): void {
ace.config.set("fontSize", "18px");
//ace.config.set('basePath', 'https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.13/');
//ace.config.set('basePath', 'https://unpkg.com/ace-builds@1.4.12/src-noconflict');
ace.config.set('basePath', 'https://cdn.jsdelivr.net/npm/ace-builds@1.4.13/src-min-noconflict');
this.aceEditor = ace.edit(this.editor.nativeElement);
this.aceEditor.session.setValue("* <p>wtf电池内部恶势力包括</p>");
this.aceEditor.setTheme('ace/theme/github');
this.aceEditor.session.setMode('ace/mode/markdown');
this.aceEditor.on('change', () => this.onUpdateText());
}
onUpdateText(): void {
this.finalHtml.nativeElement.innerHTML = marked.parse(this.aceEditor.session.getValue());
}
}
finalHtml关联到模板。
监听ace的change事件。
当ace编辑器的文本发生改变时,用marked.parse()解析文本,并赋给finalHtml的innerHTML即可实时更新markdown显示。
highlightjs
ng add highlight.js
组件里引入
import HighlightJS from 'highlight.js';
import c from 'highlight.js/lib/languages/c';
import cpp from 'highlight.js/lib/languages/cpp';
import python from 'highlight.js/lib/languages/python';
import typescript from 'highlight.js/lib/languages/typescript';
初始化时注册
HighlightJS.registerLanguage('c', c);
HighlightJS.registerLanguage('cpp', cpp);
HighlightJS.registerLanguage('python', python);
HighlightJS.registerLanguage('typescript', typescript);
初始化marked时设置一下选项
marked.setOptions({
langPrefix:'hljs language-',
highlight: function(code, lang) {
const language = HighlightJS.getLanguage(lang) ? lang : 'plaintext';
return HighlightJS.highlight(code, { language }).value;
},
});
angular.json的styles里添加喜欢的风格
“./node_modules/highlight.js/scss/monokai-sublime.scss”
参考
https://blog.shhdharmen.me/how-to-setup-ace-editor-in-angular
https://blog.angular-university.io/angular-viewchild/
https://stackoverflow.com/questions/31548311/angular-html-binding
https://pretagteam.com/question/cannot-find-module-or-its-corresponding-type-declarations-when-using-my-own-library