#3 Rails × Vue.jsで動的なページをSPAさせる


前回の続き

railsからjsonデータをVue.jsの方に受け渡すことができたが、画像のデータがうまいことVue.jsのほうに渡っていないきがするのですね。

app/javascript/app.vue
のほうに{{dirnk.image}}
とやると、

"name": "image", "record": { "id": 10, "name": "ddd", "price": 3000, "explain": "dddddd", "user_id": 7, "created_at": "2021-08-06T06:50:21.611Z", "updated_at": "2021-08-06T06:50:21.649Z", "region_id": 1, "body_id": 1, "acidity_id": 1, "processing_id": 1, "likes_count": null } }

このようなjsonデータがviewに表示される。

考えられる原因第1

users_controller.rb


class Api::UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
    @drinks = @user.drinks.order('created_at DESC').includes(image_attachment: :blob)
  end
end

drinksの方はあくまでユーザークラスのインスタンスなので、drink.image
とやってもうまくいかない説がある。

考えられる原因第2

.erbで<%= image_tag @drink.image%>

とやるように、
vueのほうにも画像を表示させるメソッドがあるかも

考えられる原因第3

画像データをvueの方に渡すときに、jsonの特別な書き方がある。

https://qiita.com/youichiro/items/2987cd9b4ab29691eb77
このきじを参考に色々みていきたい

考えられる原因第4

俺にはまだ早い説
だとしたら、画像はrailsのactive storageに処理を任せて、
テキスト情報だけは vueの方で書くしかない。。。

考えられる原因第1

にまずはアプローチ。そもそもrailsのほうでは、@drink.imageとやって上手く言ってるのか確かめていきます

views/users/show.html.erb


<% @drinks.each do |drink| %>
<%= image_tag drink.image%>
<% end %>

にこんな感じでやればうまくいきました。

drink.imageでやればうまくいくので、
jsonで上手く画像データをvueに受け渡せているなら、vueの方でもimage_tag的な画像を表示させるメソッドを使えばdrink.imageでも上手くいきそうですね。

ちなみにvueのほうには、

app/views/api/users/show.json.jbuilder

show.json.jbuilder
json.array! @drinks, :id ,:name , :price , :explain ,
                     :image , :region_id , :body_id , 
                     :acidity_id , :processing_id ,
                     :likes_count , :user_id

こんな感じでデータをvueに受け渡しています。

drink.nameとかではしっかりとvueのほうに表示させれるので、問題ないでしょう。

このような書き方でvueに画像データを受け渡せているか微妙なのですが、一旦受けわたせている前提で、
考えて、vueの方でrailsのimage_tag的なメソッドを探すってことで

考えられる原因第2

rails vue 画像データ jsonでしらべたらどうやら、

json.array! @drinks, :id ,:name , :price , :explain ,
:image , :region_id , :body_id ,
:acidity_id , :processing_id ,
:likes_count , :user_id
こういった形式では、画像データを送れないっぽいので、そもそも画像データをvueに渡せてない。
ってことでこの線は一旦消える。
受け渡せることができたら、またこの原因に戻ろう。

考えられる原因第3

ってことでrailsの画像データをvueの方に受け渡す処理をかいていこうとおもいます。

https://qiita.com/terufumi1122/items/bdc96617c818dac01b6d
https://qiita.com/ozin/items/5ec81a4b126b8ebf7a96
https://qiita.com/tabakazu/items/5489632c534c56db6a4d
https://qiita.com/youichiro/items/2987cd9b4ab29691eb77

色々ヒットしたので
これかの記事を参考にして頑張る

まずは、https://qiita.com/youichiro/items/2987cd9b4ab29691eb77
これから
微妙に自分がやりたい形式でなかった。jbuilderを使わずやっていたので、自分の理解では飲み込めなかった。

次は、https://qiita.com/terufumi1122/items/bdc96617c818dac01b6d
この方の記事。
まさしく自分がやりたいことがタイトルになっているので、なんかいけそう

その前に、https://qiita.com/tabakazu/items/5489632c534c56db6a4d
こっちを済ませとく必要がある。今さっきやろうとしてた記事にeyecatchメソッドがあって、それを先に定義していかなければならない。。。。

