Practical Web Coding

デザイナーからエンジニアへ転身して学んだWeb系テクニックのまとめ

AngularJSからRailsに$httpモジュールを使ってデータをPOSTする

概要

AngularJSでAjax通信をしようとすると、複雑なオブジェクトをうまくPOSTできなかったり、Railsでパラメーターをうまく受け取れなかったりする。試行錯誤して落ち着いたパターンのメモ。

Angular側

$(document).on 'turbolinks:load', ->
  myApp = angular.module('myApp', [])

  myApp.controller 'MainController', ['$scope', '$http', ($scope, $http) ->

    # パラメーターエンコード関数
    transform = (data) ->
      $.param({data})

    # サーバーにデータを保存
    $http({
      method : 'POST',
      url : "/load_data",
      transformRequest: transform,
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
        'Accept': 'application/vnd.api+json',
        'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
      },
      data: {
        myparam1: 123,
        myparam2: "string"
      }
    }).then (response) =>
      $scope.data = response.data
    , (response) =>
      alert '通信失敗'
  ]

  angular.bootstrap($("#app"), ["myApp"])

Rails

class HomeController < ApplicationController

  def index

  end

  def load_data

    logger.debug "params = #{params.inspect}"
    logger.debug "params[:data] = #{params[:data].inspect}"

    render json: [
      {
        name: '商品01',
        price: 1000,
      },
      {
        name: '商品02',
        price: 500,
      },
    ]
  end
end

load_dataアクションではlogger.debugを用いてパラメーターをログに出力している。該当する部分のログは以下のような感じ。

params = <ActionController::Parameters {"data"=>{"myparam1"=>"123", "myparam2"=>"string"}, "controller"=>"home", "action"=>"load_data"} permitted: false>
params[:data] = <ActionController::Parameters {"myparam1"=>"123", "myparam2"=>"string"} permitted: false>

params[:data]に実際に送信された内容が来ている。RailsのStrong Parameterを使って受理するパラメタを検証すれば普通のformタグと同様にコントローラーで処理できる。

日付を扱う

Angular(JavaScript)とRailsで日付を相互にやりとりするのは意外と難しい。

Angular → Rails

POSTするときに、Date型の変数に対してgetTime()を呼ぶ。

date = new Date();
// => Fri May 19 2017 09:14:54 GMT+0900 (JST)
postDate = date.getTime();
// => 1495152894311

Rails側では、これを次のようにTimeクラスに変換する。

# POSTされたデータを想定
time_param = "1495152894311"
# Timeクラスを初期化
time = Time.at(time_param.to_f / 1000.0)
# =>  2017-05-19 09:14:54 +0900

正しく受け渡すことができた。これはAngularと組み合わせるRailsプロジェクトでは必須になると思うので、ApplicationControllerにprivateメソッドを追加して全部のコントローラーで利用できるようにしておくと良いかもしれない。

Rails → Angular

RailsからAngularへ日付型の値を返すとき、また工夫が必要になる。

Rails

# 仮にrenderでjsonを返すケースとして考える
render json: { created_at: user.created_at.to_f * 1000 }

Angular(JavaScript)側

// こちらはシンプル
// returned_dateにRailsからの値が入っているとする
date = new Date(returned_date);

もっと便利なやり方があるかも。