Vue2 × Vuetifyでテーブル表示を実装してみた（AWS DynamoDB、Lambda使用）

## はじめに
Vue2とVuetifyを使ってテーブル表示を実装してみました。
流れは以下の通りです。

1. DynamoDBとVueプロジェクトの作成
1. プロジェクトを作成するボタンとプロジェクトを表示するテーブルの配置
1. プロジェクトを作成するボタンを押してポップアップ画面を表示
1. ポップアップ画面のテキストフィールドとボタンの配置
1. 作成ボタンを押すとDynamoDBにデータを追加するLambda関数1の作成
1. 作成ボタンを押すとDynamoDBから情報を取得し一覧をテーブルに表示するprojects関数の作成


## 開発環境
- Windows 11
- Vue CLI 5.0.8
- Vue 2.7.14
- npm 6.14.14
- Node.js 14.17.5


## 実装
### DynamoDBとVueプロジェクトの作成
まず、今回の実装で使うDynamoDBとVueプロジェクトを作成します。

DynamoDBの設定は以下の通りです。
今回は、ProjectName（文字列）をパーティションキーとして使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/e6379d15-69ee-c7a5-8df9-dfc66cd9d5cf.png)

Vueアプリケーションの作成の流れは以下の通りです。

```:ターミナル
vue create フォルダ名（任意）
```

作成できたら、以下のコマンドを実行して、Vuetifyを追加します。
```:ターミナル
vue add vuetify
```
途中でプリセットの選択を聞かれるので、Default(recommended)を選択します。

今回は作成したプロジェクト内のHelloWorld.vueを編集する形で進めていきます。

### プロジェクトを作成するボタンとプロジェクトを表示するテーブルの配置
続いて、プロジェクトを作成するボタンとプロジェクトを表示するテーブルを配置していきます。
見た目は以下の通りです。
![screencapture-localhost-8080-2023-03-13-13_56_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/73255202-e9af-fb92-f987-4cde2519981b.png)

これにあたり、HelloWorld.vueを書き換えます。
v-btnタグでボタンを配置することができます。
v-data-tableタグでテーブルを配置することができます。
scriptタグ内のdataでテーブルのヘッダーと中身のプロパティを定義します。
今回はDynamoDBのパーティションキーがProjectNameなので、headersのtextとvalueをProjectNameにします。

```HelloWorld.vue
<template>
  <div>
    <v-btn
        outlined
        class="ma-1"
        color="primary"
    >
      プロジェクトの作成
    </v-btn>
    <v-data-table :headers="headers" :items="items"></v-data-table>
  </div>
</template>

<script>

export default {
  data() {
    return {
      headers: [
        { text: 'ProjectName', value: 'ProjectName' },
      ],
      items: []
    };
  },
};
</script>
```


### プロジェクトを作成するボタンを押してポップアップ画面を表示
続いて、プロジェクトを作成ボタンを押すと、ポップアップ画面が表示されるようにしていきます。
見た目は以下の通りです。
![screencapture-localhost-8080-2023-03-13-14_01_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/87294a1e-bc3e-1bbb-6322-f870665b7444.png)

HelloWorld.vueのプロジェクト作成ボタンの下にポップアップ画面を追加します。
ポップアップ画面はv-dialogタグで実装できます。
ポップアップ画面の表示・非表示を切り替えるために、scriptタグ内のdataにdialogを定義し、初期状態をfalseにします。
プロジェクト作成ボタンのv-btnタグの中に「@click="dialog=true"」を追加することでdialogがtrueになり、ポップアップ画面が表示されるという仕組みです。

```HelloWorld.vue
<template>
  <div>
    <v-btn
        outlined
        class="ma-1"
        color="primary"
        @click="dialog=true"
    ><!--@click="dialog=true"を追加-->
      プロジェクトの作成
    </v-btn>

　　<!--追加ここから-->
    <v-dialog 
      v-model="dialog" 
      width="400px"
    >
      <v-card
        width="400px"
        class="mx-auto"
        :loading=loading
      >
      <v-card-title>プロジェクト名を入力してください。</v-card-title>
      </v-card>
    </v-dialog>
　　<!--追加ここまで-->

    <v-data-table :headers="headers" :items="items"></v-data-table>
  </div>
</template>

<script>

export default {
  data() {
    return {
      dialog:false, //追加
      headers: [
        { text: 'ProjectName', value: 'ProjectName' },
      ],
      items: []
    };
  },
};
</script>
```


