#始めに
画面の入力値とモデルのバインディングについて、実験的に実装してみます
Railsの便利系のGemは一切使わず、標準的なRubyのライブラリのみで実装しています
#実行環境
JRuby 9.1.0
Rails 4.2.9
#実装
##モデル
###exam_categori
:column_name=>"ID", :data_type=>"NUMBER"
:column_name=>"CATEGORY_CODE", :data_type=>"VARCHAR2"
:column_name=>"SAKUSEIYMD", :data_type=>"DATE"
###exam
:column_name=>"ID", :data_type=>"NUMBER"
:column_name=>"WORD", :data_type=>"VARCHAR2"
:column_name=>"SENTENCE", :data_type=>"CLOB"
:column_name=>"WLEVEL", :data_type=>"NUMBER"
:column_name=>"SAKUSEIYMD", :data_type=>"DATE"
:column_name=>"RENBAN", :data_type=>"NUMBER"
※IDにはブラウザ側で振られたランダムな数字を設定する
###大雑把なinputフィールドのIDのつけ方について
→inputタグのid属性の命名規則を「model名#入力行数.モデルのカラム名」のようにする
Ralis側ではPOSTされたリクエストパラメタを
exam [
{1=>[wlevel=value,word=value,sentence=value]},
[2=>{wlevel=value,word=value,sentence=value}] ,
[3=>{wlevel=value,word=value,sentence=value]} ,
]
のようなデータ構造に変換したい
##Rails側の実装
class ExamController< ApplicationController
protect_from_forgery :expect => ["create"]
def register
a = params
.to_a #--(1)
.group_by do |k,v| --(2)
str=k[/^.*#/]
str.present? ?str.tr("#",""):""
end
.to_a--(3)
.select{|k,v|k.present?}--(4)
.map do|k,v|--(5)
[k,v.group_by {|a,b|a.scan(/#(\d)/)[0]}]
end
puts a
render text:""
end
end
(1)リクエストパラメタ「params」を配列化する
(2)リクエストパラメタのキーで「モデル名#」にマッチングするものでグループ化する
(3)(2)の結果を配列化する
(4)(2)の結果でkeyがnilになるものを除外する
つまり、ここで、paramsに含まれていたコントローラやviewの情報が排除される
(5)(4)で変換した配列を["モデル名",{行数=>[画面の入力値の集まり]}]のデータ構造に変換する
#実行結果
##リクエストされたパラメタ値
Parameters:
{"exam_categori#1.category_code"=>"カテゴリ1",
"exam#1.word"=>"テスト1", "exam#1.sentence"=>"テスト1の説明",
"exam#1.wlevel"=>"1", "exam#2.word"=>"テスト2",
"exam#2.sentence"=>"テスト2の説明", "exam#2.wlevel"=>"2",
"exam#3.word"=>"テスト3", "exam#3.sentence"=>"テスト3の説明",
"exam#3.wlevel"=>"3", "id#1"=>"799454"}
##rails側で変換した結果
[
["exam_categori",
{["1"]=>[["exam_categori#1.category_code", "カテゴリ1"]]}],
["exam",
{["1"]=>[["exam#1.word", "テスト1"],
["exam#1.sentence", "テスト1の説明"],
["exam#1.wlevel", "1"]],
["2"]=>[["exam#2.word", "テスト2"],
["exam#2.sentence", "テスト2の説明"],
["exam#2.wlevel", "2"]],
["3"]=>[["exam#3.word", "テスト3"],
["exam#3.sentence", "テスト3の説明"],
["exam#3.wlevel", "3"]]}]
["id",
{["1"]=>[["id#1", "799454"]]}]
]
参考:画面側の実装
<!DOCTYPE html>
<script>
function doPost(){
var id = Math.floor(Math.random () * 1000000)
var param = _g()
.concat(
[`id#1=${id}`])
.join('&')
commute('http://localhost:3000/exam/register/',param)
}
function commute(url,param){
var req = new XMLHttpRequest();
req.open('POST',url , true);
req.setRequestHeader('content-type',
'application/x-www-form-urlencoded;charset=UTF-8');
console.log(param)
req.send(param);
req.onload = _r(req,
function(){_t('display',req.responseText );});
}
function _r(req,caller){
return function (event){
if (req.readyState === 4 && req.status === 200)
caller();
};
}
function _f(str){
return document.getElementById(str).value;
}
function _t(str,text){
return document.getElementById(str).innerText=text;
}
function _k(str){
return Object.keys(document.getElementsByTagName(str))
}
function _g(){
console.log(_k('input'))
return _k('input')
.filter(a=>isNaN(a))
.map(a=>`${a}=${_f(a)}`)
}
</script>
<body>
<div style="position:relative;left:7pt;padding:10pt;border: 1pt solid #2cbf88;width:600pt">
<div style="position:relative;left:7pt;padding:10pt;border: 1pt solid #2cbf88;width:400pt">
カテゴリ:
<div style="position:relative;left:5pt;padding:5pt">
<input type='text'id='exam_categori#1.category_code' size='50'>
</div>
</div>
<div style="position:relative;left:7pt;padding:5pt;"></div>
<div style="position:relative;left:7pt;padding:10pt;border: 1pt solid #2cbf88;width:500pt">
<div style="position:relative;left:5pt;padding:5pt">
レベル1:
<input type='text'id='exam#1.word' size='10'>
<input type='text'id='exam#1.sentence' size='50'>
<input type='hidden' id='exam#1.wlevel' value="1">
</div>
<div style="position:relative;left:5pt;padding:5pt">
レベル2:
<input type='text'id='exam#2.word' size='10'>
<input type='text'id='exam#2.sentence' size='50'>
<input type='hidden' id='exam#2.wlevel' value="2">
</div>
<div style="position:relative;left:5pt;padding:5pt">
レベル3:
<input type='text'id='exam#3.word' size='10'>
<input type='text'id='exam#3.sentence' size='50'>
<input type='hidden' id='exam#3.wlevel' value="3">
</div>
</div>
</div>
<input type='button' onclick='doPost();' value="登録">
<div id='display'></div>