表題の通りです。
Laravel+React+SuperagentでAjaxなフォームを実装します。
AjaxライブラリとしてSuperagentを採用しているのは、jQueryから脱却したいのと、jQueryのAjaxよりも分かりやすかったからです。
プロミスとかいう難しい概念があるらしいですが、それは横に置いておいてもとりあえずは使えそうです。
Web標準の観点からするとFetchAPIがイケてるらしいのですが、各ブラウザベンダーの実装にばらつきがあるようなので避けました。
フロントエンドってつくづくカオスだなーとボヤキつつも話を進めていきたいと想います。
順番テキトーですが、ご了承ください。。。
Route::group(['prefix' => 'api/v1'], function () {
Route::get('/api/user', 'HogeController@index');
Route::post('/api/user', 'HogeController@update');
});
色々省略しちゃいます。
こんな感じでコンポーネント召喚しますよーという体だけです。
<div id="form-component" class="mdl-cell mdl-cell--12-col"></div>
実際はResouceControllerでAPIつくって、Restな感じに仕立てているのですが、詳しい作り方は省きます。
<?php
// NameSpace
class ConfigController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
// Jsonを返すAPIを用意
$users = \Auth::user();
return \Response::json($users);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(ConfigRequest $request)
{
// Update処理例
$user_name = \Auth::guard('users')->user()->name;
$users = User::where('name', $user_name)->first();
$users->fill(\Input::all())->save();
// 配列つくってJsonにポイー
$response['status'] = 'success';
$response["message"] = ['入力に問題ありません!'];
return \Response::json($response, '200');
}
}
<?php
namespace App\Http\Requests\User;
use App\Http\Requests\Request;
class HogeRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
if ($this->form_type == 'name') {
return [
'user_name' => 'max:5|required',
'email' => 'email|required'
];
}
// デフォルト(nullの時)
return [];
}
//
public function response(array $errors)
{
$response['status'] = 'error';
$response['message'] = $errors;
return \Response::json($response, 200);
}
}
FormReqeustでエラーをJsonで返す方法ですが、Illuminate/Foundation/Http/FormRequestのresponseメソッドをオーバーライドしてあげるだけです。
それで使い方はいつものFormRequestと同じです。
エラーがあればJsonResponseでエラーメッセージを返してくれます。
VerifyCsrfToken.phpで設定を忘れずに済ませておきます。
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
// ワイルドカード使えます。
'api/*'
];
}
Laravel側はこれで完了です。
// strictモードは雰囲気だけです。。。
"use strict";
var request = require('superagent');
var ConfigNameForm = React.createClass({
getInitialState: function () {
return {
// フォームの値
data: {
user_name: '',
email: '',
},
// メッセージ
message: {
// 入力エラーがない場合はコントローラーのレスポンスが、ある場合はフォームリクエストのレスポンスがそれぞれ代入されます。(もし単純にしたい場合はフォームリクエストのバリデーションをやめてコントローラーでバリデーションロジックを組んだ方がいいかもしれないです。)
user_name: '',
email: ''
}
}
},
// API-GET
componentDidMount: function () {
request
.get('/api/user')
.set('Content-Type', 'application/json')
.end(function(err, res){
if (err) {
alert('通信エラーです。リロードしてください。');
}
this.setState({
data: {
user_name: res.body.user_name,
email: res.body.email
}
});
}.bind(this));
},
handleChange: function (event) {
var data = this.state.data;
switch(event.target.name) {
case 'user_name':
data.user_name = event.target.value;
break;
case 'email':
data.email = event.target.value;
break;
}
this.setState({
data: data
});
},
// API-POST
handleSubmit: function () {
request
.post('/api/user')
.set('Content-Type', 'application/json')
.send({
user_name: this.state.data.user_name,
email: this.state.data.email
})
.end(function(err, res){
if (res.ok) {
var message = this.state.message;
switch (res.body.status) {
case 'success':
// ここは野暮ったいですが、適当に調整してください。
message.user_name = res.body.message;
message.email = res.body.message;
break;
case 'error':
message.user_name = res.body.message.user_name;
message.email = res.body.message.email;
break;
}
this.setState({
message: message;
});
} else {
alert('通信エラーです。もう一度お試しください。')
}
}.bind(this));
},
render: function () {
// 野暮ったい。。.
var msgOfName = false;
if (this.state.message.name.length > 0) {
var msgOfName = this.state.message.name.map(function (msg) {
return (
<p key={msg}>{msg}</p>
);
});
}
var msgOfEmail = false;
if (this.state.message.email.length > 0) {
var msgOfEmail = this.state.message.email.map(function (msg) {
return (
<p key={msg}>{msg}</p>
);
});
}
return (
<div>
{/* Message */}
{msgOfName}
{msgOfEmail}
{/* Form */}
<form action="javascript:void(0)" method="POST" onSubmit={this.handleSubmit}>
{/* Name */}
<label htmlFor="user_name">名前</label>
<input type="text" name="user_name" id="user_name" value={this.state.data.user_name} onChange={this.handleChange} disabled />
{/* Email */}
<label htmlFor="email">メールアドレス </label>
<input type="text" name="email" id="email" value={this.state.data.email} onChange={this.handleChange} />
<button type="submit">更新</button>
</form>
</div>
);
}
});
ReactDOM.render(
<FormApp />,
document.getElementById('form-compoent')
);
結構雑につくったので手直しすべきところは多そうです。
アーキテクチャも大事ですが、モダンなJavaScriptの書き方はもっと勉強して柔軟にかけるようにすべきだと思いました。
関連書籍