### ポップアップ画面のテキストフィールドとボタンの配置
続いて、ポップアップ画面のテキストフィールドとボタンを配置していきます。

以下のような見た目になりますが、現段階では、入力して作成ボタンを押してもテーブルには表示されません。
![screencapture-localhost-8080-2023-03-13-14_03_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/0dbcc696-c72e-58d3-0e36-3489c9fff7e0.png)

HelloWorld.vueのv-dialogタグの中に、テキストフィールドとキャンセルボタン、作成ボタンを追加します。
v-text-fieldでテキストフィールドを追加できます。
テキストフィールドに入力した値を入れるために、scriptタグ内のdataにinputTextを定義し、v-text-fieldにバインドします。

```HelloWorld.vue
<template>
  <div>
    <v-btn
        outlined
        class="ma-1"
        color="primary"
        @click="dialog=true"
    >
      プロジェクトの作成
    </v-btn>
    <v-dialog 
      v-model="dialog" 
      width="400px"
    >
      <v-card
        width="400px"
        class="mx-auto"
        :loading=loading
      >
      <v-card-title>プロジェクト名を入力してください。</v-card-title>

        <!--追加ここから-->
        <v-card
         width="320px"
         class="mx-auto"
        >
          <v-text-field 
            v-model="inputText"
            outlined
            hide-details="true"
          ></v-text-field>
        </v-card>
      <v-card-actions>
        <v-spacer></v-spacer>
        <v-btn class="ma-2"   @click="dialog=false"
          >キャンセル
        </v-btn>
        <v-btn color="primary"
        >作成
        </v-btn>
        <v-spacer></v-spacer>
      </v-card-actions>
      <!--追加ここまで-->

      </v-card>
    </v-dialog>
    <v-data-table :headers="headers" :items="items"></v-data-table>
  </div>
</template>

<script>

export default {
  data() {
    return {
      dialog:false,
      inputText: '', //追加
      headers: [
        { text: 'ProjectName', value: 'ProjectName' },
      ],
      items: []
    };
  },
};
</script>
```


### 作成ボタンを押すとDynamoDBにデータを追加するLambda関数「create-project」の作成
続いて、作成ボタンを押すと、DynamoDBにデータを追加するcreate-project関数を作成します。
基本的な情報は以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/145d2473-65cf-bd6c-6f68-231845481a68.png)

関数の作成ボタンを押したら、追加の設定をしていきます。
基本設定でタイムアウトを30秒に変更してください。
![スクリーンショット 2023-03-11 13.03.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/05c29fb5-635d-e578-fd01-80cb821140db.png)

既存のロールにアクセスし、AmazonDynamoDBFullAccessにチェックを入れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/4141a13c-4c06-ad6f-913f-9df393c8afcc.png)

今回はLambda関数単体のURLを用いるので、関数URLを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/84cea139-ddeb-83e3-90bf-c6fa2c9c1308.png)

関数URLの設定は以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/99b7681d-5bde-c832-2c72-d47f3eb93cec.png)

create-project関数のlambda_function.pyを以下のようにします。
先ほどHelloWorld.vueで定義したinput_textが送られてくるので、その中身をDynamoDBのProjectNameに追加するという処理です。
```lambda_function.py
import boto3
import json

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('t-tsuchiyama-test')

def lambda_handler(event, context):
    input_text = json.loads(event['body'])['input_text']
    table.put_item(Item={
        'ProjectName': input_text,
    })
    return {
        'statusCode': 200,
        'body': json.dumps('OK'),
    }
```

HelloWorld.vueではcreate-project関数にinput_textの中身を送る関数postDataを追加します。
POSTされたら、dialogをfalseにして、ポップアップ画面を閉じます。

```HelloWorld.vue
<template>
  <div>
    <v-btn
        outlined
        class="ma-1"
        color="primary"
        @click="dialog=true"
    >
      プロジェクトの作成
    </v-btn>
    <v-dialog 
      v-model="dialog" 
      width="400px"
    >
      <v-card
        width="400px"
        class="mx-auto"
      >
      <v-card-title>プロジェクト名を入力してください。</v-card-title>
        <v-card
         width="320px"
         class="mx-auto"
        >
          <v-text-field 
            v-model="inputText"
            outlined
            hide-details="true"
          ></v-text-field>
        </v-card>
      <v-card-actions>
        <v-spacer></v-spacer>
        <v-btn class="ma-2"   @click="dialog=false"
          >キャンセル
        </v-btn>
        <v-btn color="primary" @click="postData"
          >作成
        </v-btn>
        <v-spacer></v-spacer>
      </v-card-actions>
      </v-card>
    </v-dialog>
    <v-data-table :headers="headers" :items="items"></v-data-table>
  </div>
</template>

<script>
import axios from 'axios';　//追加

export default {
  data() {
    return {
      dialog:false,
      inputText: '',
      headers: [
        { text: 'ProjectName', value: 'ProjectName' },
      ],
      items: []
    };
  },
　//methods追加
  methods: {
    postData() {
      axios.post('create-project関数のURL', {input_text: this.inputText})
        .then(response => {
          console.log(response);
          this.dialog = false;
        })
        .catch(error => {
          console.error(error);
        });
    },
};
</script>
```
ここまで終えると、以下のようになります。

