QR忘れ物アプリ (4) ユーザー設定確認・変更ページ [Vue,Vuetify,Firebase]

# はじめに
構成
1. 環境構築: https://qiita.com/rayan/items/5d04ee2ca7860c220dec
2. ログインページ: https://qiita.com/rayan/items/1f0576395b9727635461
3. メニュー: https://qiita.com/rayan/items/3645506cdb10512fd87c
4. ユーザー設定確認・変更ページ: __今回__

GitHubリポジトリ
> 準備中

# 省略表現
RTDB: Realtime Database

# 操作するファイル
- src
    - router
        - __index.js__
    - mixins
        - __DatabaseOps.js__
    - views
        - __UserPreferences.vue__

## src/views/UserPreferences.vue
ユーザー設定確認・変更ページの要素。各設定事項をlistの要素として格納している。

- `@change="formSubmit"`でチェックボックス、選択要素が変更されたときに`formSubmit`を呼び出す。
    - `formSubmit`ではv-modelに設定されている値を`values`に格納し、RTDBのuserPreferencesの値を更新する。

```vuejs
<template>
  <div>
    <Header :title="title"/>
    <v-card class="overflow-hidden">
      <v-list-item-group
        v-model="selectedItem"
        color="primary"
      >
        <v-list-item
          v-for="(preference, i) in preferences"
          :key="i"
        >
          <v-checkbox
            v-if="preference.type == 'checkbox'"
            v-model="preference.value"
            :label="preference.text"
            @change="formSubmit"
          ></v-checkbox>
          <v-select
            v-if="preference.type == 'select'"
            v-model="preference.value"
            :items="preference.values"
            :label="preference.text"
            @change="formSubmit"
          ></v-select>
        </v-list-item>
      </v-list-item-group> 
    </v-card>
  </div>
</template>

<script>
import Header from '@/components/Header'
import DatabaseOps from '@/mixins/DatabaseOps'

export default {
  name: "UserPreferences",
  data: () => ({
    preferences: [
      { text: 'Notify when link is accessed', value: false, type: 'checkbox'},
      { text: 'Notification for items that are not lost', value: true, type: 'checkbox'},
      { text: 'Where to notify', values: ['email', 'app'], value: 'email', type: 'select'},
    ],
    title: 'User Preferences'
  }),
  components: {
    Header
  },
  methods: {
    formSubmit() {
      var values = {
        'notifyAtLinkOpen': this.preferences[0].value,
        'notifyNotLostItem': this.preferences[1].value,
        'whereToNotify': this.preferences[2].value
      };
      this.setUserPreferences(values);
    }
  },
  mixins: [DatabaseOps]
};
</script>
```

## mixins/DatabaseOps.js
後に用いるメソッドも今回追加する。

```javascript
export default {
    methods: {
        // User Credentials
        async getUserCredentials() {
            return firebase.database().ref('userCredentials/'+this.$userId).get().then((snapshot) => {
                return snapshot.val();
            }); // return promise
        },
        addUserCredentials(data) {
            firebase.database().ref('userCredentials/'+this.$userId).set(data);
        },
        setUserCredentials(data) {
            firebase.database().ref('userCredentials/'+this.$userId).set(data);
        },

        // User Preferences
        async getUserPreferences() {
            return firebase.database().ref('userPreferences/'+this.$userId).get().then((snapshot) => {
                return snapshot.val();
            }); // returns promise
        },
        initUserPreferences() {    
            var data = {
                'notifyAtLinkOpen': false,
                'notifyNotLostItem': true,
                'whereToNotify': 'email'
            };
            firebase.database().ref('userPreferences/'+this.$userId).set(data);
        },
        setUserPreferences(data) {
            firebase.database().ref('userPreferences/'+this.$userId).set(data);
        },

        // ItemIds
        async getItemIds(itemId) {
            return firebase.database().ref('itemIds/'+itemId).get().then((snapshot) => {
                return snapshot.val();
            }); // returns promise
        },
        setItemIds(itemId) {
            firebase.database().ref('itemIds/'+itemId).set(this.$userId);
        },

        // Items
        async getItems(itemId) {
            return firebase.database().ref('items/'+this.$userId+'/'+itemId).get().then((snapshot) => {
                return snapshot.val();
            }); // returns promise
        },
        async getAllItems() {
            return firebase.database().ref('items/'+this.$userId).orderByKey().once('value').then((snapshot) => {
                var data = [];
                snapshot.forEach((childSnapshot) => {
                    var key = childSnapshot.key; // timestamp
                    var childData = childSnapshot.val(); // data for item
                    childData.itemId = key;
                    data.push(childData); 
                });
                return data;
            }); 
        },
        setItems(itemId, data) {
            firebase.database().ref('items/'+this.$userId+'/'+itemId).set(data);
        },
        updateItems(itemId, data) {
            firebase.database().ref('items/'+this.$userId+'/'+itemId).update(data);
        },
        pushItems(data) {
            var itemId = firebase.database().ref('items/'+this.$userId).push(data).key;
            return itemId;
        },

        // Messages
        async getMessages(messageId) {
            return firebase.database().ref('messages/'+this.$userId+'/'+messageId).get().then((snapshot) => {
                return snapshot.val();
            }); // return promise
        },
        setMessages(messageId, data) {
            firebase.database().ref('messages/'+this.$userId+'/'+messageId).set(data);
        },
        pushMessages(data) {
            var messageId = firebase.database().ref('messages/'+this.$userId).push(data).key;
            return messageId;
        }
    }
}
```

## router/index.js
新しくつくったUserPreferencesページの情報を追加する。

```javascript
import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

const routes = [
  {
    path: "*",
    redirect: "/"
  },
  {
    path: "/mainMenu",
    name: "MainMenu",
    meta: { requiresAuth: true },
    component: () =>
      import("@/views/MainMenu.vue"),
  },
  {
    path: "/userPreferences",
    name: "UserPreferences",
    meta: { requiresAuth: true },
    component: () =>
      import("@/views/UserPreferences.vue"),
  },
  {
    path: "/",
    name: "Authentication",
    component: () =>
      import("@/views/Authentication.vue"),
  },
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes,
});

router.beforeEach((to, from, next) => {
  let requiresAuth = to.matched.some(record => record.meta.requiresAuth)
  let currentUser = firebase.auth().currentUser
  if (requiresAuth) { // if this page requires auth, redirect to auth page
    if (!currentUser) {
      next({
        path: '/',
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    }
  } else {
    next() // this is required
  }
})

export default router;
```

# Build
- ビルドし、動作をテストする。動作のテストにはFirebase Emulatorが必要であるため、起動する。

```bash
npm run build
firebase emulators:start
```

- `dist/index.html`をブラウザで閲覧する。ログイン後UserPreferencesページに移動する。
- 設定を変更する。
- RTDBデータベースを見てUserPreferencesが入力に応じて変化していることを確認する。


# 次回
アイテム登録ページ