Learn プログ

-マナビメモ-

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の作成

最終的に完成したTodo App
https://github.com/atfeo/Riot_Observable_Rails

参考

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をチェック出来るようにする。

Riot Observable Rails 4 / 5に続く