概要
Rails6でQiitaの投稿画面のように、画像をドラッグ&ドロップ可能なMarkdownエディタを作成した際のメモです。
ちなみにRailsをGWに学んで、このエディタ機能を実装したblogを作成しました。
(Heroku,GCSで構築)
地元の静岡県東部のスポット等を紹介していますので興味があればどうぞ。
駿河部
環境
Mac OS 10.15.4
Ruby 2.6.3p62
Rails 6.0.2.2
使用する機能について
主に3つのOSS及び機能を使用して実装をしています。
- SimpleMDE
- Inline Attachment
- Active Storage
SimpleMDE
ブラウザ上のtextareaタグをMarkdownエディタを変えてくれる素晴らしいOSS。
自動保存機能など様々な機能も搭載されています。
Inline Attachment
ブラウザ上からドラッグ&ドロップでのAjaxでのファイル送信を簡単に実装してくれる。
SimpleMDEと組み合わせて、ドラッグ&ドロップされた画像を非同期で送信してアップロード結果をエディタに反映してくれます。
Active Storage
Railsに組み込まれているファイルアップロードの機能。
こいつを利用するとお手軽にファイルアップロード機能が実装可能で、AWS,Azure,GCPといったクラウドストレージへのアップロードにも対応している優れもの。
手順
PJの作成と各種インストール
# railsのプロジェクト作成
rails new markdown_drag_and_drop
# active storageのインストール
rails active_storage:install
rails db:migrate
# 必要なjsモジュールのインストール
yarn add simplemde
yarn add inline-attachment
turbolinksの無効化
Railsにはデフォルトで高速化のために、通常のWEBサイトをSPAのような部分的なDOM変更に無理やり置き変えるturbolinksが入っています。
しかしながら、jsでゴリゴリと実装をする上でよく悪さをするので今回は無効化しています。
// 無効化
// require("turbolinks").start()
<!-- 'data-turbolinks-track': 'reload'を削除 -->
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_pack_tag 'application' %>
モデル、コントローラ、ビューの作成
scaffoldで雛形を作成します。
contentがMarkdownを文字列として保存するフィールドとしています。
rails g scaffold article content:text
フォーム画面にMarkdownエディタにするtextareaを配置します。
<!-- 画面で使用するjs(後で記載) -->
<%= javascript_pack_tag 'article' %>
<%= form_with(model: article, local: true) do |form| %>
<!-- 略 -->
<div class="field">
<%= form.label :content %>
<%= form.text_area :content, { id: "editor"} %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
画面で読み込むjsを作成する。
- textareaのエディタ化
- エディタ内にドラッグ&ドロップされた際にajax通信する設定
を行います。
ajaxで通信する処理はこの後作成します。
import "inline-attachment/src/inline-attachment";
import "inline-attachment/src/codemirror-4.inline-attachment";
import 'simplemde/dist/simplemde.min.css'
import SimpleMDE from "simplemde";
import Rails from '@rails/ujs'
window.onload = function () {
// textareaをMarkdownエディタにする
const simplemde = new SimpleMDE({
element: document.getElementById("editor"),
});
// エディタに画像がドラッグ&ドロップされた際の処理
inlineAttachment.editors.codemirror4.attach(simplemde.codemirror, {
uploadUrl: "/articles/attach", // POSTする宛先Url
uploadFieldName: "image", // ファイルのフィールド名(paramsで取り出す時のkey)
allowedTypes: ['image/jpeg', 'image/png', 'image/jpg', 'image/gif'],
extraHeaders: { "X-CSRF-Token": Rails.csrfToken() }, // CSRF対策
});
};
ファイル送信用の処理の作成
Active Storageでファイル送信を行うためのモデルを作成します。
rails g model attachment
class Attachment < ApplicationRecord
has_one_attached :image
end
scaffoldで作成したコントローラにajax通信用の処理を追加する。
画像の保存及びURLをJSONで返します。
filenameがinlineAttachmentがデフォルトで受け取るパラメータなのでそれに合わせます。
class ArticlesController < ApplicationController
...
def attach
attachment = Attachment.create! image: params[:image]
render json: { filename: url_for(attachment.image) }
end
...
end
ルーティングに追加します。
post 'articles/attach', to: 'articles#attach'
動作確認
実行して、エディタにうまくドラッグ&ドロップして、画像リンク用のMarkdownが出力されれば成功です。
rails db:migrate
rails s
画像のリサイズ
アップロード画像をそのまま返すのではなく、
リサイズしたいものを使用したい場合には、variantで圧縮したものを返すことも可能です。
ActiveStorageは圧縮もやってくれるので楽ですね。
class Attachment < ApplicationRecord
has_one_attached :image
def image_compressed
if image.attached?
image.variant(resize_to_fit: [700, 600]).processed
end
end
end
まとめ及び所感
Rails6で画像をドラッグ&ドロップ可能なMarkdownエディタを作成する方法を紹介しました。
最近Railsを触りだしたのですが、色々と暗黙のルールを覚えないといけない事はありますが、様々な処理が簡単に実装できるため楽ですね。
参考