#1 WebAPIを自作してVeu.jsで表示させる

WebAPIウェブ エーピーアイVue.jsビュー ジェイエスについて

前回「応用2」で簡単なWebアプリケーションを作ったと思います。
PHPを使ってデータベースに接続し、取得されたデータを配列として変数に入れ、その配列変数をforeach文で回し、 echoを使ってHTMLとして表示させていました。
それに代わる方法として、WebAPIとVue.jsを使って表示することも可能です。

Vue.jsを使うことで、その後の操作が同じ言語のJavaScriptで済みますので、 よりページの移動や再読み込みが少ない、ユーザーの利便性を高めたWebサイト*1を作るのに適しています。 Vue.jsでこのJSONデータを取得します。
WebAPIの役割はデータベースに接続して必要なテーブルデータを取ってきて、 取れたデータをJSONジェイソン形式*2にするのが仕事です。
*1)ユーザーの使い勝手が良く、操作ストレスが少ないサイトを作ることを「UI/UX設計」「UI/UXデザイン」と呼ばれています。
*2)JSONの名称はJavaScript Object Notationジャバスクリプト オブジェクト ノーテーションの頭文字を取ってます。
データの集まり(Object)を扱うための表記法(Notation)となり、書き方のルールとして{"キー" : 値}となってるものをJSON形式と呼びます。

今回は動的なサイトをつくるというより、PHPで実装した内容をWebAPIとVue.jsを代わりに使ってみようという趣旨ですので、 本来のVue.jsの機能は十分に発揮されませんが、まずはWebAPIとVue.jsを体感してみましょう。

データの流れのイメージ

#2 PHPの代わりにVue.jsでHTML表示

Vue.jsのインストール

まず応用2で作った「名前アプリ」のindex.phpをコピーしてindex2.phpを作りましょう。
Veu.jsをCDNで読み込みます。

    <head>
    ・・・
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
    </head>
<head>タグの中にスクリプトタグを記述します。Veu.js使う準備はこれだけです。

いったんWebAPIを使わず、PHPからJavaScriptにデータをわたす

WebAPI(+Vue)のメリットは、ユーザーの操作に対して動的に表示内容を動かせることです。
ユーザーが欲しいデータをHTTP(S)通信を使って送受信します。(後ほど説明)
今回は全データを表示させるだけので、いったんWebAPIを使わずに、PHPで取得したデータをVue.jsに渡し画面に表示させます。

応用2で作ったアプリケーションでは、データの取得と表示を以下のように書きました。

<div id="drag-area">


<?php
$sql = '
  SELECT
    t1.*,
    genders.gender
  FROM
    sortable AS t1
  LEFT JOIN `genders` ON t1.gender_id = genders.id
';
$stmt = $dbh->query($sql);

/* データ表示部分 */
foreach ($stmt as $result){
  echo '  <div class="drag gender'.$result['gender_id'].'" data-num="'.$result['id'].'" style="left:'.$result['left_x'].'px; top:'.$result['top_y'].'px;">'.PHP_EOL;
  echo '    <p><span class="name">'.$result['id'].' '.$result['name'].' ('.$result['gender'].')</span></p>'.PHP_EOL;
  echo '  </div>'.PHP_EOL;
}
?>

</div>

これを以下のように追加・変更します。

<div id="drag-area">


<?php
$sql = '
  SELECT
    t1.*,
    genders.gender
  FROM
    sortable AS t1
  LEFT JOIN `genders` ON t1.gender_id = genders.id
