Vue.jsの初歩として、TODO管理アプリを作ろうと思い立った。TODO管理アプリはSPAで、データの保存はAPIを呼び出すようにする。将来的には、Webpackなどのツールも導入したい。

画面の一覧は以下のとおり。

  • タスク一覧(初期表示ページ)
  • タスク新規作成
  • タスク新規作成確認
  • タスク編集

タスクの新規作成画面からは確認画面を出すようにする。確認画面は不要な気もするけど、画面遷移を試してみたいので入れる予定とした。

初期状態のソースコード

vue-router 2を使ったサンプルのソースコードを修正していく。

<!DOCTYPE html>
<html>
<head>
  <title>Vue hello world.</title>
  <meta charset="utf-8" />
  <script src="https://unpkg.com/vue"></script>
  <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
  <!-- HTMLを書く -->
  <div id="app">
    <h1>Hello App!</h1>
    <p>
      <!-- ナビゲーションに router-link コンポーネントを使う -->
      <!-- リンク先を `to` プロパティに指定します -->
      <!-- デフォルトで <router-link> は `<a>` タグとしてレンダリングされます -->
      <router-link to="/foo">Go to Foo</router-link>
      <router-link to="/bar">Go to Bar</router-link>
    </p>
    <!-- ルートアウトレット -->
    <!-- ルートとマッチしたコンポーネントがここへレンダリングされます -->
    <router-view></router-view>
  </div>

  <!-- JavaScriptを書く -->
  <script type="text/javascript">
    const Foo = { template: '<div>foo</div>' }
    const Bar = { template: '<div>bar</div>' }
    
    const routes = [
      { path: '/foo', component: Foo },
      { path: '/bar', component: Bar }
    ]
    const router = new VueRouter({
      routes // routes: routes の短縮表記
    })
    const app = new Vue({
      router
    }).$mount('#app')
  </script>
</body>

タスク一覧を表示する

まずは初期表示画面に、TODOの一覧を表示させる。TODOはdataに持っている配列をそのまま表示することにする。

初期表示画面のコンポーネントの追加

元のソースコードに、”Todos”という名前でコンポーネントを追加した。

const Todos = {
  template: '#hello-world-template',
  props: ['todos'],
  data: function (){
    return { message : 'hello' }
  }
}

このコンポーネントが初期表示で呼び出される。ルーターにパスを追加。

const routes = [
  { path: '/', component: Todos },

コンポーネントでテンプレートを扱う

コンポーネントで表示するHTMLはテンプレートとして用意する。テンプレートは、「インラインテンプレート」と「text/x-template」がある。今回は「text/x-template」を使用した。
参考:text/x-template - Vue.js

<script type="text/x-template" id="hello-world-template">
  // HTMLを書く
</script>

注意したいのは、templateの中は1つのタグのみが有効になるという点だ。2つ以上のタグを書くと、初めの1つのタグしか表示されない。

子コンポーネントで親のデータを参照

子コンポーネントでpropsを用意することで、親のデータを受け取ることができる。子コンポーネントに”todos”というpropsを用意し、子コンポーネントに”v-bind:props名=親のデータ名”を設定してデータを渡す。

<router-view v-bind:todos="todos" ></router-view>

以下のページが参考になった。
Vue.js2.x系で親から子コンポーネントにデータを渡す方法
Vue.js のコンポーネントと親子間データの送受信

ここまでのソースコード

<!DOCTYPE html>
<html>
<head>
  <title>TODO Sample</title>
  <meta charset="utf-8" />
  <script src="https://unpkg.com/vue"></script>
  <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
  <!-- HTMLを書く -->
  <div id="app">
    <h1>Hello App!</h1>
    <p>
      <router-link to="/foo">Go to Foo</router-link>
      <router-link to="/bar">Go to Bar</router-link>
    </p>

    <router-view v-bind:todos="todos" ></router-view>
  </div>

  <!-- JavaScriptを書く -->
  <script type="text/x-template" id="hello-world-template">
    <!-- templateの中は1つの要素のみ -->
    <div>
      <p>{{ message }}</p>

      <p>TODO一覧</p>
      <template v-for="item in todos" >
        <span>{{ item }}<br/></span>
      </template>

      <p>TODO一覧その2</p>
      <span v-for="item in todos">{{ item }}<br/></span>
     </div>
  </script>
  <script type="text/javascript">
    const Todos = {
      template: '#hello-world-template',
      props: ['todos'],
      data: function (){
        return { message : 'hello' }
      }
    }
    const Foo = { template: '<div>foo</div>' }
    const Bar = { template: '<div>bar</div>' }
    
    const routes = [
      { path: '/', component: Todos },
      { path: '/foo', component: Foo },
      { path: '/bar', component: Bar }
    ]
    const router = new VueRouter({
      routes // routes: routes の短縮表記
    })
    const app = new Vue({
      router,
      message: "message",
      data: {
        todos: [
          "todo1",
          "todo2",
          "todo3"
        ]
      }
    }).$mount('#app')
  </script>
</body>