2018/11/19

RailsでMarkdownエディタとプレビューを実装する

Rails で Markdown エディタとプレビューを実装します。
また、画像のアップロード機能も加えて実装します。

marked.js で実装された demo page はこちら



手順


  • js 、スタイルシート読み込み
  • Markdown エディタとプレビュー実装
  • 画像アップロード機能実装



js 、スタイルシート読み込み


application.html.erb に以下を追記して js を読み込みます。

<script async src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script async src='https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.10/vue.js'></script>
<script async src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/highlight.min.js"></script>
<script async src="https://sdk.amazonaws.com/js/aws-sdk-2.355.0.min.js"></script>



次に、スタイルシートの読み込みですが、highlight.js の demo page でテーマが確認できます。

今回は、github のテーマを利用します。
application.html.erb に以下を追記してスタイルシートを読み込みます。

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/github.min.css">



Markdown エディタとプレビュー実装


前提として、適当な view, controller, model が作られていることとします。
( Markdownを保存するカラムは text型にします。ここでは、post という model の content というカラムに保存しています。 )
また、bootstrap を利用しています。

まず、view を以下のように編集します。( form_for などの部分は適宜読み替えてください)

<div class="container-fluid">
  <%= form_for @post do |f| %>
    <label for="upload-file">
      <input type="file" id="upload-file">
    </label>
    <div class="row container-fluid mx-auto" id='editor'>
      <textarea name="post[content]" id="markdown" class="textarea col-md-6 form-control" style="resize: none;" rows="20" v-model='input' debounce='50'></textarea>
      <div class="col-md-6 preview border p-0">
        <div v-html='input | marked' id="marked-preview"></div>
      </div>
    </div>
    <div class="text-right">
      <%= f.submit "投稿する", :class => "btn btn-info" %>
    </div>
  <% end %>
</div>



次に、先ほどの view に以下を追記して、textarea に markdown を入力するとプレビューに表示されるようにします。

<script type="text/javascript">
  $(window).on('load', function() {
    var renderer = new marked.Renderer()
    renderer.code = function(code, language) {
      return '<pre'+'><code class="hljs">' + hljs.highlightAuto(code).value + '</code></pre>';
    };

    marked.setOptions({
      renderer: renderer,
      langPrefix: '',
      breaks : true
    });
    new Vue({
      el: '#editor',
      data: {
        input: '<%== j @post.content %>',
      },
      filters: {
        marked: marked,
      },
    });
  });
</script>

turbolinks を使っている場合は、Markdown エディタの view に移動する前の link_to などで data-turbolinks="false" をしないとエディタとプレビューがうまく動作しないことがあります。


画像アップロード機能実装


最後に、画像のアップロード機能を実装するのですが、AWSを使うのでその準備をします。


Amazon Cognito で Identity Pool 作成

まず、Amazon Cognito にて、Identity Pool を作成します。
ID プール名は適当に入力して、"認証されていない ID に対してアクセスを有効にする"にチェックを入れ、プールの作成をクリックしてください。


次の画面では、そのまま許可を押します。

そうすると、Identity Pool Id が表示されるので、メモしておきます。


S3の設定

S3 に移動して、Bucket を作成します。

アクセス権限というタブを選択して、CORSの設定を選択します。
以下のように編集して保存します。

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Authorization</AllowedHeader>
        <AllowedHeader>Content-*</AllowedHeader>
        <AllowedHeader>Host</AllowedHeader>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>


IAM ロールの設定

IAM に移動して、ロールを選択します。
先ほど作成した Cognito のロールがあるので **UnauthRole という名前のロールを選択します。
ポリシーをアタッチ -> ポリシー作成の順にクリックし、画像のように入力してポリシーを作成します。(リソースのところは、先ほど作成した S3 の Bucket の ARN を指定します)

そうしたら、先ほどのロールの設定に戻って、作成したポリシーをアタッチします。
これで AWSの設定は完了です。

アップロード機能実装

次に、画像をアップロードすると自動的に 画像のURLを textarea に入力して、previewで表示されるようにします。
先ほどの、script タグの中に以下を追記します。

$(function() {
  $("#upload-file").on('change', function() {
    var file = $("#upload-file").prop("files")[0];
    var timestamp = new Date().getTime();
    var filename = file.name.split('.')[0] + timestamp + ".jpg";

    s3_client().putObject({Bucket: "Bucket の名前を入力", Key: filename, ContentType: file.type, Body: file, ACL: 'public-read-write'},
    function(err, data){
      if(data !== null){
        // 画像表示
        var textarea = $('#markdown');
        var sentence = textarea.val();
        // 画像のURLは、自分で決めた region, Bucket Name によって変わります。適宜書き換えてください
        var word = `<img src=https://s3-ap-northeast-1.amazonaws.com/"Bucket の名前を入力"/${filename} class="img-fluid" style="display: block;">`;

        sentence = sentence + '\n' + word;
        textarea.val(sentence);
        $("#marked-preview").html(marked(sentence))
      } else {
        alert("アップロード失敗.");
      }
    });
  });

  function s3_client() {
     // 自分の設定した region に書き換えてください。ここでは東京の region を指定しています
    AWS.config.region = "ap-northeast-1";
    // 先ほどメモした Identity Pool Id に書き換えてください
    AWS.config.credentials = new AWS.CognitoIdentityCredentials({IdentityPoolId: "自分の Identity Pool Id を入力" });
    AWS.config.credentials.get(function(err) {
        if (!err) {
            console.log("Cognito Identify Id: " + AWS.config.credentials.identityId);
        }
    });
    return new AWS.S3();
  };
});

これで、Markdown エディタとプレビューの実装は完了です。