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へ日付型の値を返すとき、また工夫が必要になる。
# 仮にrenderでjsonを返すケースとして考える render json: { created_at: user.created_at.to_f * 1000 }
Angular(JavaScript)側
// こちらはシンプル // returned_dateにRailsからの値が入っているとする date = new Date(returned_date);
もっと便利なやり方があるかも。