[AWS Amplify] Todoアプリを Vue.js 3のComposition APIを使って書いてみる
Todo アプリを作ってみたいと思います。
github : https://github.com/opvelll/amplifyTodoVue

バックエンドはAWS Amplify を使います。
フロント部分では、Vue.js v3 の新機能、Composition Api を使い記述します。
Vue v3プロジェクト作成
まず、vue cli でVueプロジェクトを作ります。
久しぶりなので vue cli をアップデートします。
vue create でシンプルなプロジェクトを作り、移動して、Vue 3の機能を使うために vue-nextを使います。
npm update -g @vue/cli
vue create vuetodo3
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Linter
? Pick a linter / formatter config: Basic
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No
cd .\vueTodo3
vue add vue-next
Amplify の設定する
こちらも間が空いたので、amplify cliをアップデートします。
そして amplifyのプロジェクト設定をします。
npm update -g @aws-amplify/cli
amplify init
amplify init の質問に以下のように答えます
? Enter a name for the project vuetodo3
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using vue
? Source Directory Path: src
? Distribution Directory Path: dist
? Build Command: npm.cmd run-script build
? Start Command: npm.cmd run-script serve
Using default provider awscloudformation
? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use default
ほとんどそのままEnterKey押します、最後 aws cliで使う AWS profile を default から変えたので指定しました。
Amplify API を追加
プロジェクトに Amplify API を追加します。
amplify add api
最初の質問でGraphQL を選びます。
GraphQLスキーマを生成しています。(Todo)
? Please select from one of the below mentioned services: GraphQL
? Provide API name: vuetodo3
? Choose the default authorization type for the API API key
? Enter a description for the API key:
? After how many days from now the API key should expire (1-365): 365
? Do you want to configure advanced settings for the GraphQL API No, I am done.
? Do you have an annotated GraphQL schema? No
? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description)
? Do you want to edit the schema now? No
質問では、認証方法にAPI keyを選びます。期間を365日にしています。
スキーマを編集してTask型を作る
質問に答えたあと生成されたTodoスキーマを、エディタで編集します。
{project root}\amplify\backend\api\vuetodo3\schema.graphqlを開き以下のように編集します。
type Task @model {
id: ID!
name: String!
completed: Boolean!
createdAt: AWSDateTime
}
名前をTaskにして、タスクをこなしたかの項目(completed)と、生成された時間(createdAt)を足します。
BooleanやAWSDAteTimeなど型についての情報は、AWS AppSyncにドキュメントがあります。
スキーマを編集したら、amplify pushというコマンドで、AWSに環境を作ってもらいます。
amplify push
? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src\graphql\**\*.js
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 3
質問では、スキーマファイルを生成するかや、ネストの深さを指定します。だいたいyesでOK。
すると、DynamoDBとAppSyncの環境が作られています。
Vue で Todo画面を作る
バックエンド側は用意できたので、フロント側を制作します。
npmで必要ライブラリをインストール
npm で必要ライブラリをインストールします。
npm install aws-amplify @aws-amplify/ui-vue --save
プロジェクトを編集する
vue create したときに作られたプロジェクトを編集していきます。
~ 横道 : @aws-amplify/ui-vue をVue v3で使えるようにする。
aws-amplify/ui-vue が Vue v3 に対応してなかったので、無理やり使えるようにします。
\node_modules\@aws-amplify\ui-vue\dist\index.js のエラー箇所をコメントアウト
import { applyPolyfills, defineCustomElements, } from '@aws-amplify/ui-components/loader';
import Vue from 'vue';
// Tell Vue to ignore all components defined in the @aws-amplify/ui-components
// package. The regex assumes all components names are prefixed
// 'amplify-'
// Vue.config.ignoredElements = [/amplify-\w*/]; <- ここ
// Bind the custom elements to the window object
applyPolyfills().then(() => {
defineCustomElements(window);
});
//# sourceMappingURL=index.js.map
\src\main.js のインポート部分を以下のようにします。
import { createApp } from "vue";
import App from "./App.vue";
import Amplify from "aws-amplify";
import "@aws-amplify/ui-vue";
import aws_exports from "./aws-exports";
Amplify.configure(aws_exports);
const app = createApp(App);
app.config.ignoredElements = [/amplify-\w*/];
app.mount("#app");
横道ここまで ~
App.vue ではTodoコンポーネントを呼び出します。
<template>
<div id="app">
<todo></todo>
</div>
</template>
<script>
import Todo from "./components/Todo.vue";
export default {
name: "App",
components: {
Todo,
},
};
</script>
Todo コンポーネントを書く
Composition API を使って書いたら以下のようになりました。
<template>
<div class="container">
<div>
<!-- todo list -->
<div>
<h3>Todo List</h3>
<div class="todo-container" v-for="item in taskList" :key="item.id">
<input
type="checkbox"
name="todo"
v-model="item.completed"
@click="updateTodo(item, $event)"
/>
<span class="todo-name" :class="{ strikethrough: item.completed }">{{
item.name
}}</span>
<button @click="deleteTodo(item.id)">消去</button>
</div>
</div>
<!-- create todo form -->
<form class="create_form" @submit.prevent="createTodo">
<div>新しいタスク</div>
<textarea
v-model="createForm.name"
name="todoName"
rows="4"
cols="40"
placeholder="タスク名"
></textarea>
<div class="createForm-footer">
<button type="submit">追加</button>
</div>
</form>
</div>
</div>
</template>
<script>
import { API } from "aws-amplify";
import { listTasks } from "../graphql/queries";
import { createTask, updateTask, deleteTask } from "../graphql/mutations";
import { reactive, ref, onMounted } from "vue";
export default {
setup() { // 1
const createForm = reactive({
name: "",
});
const taskList = ref([]); // 2
// 取得
async function getTodoList() {
const tasks = await API.graphql({
query: listTasks,
});
const list = tasks.data.listTasks.items;
// ソートして画面に反映
taskList.value = list.sort( // 2
(a, b) =>
new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
);
}
// 新しく作成
async function createTodo() {
const newTask = { name: createForm.name, completed: false };
await API.graphql({ query: createTask, variables: { input: newTask } }); // 3
createForm.name = "";
await getTodoList();
}
// Taskの更新 チェックする
async function updateTodo(item, event) {
const isCompleted = event.target.checked;
await API.graphql({
query: updateTask,
variables: { input: { id: item.id, completed: isCompleted } },
});
await getTodoList();
}
// Taskの削除
async function deleteTodo(id) {
await API.graphql({
query: deleteTask,
variables: { input: { id: id } },
});
await getTodoList();
}
onMounted(() => {
getTodoList();
});
return {
createForm,
taskList,
getTodoList,
updateTodo,
createTodo,
deleteTodo,
};
},
};
</script>
<style scoped>
.container {
display: flex;
flex-direction: column;
align-items: center;
}
.todo-container {
display: flex;
align-items: center;
}
.todo-name {
flex-grow: 1;
}
.strikethrough {
text-decoration: line-through;
}
.create_form {
margin-top: 2em;
}
.createForm-footer {
display: flex;
flex-direction: column;
align-items: stretch;
}
</style>
1 setup と return で定義したプロパティと関数を、外に出しています。
2. ref や reactive でプロパティを定義しています。これで値がリアクティブになり、変更によって画面に反映されるようになります。気をつけたいのは ref で定義した値は、.value と付けてvalue部分を変更する必要があります。
3. async になっているCURD関数は、 Amplify API を使っています。別ファイルに生成された文字列の GraphQLクエリ を読み込み、フォームの値などを 引数に設定してあげると クエリや、アップデートやデリートなどのミューテーション、さらにサブスクリプションができます。
プロジェクト全体は以下です。
ディスカッション
コメント一覧
まだ、コメントがありません