models/drink.rb

    def image=(image)
      if image.present?
        prefix = image[/(image|application)(\/.*)(?=\;)/]
        type = prefix.sub(/(image|application)(\/)/, '')
        data = Base64.decode64(image.sub(/data:#{prefix};base64,/, ''))
        filename = "#{Time.zone.now.strftime('%Y%m%d%H%M%S%L')}.#{type}"
        File.open("#{Rails.root}/tmp/#{filename}", 'wb') do |f|
          f.write(data)
        end
        image.detach if image.attached?
        image.attach(io: File.open("#{Rails.root}/tmp/#{filename}"), filename: filename)
        FileUtils.rm("#{Rails.root}/tmp/#{filename}")
      end
    end

うん、意味がわからんので、頑張って一つずつ解説します。

まず、image=(image)、このメソッドの定義の仕方。。。
どっかで見たことあるけど、image(image)との違いがよくわからんくなった。
https://donghai821.hatenadiary.org/entry/20080406/1207479842
def hoge=(fuga)は書き込みオンリーらしい。
このようなメソッドを「要素代入関数」と言うらしい。へぇーー。

https://wa3.i-3-i.info/diff397data.html
エンコードとデコードの違いはこれ。

なぜbase64をつかうの??
かつての電子メールを送るためのプロトコルSMTPでは、ASCIIといわれる7bitで表現される英数字しか送ることができませんでした
したがって、メールを使って画像や音声などのデータをやりとりしたいと思った時に、英数字しか対応していないSMTPでは、それらのデータを送受信することができませんでした

そこで、すべてのデータを英数字で表すMIME(Multipurpose Internet Mail Extensions)という規格が登場し、その中でbase64というデータの変換方法が定められました
これによって、受信側と送信側がMIMEに則ってエンコード・デコードをすることで、メールを通して画像や音声などの送受信が可能になりました

現在では、JSONなどで特殊文字を含まないように画像データをbase64でエンコードしたり、Webページの表示の際にリクエスト数を減らすためにbase64でエンコードした画像をhtmlにそのまま埋め込むなどの用途で用いられています

そこで、たしかに画像データを文字列に置き換えて送受信をしたいから、画像をエンコードする必要があるのかもね。

drink.rb

  # Base64形式でうけとったimageデータをエンコードし、
  # 一時的にtmp配下に画像ファイルを作成、作成した画像ファイルをアタッチ
  # その後画像ファイルを削除
    def image=(image)
      if image.present?
        # もし、imageがあったら?
        prefix = image[/(image|application)(\/.*)(?=\;)/]
        # ???
        type = prefix.sub(/(image|application)(\/)/, '')
        # この正規表現にマッチしたものを空文字にする
        data = Base64.decode64(image.sub(/data:#{prefix};base64,/, ''))
        # 画像のデータをこの文字列にマッチしたものは、空にして,
        # 残った文字列をデコード
        filename = "#{Time.zone.now.strftime('%Y%m%d%H%M%S%L')}.#{type}"
        # 画像のファイルネームを定義
        File.open("#{Rails.root}/tmp/#{filename}", 'wb') do |f|
          f.write(data)
          # ファイルにデータを書き込み
        end
        image.detach if image.attached?
        image.attach(io: File.open("#{Rails.root}/tmp/#{filename}"), filename: filename)
        FileUtils.rm("#{Rails.root}/tmp/#{filename}")
      end
    end

正直この程度しか分からんかった。
なんでこれらのコードを実行する必要があるのかがよくわからんかった。。
後でrailsの画像周りの知識をしっかり勉強しよう。
https://railsguides.jp/active_storage_overview.html
ホントはダメだけど、理解しないまま一旦実装してしまおう。
一旦全体の動き把握して、実装してから理解する。
まさに駆け出しって感じですね。。精進します。。。

https://qiita.com/terufumi1122/items/bdc96617c818dac01b6d
この記事には、

posts_controller.rb
  def show
    post = Post.find(params[:id]).as_json #JSON形式にしておく

    eyecatch = post.eyecatch #eyecatchは添付した画像ファイル

    if eyecatch.present?
      post['image'] = encode_base64(eyecatch) # 画像ファイルを1.で定義したメソッドでBase64エンコードし、renderするデータに追加する
    end

    render json: post
  end

と書いてある。。。

自分の場合は、


  def show
    @user = User.find(params[:id])
    @drinks = @user.drinks.order('created_at DESC').includes(image_attachment: :blob)

  end

``


何がやっかいかと言うと、
こっちは投稿(drink)を複数取得して、配列だから、
配列一つ一つに対して処理をしていかないといけない。。。
eachとか使えるけど、それでうまくいくのだろうか。。。


配列だと扱いが難しいから、まずは画像一枚一枚を非同期的に表示させていく。

つまり、  @drinks = @user.drinks.order('created_at DESC').includes(image_attachment: :blob)

これだと複数枚画像があるけど、

drinks#showだったら、一枚しか画像がないから、それをうまいこと表示させていきたい

# ってことで、drinks#showのSPA化を一旦目指す。
配列データのSPA化は一応できたけど、画像の扱いが今は難しかったから、
一旦単一データのSPA化をしてみたいと思う。
ってことでブランチを一旦削除、、、ピエン

画像が入ってる配列データは俺には早かったっていう
考えられる原因第4に当てはまってしまった!リベンジかますで!!!