Riot.js Observable Rails 3 / 5 「新たなTodoを追加出来るようにする」
はじめに
Riot.js Redux Railsシリーズで作ったTodoアプリのRedux部分をObservableを使って書いた。 (Observerパターンとか全然わかってない。ReduxをObservableで置き換える意味があるかもわからない。なんとなく似ていると思ったのでやってみた。)
Riot.js Redux Railsで作ったTodoアプリと全く同じものを作成するのでコードが大分重なっている(Redux部分をObservableにしたのと、それに伴う変更以外全く同じ)が、今回の一連の記事だけ読んでも出来るように省略はしない。
環境構築
Todo Appの作成
- Riot.js Observable Rails 2 / 5 「Todoリストを表示する」
- Riot.js Observable Rails 3 / 5 「新たなTodoを追加出来るようにする」
- Riot.js Observable Rails 4 / 5 「完了したTodoをチェック出来るようにする」
- Riot.js Observable Rails 5 / 5 「チェックしてあるタスクを削除出来るようにする」
最終的に完成したTodo App
https://github.com/atfeo/Riot_Observable_Rails
参考
- Riot公式のAPIの説明
- Riot公式のexample
- RiotJS and Redux - Part 5
- react-railsを使ってReactのTutorialをやってみる
- Riot公式のTodo App
Rails
新たなTodoを追加する際のサーバー側の処理を作成する。
Controller
tasks_controller.rbに
# ./app/controllers/api/tasks_controller.rb def create @task = Task.new(task_params) if @task.save render :show, status: :created else head :unprocessable_entity end end private def task_params params.permit(:name) end
を追加。
View(JSON)
保存に成功したときにJSON返すshow.json.jbuilderを作成。
# ./app/views/api/tasks/show.json.jbuilder json.extract! @task, :id, :name
これでサーバー側は完成。
Todoを追加するためのtask-formタグ
UIをRiot公式のTodoのように作成する。
+ フォームに入力があった時にボタンが有効になるようにする。
+ 何個目のTodoを登録しようとしているかわかるようにする。
// ./client/tags/task-form.tag <task-form> <form onsubmit={handleSubmit}> <input type="text" name="newTask" onkeyup={handleKeyup} placeholder="new task" > <button type="submit" disabled={!this.opts.istext} > Add Task # {this.opts.objects.length + 1} </button> </form> <script> handleSubmit() { if (!this.newTask.value) { return } this.opts.addtask(this.newTask.value) this.newTask.value = '' } handleKeyup() { this.opts.handlekeyup(this.newTask.value) } </script> </task-form>
サーバーに新しいタスクを送るメソッドなどをactions.jsに追加。
// ./client/src/actions.js module.exports = { loadTasks, // 追加 addTask, textExists, }; // 追加 function addTask(store, newTask) { toggleLoading(store, true); $.ajax({ url: '/api/tasks.json', type: 'POST', dataType: 'json', data: { name: newTask }, success: (res) => { newTaskAdded(store, res.id, res.name); toggleLoading(store, false); }, error: (xhr, status, err) => { toggleLoading(store, false); console.log('/api/tasks.json', status, err.toString()); }, }); } function newTaskAdded(store, id, name) { store.trigger('TASK_ADDED', { data: { id, name } }); } function textExists(store, value) { store.trigger('TEXT_EXISTS', { data: value }); }
タグをマウントするための読み込みと、イベントがトリガーされた時の処理をindex.jsに追加。
// ./client/src/index.js import './tags/task-form.tag'; store.on('TASK_ADDED', (action) => { store.setState({ tasks: store.getState().tasks.concat(action.data) }); }); store.on('TEXT_EXISTS', (action) => { store.setState({ isText: action.data }); });
todo-appタグにtask-formタグ、h3タグ、メソッドを追加
// ./client/src/tags/todo-app.tag <todo-app> <h3>Todo List</h3> <task-form addtask={this.handleNewTask} handlekeyup={handleInputForm} objects={this.state.tasks} istext={this.state.isText} > </task-form> <loading- ...> // scriptに追加 handleNewTask(task) { actions.addTask(store, task) } handleInputForm(value) { actions.textExists(store, value) }
cssを追加しUIをRiot公式のTodoに近づける。
/* ./app/assets/stylesheets/application.css */ body { font-family: 'myriad pro', sans-serif; font-size: 20px; border: 0; } todo-app { display: block; max-width: 600px; margin: 5% auto; } form input { font-size: 85%; padding: .4em; border: 1px solid #ccc; border-radius: 2px; } button { background-color: #1FADC5; border: 1px solid rgba(0,0,0,.2); font-size: 75%; color: #fff; padding: .4em 1.2em; border-radius: 2em; cursor: pointer; margin: 0 .23em; outline: none; } button[disabled] { background-color: #ddd; color: #aaa; } ul { padding: 0; } li { list-style-type: none; padding: .2em 0; }
ブラウザでlocalhost:5000を更新して確認。
フォームにタスクの名前を入力しボタンを押して登録できること、更新後も登録したTodoが維持されていることを確認。
次回は完了したTodoをチェック出来るようにする。