本篇將介紹如何透過 TypeScript 把 CKeditor 包裝成 Angular 4 的 Directive,讓 Angular 4 能更方便使用 CKEditor。 並建立一個簡單的 ASP.NET Core Web API 跟 CKEditor 做存取資料的互動。
程式碼延續前兩篇的範例:ASP.NET Core + Angular 4 教學 - 從無到有 ASP.NET Core + Angular 4 教學 - Webpack打包
安裝 npm 套件 安裝 ckeditor,指令如下:
1 npm install --save ckeditor
CKEditor CKEditorDirective 因為 CKEditor 是透過 JavaScrupt 調用,為了方便在 Angular 中使用,我建立了一個 CKEditorDirective 包裝 CKEditor 成 Directive。 經由 CKEditorDirective 包裝後,使用存取 CKEditor 的值都是透過 ngModel,用起來就像 input text 一樣簡單。 shared\directives\ckeditor.directive.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import { Directive , Input , Attribute , Output , EventEmitter , OnChanges } from "@angular/core" ;declare var CKEDITOR : any; @Directive ({ selector : "[ckeditor][id][ngModel]" }) export class CKEditorDirective implements OnChanges { private ckeditor : any; @Input () config : any; @Input () ngModel : any; @Output () ngModelChange = new EventEmitter (); constructor ( @Attribute("id" ) private id: string ) { if (CKEDITOR === undefined || !id) { throw new Error ("The 'CKEDITOR' or the 'id' are not defined..." ); } } ngAfterViewInit ( ) { if (this .ckeditor == null ) { this .ckeditor = CKEDITOR .replace (this .id , this .config ); } this .ckeditor .on ("change" , () => this .ngModelChange .emit (this .ckeditor .getData ())); this .ckeditor .on ("instanceReady" , () => this .ckeditor .setData (this .ngModel )); } ngOnChanges ( ) { if (this .ckeditor ) { this .ckeditor .setData (this .ngModel ); this .ngModelChange .emit (this .ngModel ); } } }
載入套件 在 NgModule 的 declarations 注入 CKEditorDirective。 app\main.ts
1 2 3 4 5 6 7 8 9 10 import { CKEditorDirective } from "./shared/directives/ckeditor.directive" ;@NgModule ({ declarations : [ CKEditorDirective ] }) export class AppModule { }
因為是透過 npm 安裝 CKEditor,所以 CKEditor 相關的 plugins 跟 css 都在 node_modules/ckeditor 資料夾底下。 在 Startup.cs 的 Configure 加入靜態檔案路由,把 http://{domain}/ckeditor
指向 node_modules/ckeditor。 Startup.cs
1 2 3 4 5 6 7 8 9 10 public void Configure (IApplicationBuilder app ){ app.UseStaticFiles(new StaticFileOptions() { FileProvider = new PhysicalFileProvider( Path.Combine(Directory.GetCurrentDirectory(), @"node_modules/ckeditor" )), RequestPath = new PathString("/ckeditor" ) }); }
CKEDITOR_BASEPATH 改為 Startup.cs 中設定的 http://{domain}/ckeditor
位置,並把 ckeditor 加入參考。 app\bundle-vendors.ts 加入以下程式碼:
1 2 3 4 declare var CKEDITOR_BASEPATH : string; eval (`CKEDITOR_BASEPATH = "/ckeditor/";` );require ("ckeditor" );
CKEDITOR_BASEPATH 一定要在載入 ckeditor 前賦予設定值。
範例 app\components\editor.component.html
1 2 3 4 5 6 7 8 9 10 <h2 > Editor</h2 > <div > {{message}}</div > <br /> <textarea ckeditor id ="editor" [(ngModel )]="content" [config ]="ckeditorConfig" > </textarea > <div > <input type ="button" value ="Send" (click )="send()" /> <input type ="button" value ="Clear" (click )="clear()" /> </div >
app\components\editor.component.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import { Component } from "@angular/core" ;import { Http , Headers } from "@angular/http" ;import { ResultModel } from "../shared/models/result.model" ;@Component ({ template : require ("./editor.component.html" ) }) export class EditorComponent { private api : string = "/api/editor" ; message : string; content : string; ckeditorConfig : any = { height : "300px" }; constructor (private http: Http ) { } ngAfterViewInit ( ) { this .http .get (this .api ).subscribe ( (response ) => { let result : ResultModel = response.json (); if (!result.IsSuccess ) { this .showMessage (result.Message ); this .clear (); } else { this .content = result.Data ; } }); } send (): void { this .clearMessage (); let headers = new Headers ({ "Content-Type" : "application/json" }); this .http .put (this .api , JSON .stringify (this .content ), { headers : headers }).subscribe ( (response ) => { let result : ResultModel = response.json (); if (!result.IsSuccess ) { this .showMessage (result.Message ); } else { this .showMessage (`Saved successfully` ); } }); } clear (): void { this .content = "" ; } clearMessage (): void { this .message = "" ; } showMessage (message : string): void { this .message = message; } }
Controllers\EditorController.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 using Microsoft.AspNetCore.Mvc;using MyWebsite.Models;namespace MyWebsite.Controllers { [Route("api/[controller]" ) ] public class EditorController : Controller { private static string _content = string .Empty; [HttpGet ] public ResultModel Get () { var result = new ResultModel(); result.Data = _content; result.IsSuccess = result.Data != null ; return result; } [HttpPut ] public ResultModel Put ([FromBody]string content ) { var result = new ResultModel(); _content = content; result.IsSuccess = true ; return result; } } }
執行結果
範例程式碼 asp-net-core-angular-ckeditor