firebase+Vue.tsで筋トレタスクアプリを作ってみた

#はじめに
firebaseとVue.tsを使って筋トレタスクアプリを作ってみました。
firebaseとVue.tsとVuetifyの情報が少なく感じたのでまとめておきます
ちなみに作成したものがこちらです。
[筋トレタスク](https://training-b1728.web.app/)
チュートリアルを含めてこのレベルなら私は２週間で出来ましたが、フレームワークの知識がある方ならもっと早く作れると思います。
Vueは良いぞ
##firebase plugin
これを作成しておくと楽になります。
firebase storeからのデータ取得はvuefireを使います。
公式apiは・・・

```ts
import Vue from 'vue';
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
import { firestorePlugin } from 'vuefire';

Vue.use(firestorePlugin);

// Your web app's Firebase configuration
const firebaseConfig = {
  // なんか色々
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);

// DB
export const db = firebase.firestore();
// Auth
export const auth = firebase.auth;
```
main.tsに読み込ませます

```ts
import Vue from 'vue';
import App from '@/App.vue';
import '@/plugins/firebase';
import vuetify from '@/plugins/vuetify';

Vue.config.productionTip = false;

new Vue({
  vuetify,
  render: (h) => h(App),
}).$mount('#app');
```
##ログインコード
```vue
<template>
  <v-container>
   <!--フォーム-->
    <v-form
    v-model="valid"
    >
        <v-text-field
        v-model="email"
        label="E-mail"
        :type="'email'"
        :rules="[
        v => !!v || '未入力です',
        v => /.+@.+\..+/.test(v) || 'メアド...?',
        ]"
        required
        ></v-text-field>
        <v-text-field
        v-model="password"
        label="Password"
        :type="'password'"
        :rules="[
        v => !!v || '未入力です',
        v => v.length > 5 || '6文字以上',
        ]"
        required
        ></v-text-field>

        <v-btn
        @click="signin"
        :disabled="!valid"
        color="blue"
        class="white--text"
        >
        ログイン
        </v-btn>
    </v-form>
  </v-container>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { auth } from '@/plugins/firebase';

@Component
export default class Signup extends Vue {

    // メアドの入力内容
    private email: string = '';

    // メアドの入力内容
    private password: string = '';

    // 入力項目がすべて入力されていればログインボタンを押せるようにする
    private valid: boolean = false;

    public signin() {

        auth().signInWithEmailAndPassword(this.email, this.password)
        .then((user) => {
            // ログイン情報保持
            auth().setPersistence(auth.Auth.Persistence.LOCAL);
        })
        .catch((error) => {
            // ログイン失敗
        });
    }
}
</script>
```
##アカウント新規作成
```vue
<template>
  <v-container>
    <!--フォーム-->
    <v-form
    v-model="valid"
    >
      <v-text-field
      v-model="email"
      label="E-mail"
      :type="'email'"
      :rules="[
      v => !!v || '未入力です',
      v => /.+@.+\..+/.test(v) || 'メアド...?',
      ]"
      required
      ></v-text-field>
      <v-text-field
      v-model="password"
      label="Password"
      :type="'password'"
      :rules="[
      v => !!v || '未入力です',
      v => v.length > 5 || '6文字以上',
      ]"
      required
      ></v-text-field>

      <v-btn
      @click="signup"
      :disabled="!valid"
      color="blue"
      class="white--text"
      >
      アカウント作成
      </v-btn>
    </v-form>
  </v-container>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { auth, db } from '@/plugins/firebase';

@Component
export default class Signup extends Vue {

  private email: string = '';

  private password: string = '';

  private valid: boolean = false;

  public signup() {
    auth().createUserWithEmailAndPassword(this.email, this.password)
    .then((user) => {
      // ログイン保持
      auth().setPersistence(auth.Auth.Persistence.LOCAL);
    })
    .catch((error) => {
      // アカウント作成失敗
    });
  }
}
</script>
```
Vuetifyにフォーム部分のルールを決める方法がjsだったのでtsだとどうすれば良いのか悩んでましたが、このような書き方でいいんですね。
##データの取得と更新と削除
```vue
<template>
    <v-container>
        <v-card
        class="mx-auto"
        >
            <v-list-item v-for="task in tasks">
                <v-list-item-action>
                    <v-checkbox
                    v-model="task.complete"
                    color="blue"
                    @click="updatecomplete(task.id, task.complete)"
                    ></v-checkbox>
                </v-list-item-action>
                <v-list-item-content>
                    <v-list-item-title>{{ task.menu }}</v-list-item-title>
                    <v-list-item-subtitle>{{task.count}}回</v-list-item-subtitle>
                </v-list-item-content>
                <v-spacer></v-spacer>
                <v-btn
                @click="deletetask(task.id)"
                icon
                >
                    <v-icon>mdi-delete</v-icon>
                </v-btn>
            </v-list-item>
        </v-card>
   </v-container>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { db, auth } from '../plugins/firebase';
@Component({
    data() {
        return {
            tasks: [],
        };
    },
    // データの取得
    firestore() {
        const user = auth().currentUser;
        // nullチェック
        if (user) {
            return {
                tasks: db.collection(user.uid).orderBy('createAd'),
            };
        } else {
            return {
                tasks: db.collection(''),
            };
        }
    },
})
export default class Tasklist extends Vue {

    private user: firebase.User | null = auth().currentUser;

    // 削除
    private deletetask(id: string) {
        if (this.user) {
            db.collection(this.user.uid).doc(id).delete();
        }
    }

    // 更新
    private updatecomplete(id: string, complete: boolean) {
        if (this.user) {
            db.collection(this.user.uid).doc(id).update({
                complete: !complete,
            });
        }
    }
}
</script>
```
データの取得方法もっといいやり方ないですかね・・・
##データの追加
```vue
<template>
  <v-container>
      <v-card>
        <v-form
        ref="form"
        v-model="valid"
        >
          <v-card-text>
            <v-select
              v-model="selectmenu"
              item-text=menu
              item-value=menu
              :items="traning"
              :rules="[v => !!v || '選択してください']"
              label="筋トレメニューの選択"
              required
            ></v-select>
            <v-select
              v-model="selectcount"
              :items="countitems"
              :rules="[v => !!v || '選択してください']"
              label="回数の選択"
              required
            ></v-select>
          </v-card-text>
          <v-card-actions>
            <div class="flex-grow-1"></div>
            <v-btn
            color="blue"
            class="white--text"
            @click="addTask"
            :disabled="!valid"
            >
            タスクの追加
            </v-btn>
          </v-card-actions>
        </v-form>
      </v-card>
  </v-container>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { db, auth } from '../plugins/firebase';

@Component({
    data() {
      return {
          traning: [],
      };
    },
    firestore() {
      return {
          traning: db.collection('traning'),
      };
    },
})
export default class Addtask extends Vue {

  private selectmenu: string = '';
  private selectcount: number = 0;

  private user: firebase.User | null = auth().currentUser;

  private valid: boolean = false;

  private countitems: number[] = [10, 20, 30, 50, 100];

  // データの追加
  public addTask() {
    const now = new Date();
    if (this.user) {
      db.collection(this.user.uid).add({
          menu: this.selectmenu,
          count: this.selectcount,
          complete: false,
          createAd: now,
      });
      // フォームのリセット
      (this.$refs.form as HTMLFormElement).reset();
      }
  }
}
</script>
```
##ログアウトと退会
```vue
<script lang="ts">
import { Component, Vue, Emit } from 'vue-property-decorator';
import { auth, db } from '@/plugins/firebase';

@Component
export default class Navigation extends Vue {

    private user: firebase.User | null = auth().currentUser;

    // ログアウト
    private signout() {
        auth().signOut();
    }

    // 退会
    private unsubscribeClick() {
        // タスクの削除
        if (this.user) {
            db.collection(this.user.uid).get().then((snapshot) => {
                snapshot.forEach((doc) => {
                    if (this.user) {
                        db.collection(this.user.uid).doc(doc.id).delete();
                    }
                });
                // アカウント削除
                if (this.user) {
                    this.user.delete().then(() => {
                      // 削除成功処理
                    });
                }
            });
        }
    }
}
</script>
```
コレクションの中身を全部消したい場合はひとつずつ取り出して削除するようにとのことです。全て消せば自動でコレクションも消えます。
nullチェックが多すぎる

firebaseのログイン認証機能だけを使って他は別のクラウドを使うとか、クラウドはfirebaseだけで済ませるとかほんとfirebaseは使ってて最高なのでもっと広まって欲しいですね。
また思い出したら更新するかもです