Elixir / Phoenix — CKEditor 5導入篇

Deniel Chiang
14 min readApr 1, 2020

--

CKEditor 5 重製版 — 自己動手包起來之後產生的重製版CKEditor 5,我們來把它加到專案裡來用吧!

Photo by Glen Carrie on Unsplash

Genesis

接續CKEditor 5 重製版 — 自己動手包起來之後已產生build files,如果還沒有產生重製版的朋友,建議從CKEditor 5 重製版 — 自己動手包起來開始收看喔,或者是從CKEditor 5 重製版 — 需求談起來更好,畢竟談得好的需求讓你上天堂…

Concept

由於Phoenix Framework有一種以上的方式來處理assets,詳情請看官方文件Default Configuration And Workflow這段:

The css and js directories inside of assets are a convention. Brunch will simply look for all files in assetsexcluding assets/static and sort all found files by their type.

Processed and concatenated javascript will be put into priv/static/js/app.js, styles will be in priv/static/css/app.css.

所以我們這次選用比較無痛的方式,而且也可以很快理解Phoenix在這個部分到底做了什麼。

Steps

Copy build files to phoenix

產生一個新專案ckeditor_integration_demo,然後把上一篇專案裡build出來的結果,複製到我們phoenix產生的新專案底下

$ cp -R ~/develop/ckeditor5-build-classic/build ~/develop/ckeditor_integration_demo/assets/vendor/ckeditor5/

Git Branch — add_into_asset

Write some code

這裡我們可以感受到Phoenix快速開發的優點,直接產生一組模板出來修改成我們要的內容,我們只需定義資料庫欄位名稱與類別,其他的都會從標準CRUD來產生。

如果你想知道更詳細Phoenix可以幫我們做什麼?

$ mix phx
Phoenix v1.4.16
Productive. Reliable. Fast.
A productive web framework that does not compromise speed or maintainability.
Available tasks:mix phx.digest # Digests and compresses static files
mix phx.digest.clean # Removes old versions of static assets.
mix phx.gen.cert # Generates a self-signed certificate for HTTPS testing
mix phx.gen.channel # Generates a Phoenix channel
mix phx.gen.context # Generates a context with functions around an Ecto schema
mix phx.gen.embedded # Generates an embedded Ecto schema file
mix phx.gen.html # Generates controller, views, and context for an HTML resource
mix phx.gen.json # Generates controller, views, and context for a JSON resource
mix phx.gen.presence # Generates a Presence tracker
mix phx.gen.schema # Generates an Ecto schema and migration file
mix phx.gen.secret # Generates a secret
mix phx.new # Creates a new Phoenix v1.4.16 application
mix phx.new.ecto # Creates a new Ecto project within an umbrella project
mix phx.new.web # Creates a new Phoenix web project within an umbrella project
mix phx.routes # Prints all routes
mix phx.server # Starts applications and their servers

這次我們來試試看完整產生到頁面再來修改成我們希望的內容吧!

$ mix phx.gen.html Content Post posts title:string content:string
* creating lib/ckeditor_integration_demo_web/controllers/post_controller.ex
* creating lib/ckeditor_integration_demo_web/templates/post/edit.html.eex
* creating lib/ckeditor_integration_demo_web/templates/post/form.html.eex
* creating lib/ckeditor_integration_demo_web/templates/post/index.html.eex
* creating lib/ckeditor_integration_demo_web/templates/post/new.html.eex
* creating lib/ckeditor_integration_demo_web/templates/post/show.html.eex
* creating lib/ckeditor_integration_demo_web/views/post_view.ex
* creating test/ckeditor_integration_demo_web/controllers/post_controller_test.exs
* creating lib/ckeditor_integration_demo/content/post.ex
* creating priv/repo/migrations/20200325180149_create_posts.exs
* creating lib/ckeditor_integration_demo/content.ex
* injecting lib/ckeditor_integration_demo/content.ex
* creating test/ckeditor_integration_demo/content_test.exs
* injecting test/ckeditor_integration_demo/content_test.exs
Add the resource to your browser scope in lib/ckeditor_integration_demo_web/router.ex:resources "/posts", PostControllerRemember to update your repository by running migrations:$ mix ecto.migrate

上面主要是產生一組controller, views, and context for an HTML resource;我們context命名為Content,modal命名為Post,而schema table name命名為posts;給他兩個欄位:Title、content,且兩個欄位都是string類別。

Phoenix有很貼心的提醒你要記得把下面這段加到lib/ckeditor_integration_demo_web/router.ex

