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>
 
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