';
$stmt = $dbh->query($sql)->fetchAll(PDO::FETCH_ASSOC);
$json_data = json_encode( $stmt, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
?>


<script>
let jsonData = JSON.parse('<?= $json_data; ?>');
</script>


<div
  v-for="item in jsonData"
  v-bind:key="item.id"
  class="drag"
  v-bind:class="'gender' + item.gender_id"
  v-bind:data-num="item.id"
  v-bind:style="'left:' + item.left_x + 'px; top:' + item.top_y + 'px;'"
>
  <p><span class="name">{{item.id}} {{item.name}} ({{item.gender}})</span></p>
</div>
</div>


<script>
new Vue({
    el  : '#drag-area',
    data: {jsonData}
  });
</script>
PHPはデータ取得のみの機能になり、データを表示するVue.jsの設定と、表示させるHTMLタグが登場しました。

解説

その① データベースからデータの取得

<div id="drag-area">
<?php
$sql = '
  SELECT
    t1.*,
    genders.gender
  FROM
    sortable AS t1
  LEFT JOIN `genders` ON t1.gender_id = genders.id
';
$stmt = $dbh->query($sql)->fetchAll(PDO::FETCH_ASSOC);

/* ↓取得したデータをJSON形式に変換 */
$json_data = json_encode( $stmt, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
?>
データ取得部分のSQL文は同じですが、データ取得後にJSON化させて$json_data変数に格納しています。
json_encode()関数を使って、連想配列をJSON化(エンコード)します。
○第一引数には、対象となる変数$stmtを指定します。
○第二引数には、文字をエスケープさせるオプションを4つ指定します。
 JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT
エスケープされたJSONデータの内容は以下のような文字列になります。print_r()関数で確認してみましょう。
{"id":9, "name":"\u5c71\u7530\u5b5d\u4e4b", "left_x":37, "top_y":273, "gender_id":1, "gender":"\u7537\u6027"},{...
データベースの内容が "id":9 のように {"キー名" : 値} の関係で取得されます。
日本語はエスケープされているので、見慣れない文字列になっているのが確認できます。
その② Vue.jsの出力部分

<div
  v-for="item in jsonData"
  v-bind:key="item.id"
  class="drag"
  v-bind:class="'gender' + item.gender_id"
  v-bind:data-num="item.id"
  v-bind:style="'left:' + item.left_x + 'px; top:' + item.top_y + 'px;'"
>
  <p><span class="name">{{item.id}} {{item.name}} ({{item.gender}})</span></p>
</div>
</div>
データ表示部分です。Vue.jsを使って表示(レンダリング)させています。
見慣れない属性タグや、Vue.jsテンプレート構文の{{ }}でデータを出力しているのが特徴的ですね。 属性タグは見やすいように改行していますが、1行で書いてもかまいません。
○属性タグ
v-forブイ フォー ディレクティブと呼ばれるもので、 "エイリアス名 for データ"の書き方で、 jsonData に入っている配列データの中からひとつづつのデータを指すために、itemという名前の”エイリアス”を作り、反復処理させるものです。
phpのforeachのようなものです。ディレクティブとは(命令する)という意味です。Vue.js独自の属性タグのことです。

v-bind ブイ バインドディレクティブと呼ばれており、 html属性タグで出力・更新するときに使います。 固定されたclassがある場合は、上記のように分けて記載しましょう。
また、文字のあとにデータをバインドさせる場合は 文字列を' 'で囲って+でつなぎます。

③属性タグの属性値はエイリアス.キーでJSONデータの値をバインドさせます。
上の例では、itemというエイリアスのなかにあるキー「gender_id」を指定してありその値が出力されます。v-bind:keyは必ず必要です。

○テンプレート
④HTML内にテキスト表示させる場合は、{{ }}(マスタッシュタグ)にいれて、{{エイリアス.キー}}で出力。 ユーザーの操作に合わせて内容が変更されるため、データバインディング(データを紐付ける)と呼ばれています。
その③ Vue.jsの設定部分

<script>
/* PHPのデータをJavaScriptへ渡す処理です。 */
let jsonData = JSON.parse('<?= $json_data; ?>');

new Vue({
    el  : '#drag-area',
    data: {jsonData}
});
</script>

JSON.parseジェイソン パース() メソッドを使うと、文字列(PHPでechoしたJSON形式の文字列)を、{ }で囲われているデータの集まりである、 JavaScriptオブジェクトに変換します。parseとは「解析する」という意味です。
jsonDataという名前の変数に入れておきます。

new Vue()のところはVueの設定部分です。

new Vue()でインスタンスを作成するときは、オプションの Vue()関数で Vue インスタンスを作成して新しいオブジェクトを作り、{ }内にオプションを付けていきます。
オプションdataプロパティにはPHP→jsで変換取得したjsonDataを入れます。データは連想配列なので{ }の中に入れます。

注)new Vue()でインスタンスを作成する場合は、elプロパティに設定している要素(ここでは #drag-area)が読み込まれたあと(下部)に記述しなければなりません。

※配列を展開してみた場合

<script>
new Vue({
  el  : '#drag-area',
  data: {
    jsonData:[
      {id:1, gender:"男性", gender_id:1, name:"下北太郎", left_x:10, top_y:15},
      {id:2, gender:"男性", gender_id:1, name:"下北次郎", left_x:20, top_y:12},
      {id:3, gender:"女性", gender_id:2, name:"下北三郎", left_x:43, top_y:82},
      {id:4, gender:"男性", gender_id:1, name:"下北四郎", left_x:56, top_y:47},
      {キー:値, キー:値,  ...    }
    ]
  }
});
</script>
仮にdata: {jsonData}の部分が展開された場合は、このような内容になります。

実際の表示

新規登録、移動時の座標登録のロジックはそのままですので、 見た目も機能も全く同じものになります。

#3 コンポーネントでHTML表示させる

Vue.jsにはcomponentコンポーネントという考え方があり、 簡単にいえばコンポーネントとよばれる部品を作り、HTMLのなかで出力する方法です。
表示させる内容をコンポーネント化して、いろんなページで使い回す事ができますので、便利ですね。

今回のサンプルプログラムには向いていませんが、複数の同じようなレイアウトの画面を作る際に適しています。 ここでは、コンポーネントのさわりをやってみましょう。
index2.phpをコピーしてindex3.phpを作り、以下のように、データを変更してみましょう。

  
  <div
    v-for="item in jsonData"
    v-bind:key="item.id"
    class="drag"
    v-bind:class="'gender' + item.gender_id"
    v-bind:data-num="item.id"
    v-bind:style="'left:' + item.left_x + 'px; top:' + item.top_y + 'px;'"
  >
    <p><span class="name">{{item.id}} {{item.name}} ({{item.gender}})</span></p>
  </div>

  
  <child-component
    v-for="item in jsonData"
    v-bind:key="item.id"
    v-bind:item="item"  
  ></child-component>
htmlではdivではなく、独自に作ったHTMLタグ(テンプレートタグ)を入れます。
一部のv-bind設定は記述していくと長くなるので、コンポーネント設定のtemplete:側に移動しています。
また、v-bind:itemと宣言し、 データを参照しているエイリアス名を入れ、vueインスタンスで設定したdataプロパティの内容をバインド(連動)させています。


<script>
/* コンポーネント設定 */
let ComponentData = {
  props: ['item'],
  template: `
    <div class="drag"
      v-bind:class="'gender' + item.gender_id"
      v-bind:data-num="item.id"
      v-bind:style="'left:' + item.left_x + 'px; top:' + item.top_y + 'px;'"
    >
      <p><span class="name">{{item.id}} {{item.name}} ({{item.gender}})</span></p>
    </div>
  `
}

/* Vueインスタンス設定 */
new Vue({
  el  : '#drag-area',
  data: {jsonData},
  /* コンポーネント登録 */
  components: {
    'child-component': ComponentData
  }
});
</script>

Vue.jsの方では、let 変数名 = {}でコンポーネントを作ります。
props: プロパティにプロパティを登録します。 template: テンプレートで記述したコンポーネントが、HTMLに挿入されます。

Vue()関数の中に、あらたにcomponents: コンポーネントプロパティを追加します。
components: {'コンポーネント名' : コンポーネント変数} とし、コンポーネント表示部分にコンポーネントデータを渡します。

コンポーネントを理解する

以下のような画面表示になるものを作ってみたとします。
表示   htmlコード
index3.phpの完成型

<!DOCTYPE html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>テスト</title>
  <script src="//cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>


<div id="app">
  <user-list
    v-for="list in obj"
    v-bind:key="list.id"
    v-bind:list="list"
  >
  </user-list>
</div>

<script>
/* 何かしらのJSONデータ */
let obj =
[
  {id:1, age:"26", name:"下北太郎"},
  {id:2, age:"32", name:"大橋次郎"}
];

/* コンポーネント設定 */
let Component = {
  props: ['list'],
  template: `
    <div>
      <span>{{list.name}} {{list.age}}歳</span>
    </div>
  `
}

/* vue インスタンス作成 */
new Vue({
  el:#app,
  data:{ Obj },
  components: {
    'user-list': Component
  }
})
</script>

</body>
</html>

解説

コーディングを図解するとこのようになります。
コンポーネントやデータを準備をしたのちに、vueインスタンスに登録します。
同じファイルに記述する、かんたんなコンポーネント設定方法。
webpack/babelといったビルドツールを使用しない方法です。
※WebAPIは今回は使用していません。説明のみです。

★別の書き方
コンポーネントの登録方法をグローバルにしたときの記述のしかた。
インスタンスにcomponentsを記述しない。特に理由がなければこちらではなく、ローカル設定でコンポーネントを作るほうが良いです。
powerd by