# lib/ckeditor_integration_demo_web/router.exresources "/posts", PostController

跑一下ecto.migrate

$ mix ecto.migrate
Compiling 5 files (.ex)
.....

啟動一下server

$ mix phx.server
.....

打開瀏覽器的http://localhost:4000/posts,看看Phoenix幫我們做了什麼

$ open http://localhost:4000/posts
Photo by Deniel Chiang

新增、修改、查詢都幫我們產生了,接下來我們先把頁面的content從text改成textarea吧。

# lib/ckeditor_integration_demo_web/templates/post/form.html.eex<%= text_input f, :title %>     <---- 原本的
<%= textarea f, :content %> <---- 改成這樣

再來要是針對ckeditor5的步驟了,首先要先增加一個deps

$ cd assets && npm install --save-dev @babel/plugin-transform-runtime

接著把增加的@babel/plugin-transform-runtime以及原本就有的presets加到babel-loader裡

# assets/webpack.config.js.....
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-transform-runtime']
}
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
.....

在app.js裡追加我們自己寫的Javascript file — custom.js

# assets/js/app.js.....// Import local files
import './custom'
.....

寫一下custom.js來啟動這個textarea,讓他可以被ckeditor使用;這裡就是上一篇提到的設定檔,產生出來的編輯器功能,可以在這裡再經過刪減。注意!是刪減喔,新增除非重製版編輯器裡已經有了,否則就只能再重製一版喔!

# assets/js/custom.js// CKEditor5
ClassicEditor
.create( document.querySelector( '#post_content' ), {
toolbar: ['bold', 'fontSize', 'fontColor', '|', 'bulletedList', 'numberedList', 'indent', 'outdent', '|', 'undo', 'redo'],
language: {
ui: 'zh',
content: 'zh'
},
})
.catch( error => {
console.log( error );
} );

這裡額外提一下這段

document.querySelector( '#post_content' )

為什麼這裡的ID是post_content,而不是content呢?

因為我們用了Phoenix.HTML.Tag,它會幫我們把Form的名字自動加在底線前面,就變成這種格式。如果不確定的人,建議你可以用檢視原始碼工具看一下那個欄位就知道ID是什麼了。

Git Branch — gen_curd_code

到這邊我們再來啟動一下server,確認一下目前改得如何了

$ mix phx.server
[info] Running CkeditorIntegrationDemoWeb.Endpoint with cowboy 2.7.0 at 0.0.0.0:4000 (http)
[info] Access CkeditorIntegrationDemoWeb.Endpoint at http://localhost:4000
.....

可以看到我們重製的CKEditor5被套進textarea裡了,而且功能也都正常,編輯器產生的效果,實際上是被轉換成HTML包起來,再存進DB。

Photo by Deniel Chiang

Display Content

那寫入沒問題,讀取該怎麼在Elixir / Phoenix上呈現啊?很簡單,就是使用Phoenix.HTML.raw/1

Marks the given content as raw.

This means any HTML code inside the given string won’t be escaped.

我們把剛剛的例子繼續改完看看吧!把#16改一下

# lib/ckeditor_integration_demo_web/templates/post/index.html.eex<td><%= post.content %></td>       <----- 原本的
<td><%= raw post.content %></td> <----- 把內容喂給raw/1
Photo by Deniel Chiang

太好了!顯示也沒問題囉!

Git Branch — display_content

Photo by chuttersnap on Unsplash

One more thing…

其實我自己的Real world problem是我使用了Tailwind CSS,要怎麼在使用Tailwind CSS的情況之下,要支援CKEditor5產生出來的語法?

最後我的解法是在app.css裡,先引用@tailwind base之後,再去指定CKEditor5語法跟Tailwind CSS語法的關聯。

# assets/css/app.css@tailwind base;

h1 {
@apply text-2xl;
}
h2 {
@apply text-xl;
}
h3 {
@apply text-lg;
}
ul {
list-style-type: disc;
padding-left: theme('padding.4');
}

ol {
list-style-type: decimal;
padding-left: theme('padding.4');
}

@tailwind components;
.....

類似上面的例子,把HTML code與Tailwind CSS綁在一起就行了!是不是很簡單,這也是Tailwind CSS的好處之一吧!

找時間再來寫一篇,Elixir / Phoenix 與Tailwind CSS的整合篇吧!

重頭收看

CKEditor 5 重製版 — 需求談起來

CKEditor 5 重製版 — 自己動手包起來

--

--

Deniel Chiang

Decades of yrs experience in software developing. Started from J2EE 1.4/EJB, currently doing Elixir developing for my own consulting workshop.