「a」というプロジェクト名を入力して作成ボタンを押します。
![screencapture-localhost-8080-2023-03-13-14_09_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/5ac1730a-bcef-d706-02d4-6e5c56f30d8d.png)


DynamoDBに追加されていることが確認できます。
![スクリーンショット 2023-03-13 11.03.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/533cd674-7e1e-31e4-6b40-8616b2f770f6.png)


### 作成ボタンを押すとDynamoDBから情報を取得し一覧をテーブルに表示するprojects関数の作成
続いて、作成ボタンを押すとDynamoDBから情報を取得し一覧をテーブル表示するprojects関数を作成します。
先ほどのcreate-project関数を作成した手順と同様に、projects関数を作成し、関数URLの設定まで行ってください。

関数URLまでの設定が終わったら、projects関数のlambda_function.pyを以下のようにします。
DynamoDBのテーブルをスキャンし中身を取得する処理です。

```lambda_function.py
import boto3
import json

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('t-tsuchiyama-test')

def lambda_handler(event, context):
    response = table.scan()
    items = response['Items']
    return {
        'statusCode': 200,
        'body': json.dumps(items)
    }
```


HelloWorld.vueでは、作成ボタンを押してDynamoDBにProjectNameが追加された後に、DynamoDBの情報を取得してテーブルに表示させる関数getItemsを追加します。
また、createdの中身にもgetItemsを記述することで、作成ボタンを押した後だけでなく、ページをリロードしたタイミングでもテーブルが表示されます。
createdはページを表示するときとリロードするときに実行されます。

```HelloWorld.vue
<template>
  <div>
    <v-btn
        outlined
        class="ma-1"
        color="primary"
        @click="dialog=true"
    >
      プロジェクトの作成
    </v-btn>
    <v-dialog 
      v-model="dialog" 
      width="400px"
    >
      <v-card
        width="400px"
        class="mx-auto"
      >
      <v-card-title>プロジェクト名を入力してください。</v-card-title>
        <v-card
         width="320px"
         class="mx-auto"
        >
          <v-text-field 
            v-model="inputText"
            outlined
            hide-details="true"
          ></v-text-field>
        </v-card>
      <v-card-actions>
        <v-spacer></v-spacer>
        <v-btn class="ma-2"   @click="dialog=false"
          >キャンセル
        </v-btn>
        <v-btn color="primary" @click="postData"
          >作成
        </v-btn>
        <v-spacer></v-spacer>
      </v-card-actions>
      </v-card>
    </v-dialog>
    <v-data-table :headers="headers" :items="items"></v-data-table>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      dialog:false,
      inputText: '',
      headers: [
        { text: 'ProjectName', value: 'ProjectName' },
      ],
      items: []
    };
  },
  //created追加
  created() {
    this.getItems();
  },
  methods: {
    postData() {
      this.loading = true;
      axios.post('create-project関数のURL', {input_text: this.inputText})
        .then(response => {
          console.log(response);
          this.dialog = false;
          this.getItems(); //追加
        })
        .catch(error => {
          console.error(error);
        });
    },
    //getItems追加
    async getItems() {
      try {
        const response = await axios.get('projects関数のURL')
        this.items = response.data
      } catch (error) {
        console.log(error)
      }
    }
  },
};
</script>
```

ここまで終え、新たに「b」、「c」というプロジェクトを作成すると以下のようになります。
![screencapture-localhost-8080-2023-03-13-14_13_51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/b67acfe9-3291-c720-c04f-83b15eaf7bb0.png)


これでVue2とVuetifyを使ってテーブル表示を実装することができました。

## おわりに
今回はVue2とVuetifyを使ってテーブル表示を実装してみました。
入力した値をテーブルに追加するというケースはよくあると思うので、ぜひ試してみてください。