Tunapon
@Tunapon

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Laravel,Vue,Vuetifyの開発での不明点(初心者)

解決したいこと

テーブルのレコードをクリックしても色がつかない。(選択自体はできている模様)
→こちらの問題は解決しました!@UMA9626さんありがとうございます!!


・ボタンを押しても入力フォームのダイアログが発生しない。(未解決)


初投稿、質問失礼いたします。
本腰を入れてプログラミングを開始してから1ヶ月程度の者です。

初心者用のテスト実施なのですが、人員管理システムのようなものを作成中です。
使用言語はLaravelとvue(vuetify)かと思います。

とても簡易な作りで、新規、更新、確認、削除の4ボタンの実装があり、
データベースの一覧を表示するテーブルには役職名、手当、有効無効のチェックボックスの3要素としております。

新規以外の項目はレコードを選択してからボタンを押す必要があるようにしたくて、
そのためにレコードを選択したら色が変わるようにしたいのですが、styleタグで実装したつもりですが、
有効に働いていないようです。

ブラウザ、ログファイルのエラー表記はなくなりましたが、思い描いている機能は実装できておりません。
お手数ですが、解決方法を教授いただけますと幸いです。

環境

visual studio code
Laravel
vue.js
vuetify 3.4.1
node v18.18.0

該当するソースコード

--Role.vue--

<template>
  <v-container align="center" justify="center">
    <v-card height="800" width="800" style="overflow-y: auto;">
      <v-toolbar class="toolbar-color" dark flat>
        <v-toolbar-title>ここは役職管理画面の予定</v-toolbar-title>
      </v-toolbar>

      <!-- RoleActions コンポーネントの追加 -->
      <RoleActions @dialog-open="openDialog"></RoleActions>

      <!-- RoleDialog コンポーネントの追加 -->
      <!-- <RoleDialog :value="showDialog.NEW" @input="val => showDialog.NEW = val" :dialog-type="dialogTypes.NEW"
        :form-data="formData"></RoleDialog> -->

      <RoleDialog :show-dialog="showDialog.NEW" @update:show-dialog="val => showDialog.NEW = val"
        :dialog-type="dialogTypes.NEW" :form-data="formData"></RoleDialog>

      <RoleTable @role-selected="onRoleSelected" :roles="roles"></RoleTable>

    </v-card>
  </v-container>
</template>

<script>
import RoleTable from '@/Components/RoleTable.vue';
import RoleActions from '@/Components/RoleActions.vue';
import RoleDialog from '@/Components/RoleDialog.vue';

export default {

  data() {
    return {
      roles: [],
      selectedRole: null,
      formData: {
      },
      dialogTypes: {
        NEW: '新規',
        UPDATE: '更新',
        CONFIRM: '確認',
        DELETE: '削除',
      },
      showDialog: {
        NEW: false,
        UPDATE: false,
        CONFIRM: false,
        DELETE: false,
      },
    };
  },

  mounted() {
    this.fetchRoles();
  },
  methods: {
    openDialog(dialogType) {
      console.log("Opening dialog:", dialogType); // これを追加
      if (dialogType in this.showDialog) {
        this.showDialog[dialogType] = true;
        this.dialogType = dialogType; // dialogType を設定
        // 必要に応じて formData を初期化または設定
      }
      console.log("show dial:", this.showDialog); // これを追加
    },

    onRoleSelected(role) {
      // RoleTable から受け取った役職情報を RoleDialog に渡す
      console.log("Role selected in parent:", role);
      this.selectedRole = role;
    },

    //選択したレコードを記憶するために作ったつもり
    selectRole(role) {
      this.selectedRole = role;
      this.formData.roleName = role.RoleName;
      this.formData.allowance = role.Allowance;
      this.formData.isEnabled = role.IsEnabled === 1;
    },

    fetchRoles() {
      axios.get('/api/role')
        .then(response => {
          this.roles = response.data;
        })
        .catch(error => {
          console.error(error);
        });
    },

    resetForm() {
      this.formData = {
        roleName: null,
        allowance: null,
        isEnabled: true,
      };
    },
  },

  components: {
    RoleTable,
    RoleActions,
    RoleDialog,
  }
};
</script>

<style>
.selected-row {
  background-color: #ccca;
  /* グレーの背景色を指定 */
}
</style>


--RoleTable.vue--

<template>

  <!-- チェックボックスの表示 -->
<v-checkbox label="有効のみ表示" style="margin-left: 50px;" v-model="showOnlyEnabled"></v-checkbox>

  <!-- テーブルの表示 -->
  <v-table>
    <thead>
      <tr>
        <th class="text-left">役職名</th>
        <th class="text-left">手当</th>
        <th class="text-left">有効</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="role in filteredRoles" :key="role.RoleSid" @click="selectRole(role)"
        :class="{ 'selected-row': role === selectedRole }">
        <td>{{ role.RoleName }}</td>
        <td>{{ role.Allowance }}</td>
        <td>
          <v-checkbox v-model="role.IsEnabled" :true-value="1" :false-value="0" label="" :disabled="true"></v-checkbox>
        </td>
      </tr>
    </tbody>
  </v-table>

</template>


<script>


export default {
  emits: ['roleSelected'],  //必要かわからないけど、とりあえず


  props: {
    roles: Array, // roles をプロップスとして受け取る
    // showOnlyEnabled: Boolean,
  },

  computed: {
    filteredRoles() {
      // "有効のみ表示" チェックボックスがオンの場合、IsEnabledが1の役職のみをフィルタリング
      return this.showOnlyEnabled ? this.roles.filter(role => role.IsEnabled === 1) : this.roles;
    },
  },

  data() {
    return {
      selectedRole: null,
      showOnlyEnabled: false, // チェックボックスの状態

    };
  },

  methods: {
    //選択したレコードを記憶するために作ったつもり
    selectRole(role) {
      // this.selectedRole = role;
          // 役職が選択されたときにカスタムイベントを発生させて親コンポーネントに役職情報を送信
    // this.$emit('role-selected', role);
    console.log("Selected role:", role);
    this.$emit('roleSelected', role); // roleSelected イベントを発火
      
    },
  }
};
</script>

<style>
.selected-row {
  background-color: #ccca;
  /* グレーの背景色を指定 */
}
</style>



--RoleActions.vue--

<template>
    <div class="role-actions">
      <v-btn color="primary" @click="openDialog('NEW')">新規</v-btn>
      <v-btn color="secondary" @click="openDialog('UPDATE')">更新</v-btn>
      <v-btn color="info" @click="openDialog('CONFIRM')">確認</v-btn>
      <v-btn color="error" @click="openDialog('DELETE')">削除</v-btn>
    </div>
  </template>
  
  <script>
  export default {
    methods: {
      openDialog(dialogType) {
        console.log("Dialog type:", dialogType); // これを追加
        this.$emit('dialog-open', dialogType);
      },
    },
  };
  </script>
  
  <style scoped>
  .role-actions {
    margin: 20px;
  }
  </style>


--RoleDialog.vue--

<template>
    <div class="role-actions">
      <v-btn color="primary" @click="openDialog('NEW')">新規</v-btn>
      <v-btn color="secondary" @click="openDialog('UPDATE')">更新</v-btn>
      <v-btn color="info" @click="openDialog('CONFIRM')">確認</v-btn>
      <v-btn color="error" @click="openDialog('DELETE')">削除</v-btn>
    </div>
  </template>
  
  <script>
  export default {
    methods: {
      openDialog(dialogType) {
        console.log("Dialog type:", dialogType); // これを追加
        this.$emit('dialog-open', dialogType);
      },
    },
  };
  </script>
  
  <style scoped>
  .role-actions {
    margin: 20px;
  }
  </style>
  

その他

・一つのvueファイルのみで構成していたところ、思い描いた機能の実装ができまして、これを実務で活かせるように機能ごとのコンポーネント化を図ったところ、壁にぶつかっている状況です。
下記がコンポーネントを分ける前のソースコードです。

--Role.vue(全部込み)--

<template>
  <v-container align="center" justify="center">
    <v-card height="800" width="800" style="overflow-y: auto;">
      <v-toolbar class="toolbar-color" dark flat>
        <v-toolbar-title>ここは役職管理画面の予定</v-toolbar-title>
      </v-toolbar>

      <v-row align="center" justify="center" style="margin-top: 1%">
        <!-- ボタンとダイアログを繰り返し生成 -->
        <v-col v-for="dialogType in Object.values(dialogType)" :key="dialogType" cols="auto" style="margin: 1%">
          <v-btn @click="openDialog(dialogType)" elevation="10" size="x-large">{{ dialogType }}</v-btn>
          <v-dialog v-model="showDialog[dialogType]" max-width="500" >
            <v-card :height="dialogType === dialogTypes.DELETE ? '150' : '400'" width="500">
              <v-card-text v-if="dialogType == dialogTypes.DELETE">選択した役職を削除しますか?</v-card-text>
              <v-card-text v-else>
                <v-form ref="form">
                  <v-text-field v-model="formData.roleName" label="役職名" :readonly="dialogType === dialogTypes.CONFIRM" :rules="[rules.required]"
                    ></v-text-field>
                  <v-text-field v-model="formData.allowance" label="手当" type="number"
                    :disabled="dialogType === dialogTypes.CONFIRM"
                    ></v-text-field>
                  <v-checkbox v-model="formData.isEnabled" label="有効" :disabled="dialogType === dialogTypes.CONFIRM"
                    ></v-checkbox>
                </v-form>
                
              </v-card-text>
              <v-card-actions class="justify-end">
                <v-btn color="primary" @click="saveForm(dialogType)" v-if="dialogType !== dialogTypes.CONFIRM && dialogType !== dialogTypes.DELETE">{{ submitLabel }}</v-btn>
                <v-btn color="primary" @click="deleteSelectedRole()" v-if="dialogType == dialogTypes.DELETE">はい</v-btn>
                <v-btn color="error" @click="cancelForm(dialogType)">キャンセル</v-btn>
              </v-card-actions>
            </v-card>
          </v-dialog>
        </v-col>
      </v-row>
      <v-checkbox label="有効のみ表示" style="margin-left: 50px;" v-model="showOnlyEnabled"></v-checkbox>

      <!-- テーブルの表示 -->
      <v-table>
        <thead>
          <tr>
            <th class="text-left">役職名</th>
            <th class="text-left">手当</th>
            <th class="text-left">有効</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="role in filteredRoles" :key="role.RoleSid" @click="selectRole(role)"
            :class="{ 'selected-row': role === selectedRole }">
            <td>{{ role.RoleName }}</td>
            <td>{{ role.Allowance }}</td>
            <td>
              <v-checkbox v-model="role.IsEnabled" :true-value="1" :false-value="0" label=""
                :disabled="true"></v-checkbox>
            </td>
          </tr>
        </tbody>
      </v-table>
    </v-card>
  </v-container>
</template>

<script>
import dialogType from '../constants'; // constants.js をインポート
import axios from 'axios';
import rules  from "../validation_rules";

export default {
  data() {
    return {
      rules: rules,
      roles: [], // ロールデータを保持するデータプロパティ
      dialogType: dialogType.dialogType,
      currentDialogType: null,
      selectedRole: null, // 選択中の役職を保持するデータプロパティ
      formData: {
        // roleName: '',
        // allowance: 0,
        // isEnabled: true,
      },
      dialogTypes: {
        NEW: '新規',
        UPDATE: '更新',
        CONFIRM: '確認',
        DELETE: '削除',
      },
      showDialog: {
        new: false,
        update: false,
        confirm: false,
        delete: false,
      },
      showOnlyEnabled: false, // チェックボックスの状態
    };
  },

  computed: {
    filteredRoles() {
      // "有効のみ表示" チェックボックスがオンの場合、IsEnabledが1の役職のみをフィルタリング
      return this.showOnlyEnabled ? this.roles.filter(role => role.IsEnabled === 1) : this.roles;
    },
  },

  mounted() {
    this.fetchRoles();
  },

  methods: {
    //選択したレコードを記憶するために作ったつもり
    selectRole(role) {
      this.selectedRole = role;
      this.formData.roleName = role.RoleName;
      this.formData.allowance = role.Allowance;
      this.formData.isEnabled = role.IsEnabled === 1;
    },

    fetchRoles() {
      axios.get('/api/role')
        .then(response => {
          this.roles = response.data;
        })
        .catch(error => {
          console.error(error);
        });
    },

    resetForm() {
      this.formData = {
        roleName: null,
        allowance: null,
        isEnabled: true,
      };
    },

    // ダイアログを開くメソッド
    // openDialog に選択された役職の確認を追加
    openDialog(dialogType) {
      if (dialogType !== this.dialogTypes.NEW && !this.selectedRole) {
        // 役職が選択されていない場合の処理
        alert("レコードを選択してください"); // または他のメッセージ表示方法を利用
        return;
      }

      this.currentDialogType = dialogType;
      this.resetForm(); // フォームデータをリセット

      if (dialogType === this.dialogTypes.NEW) {
        // 新規作成の場合の処理
        this.dialogTitle = '新規作成';
        this.submitLabel = '登録';
      } else if (dialogType === this.dialogTypes.UPDATE) {
        // 更新の場合の処理
        this.dialogTitle = '更新';
        this.submitLabel = '更新';
        // フォームデータを選択したデータで初期化
        this.formData.roleName = this.selectedRole.RoleName;
        this.formData.allowance = this.selectedRole.Allowance;
        this.formData.isEnabled = this.selectedRole.IsEnabled === 1;
      } else if (dialogType === this.dialogTypes.CONFIRM) {
        // 確認の場合の処理
        // フォームデータを選択したデータで初期化
        this.formData.roleName = this.selectedRole.RoleName;
        this.formData.allowance = this.selectedRole.Allowance;
        this.formData.isEnabled = this.selectedRole.IsEnabled === 1;
      }
      this.showDialog[dialogType] = true;
    },

     async saveForm(dialogType) {
      console.log(this.$refs.form[0])
      var result = await this.$refs.form[0].validate();
      console.log(result)
      if (!result.valid) {
        return;
      }

      if (!this.formData.roleName) {
        // 役職名が空の場合、アラートメッセージを表示
        alert("役職名は必須です");
        return;
      }else if (this.formData.allowance < 0 || !this.formData.allowance) {
        // 手当が0未満の場合、未入力の場合にアラートメッセージを表示
        alert("手当は0以上の入力が必須です");
        return;
      }

      if (this.currentDialogType === this.dialogTypes.NEW) {
        // 新しい役職を登録する処理
        // 数値に変換する
        this.formData.allowance = Number(this.formData.allowance);
        const newRole = {
          RoleName: this.formData.roleName,
          Allowance: this.formData.allowance,
          IsEnabled: this.formData.isEnabled ? 1 : 0,
        };

        axios.post('/api/role', newRole)
          .then(response => {
            console.log(response);
            this.fetchRoles();
            this.cancelForm(this.currentDialogType);
          })
          .catch(error => {
            console.error('エラーレスポンス', error.response); // エラーレスポンスをコンソールに表示
          });
          this.selectedRole = null; // 処理が終了したら選択を解除
      } else if (this.currentDialogType === this.dialogTypes.UPDATE) {
        const updatedRole = {
          RoleName: this.formData.roleName,
          Allowance: this.formData.allowance,
          IsEnabled: this.formData.isEnabled ? 1 : 0,
        };

        // 更新対象の役職IDを取得するロジックを記述してください
        const roleSid = this.selectedRole.RoleSid;

        axios.put(`/api/role/${roleSid}`, updatedRole)
          .then(response => {
            console.log(response);
            this.fetchRoles(); // 役職データを再取得
            this.cancelForm(this.currentDialogType); // ダイアログを閉じる
          })
          .catch(error => {
            console.error(error);
          });
          this.selectedRole = null; // 処理が終了したら選択を解除
      }
    },

    // 例: 選択された役職を削除するメソッド
    deleteSelectedRole() {
      if (this.selectedRole) {
        // 削除確認用ダイアログを非表示にする
        this.showDialog[this.dialogTypes.DELETE] = false;

        // 選択した役職を削除する処理を実行
        const roleSid = this.selectedRole.RoleSid;

        axios.delete(`/api/role/${roleSid}`)
          .then(response => {
            console.log(response);
            this.fetchRoles(); // 役職データを再取得
            this.cancelForm(this.dialogTypes.DELETE); // ダイアログを閉じる
          })
          .catch(error => {
            console.error(error);
            // 削除が失敗した場合のエラーハンドリングを追加
            // ユーザーにエラーメッセージを表示するなど、適切な処理を行ってください
          });
          this.selectedRole = null; // 処理が終了したら選択を解除
      }
    },

    cancelForm(dialogType) {
      this.showDialog[dialogType] = false;
      this.selectedRole = null; // 処理が終了したら選択を解除
    },

    // 各ダイアログを閉じる
    closeDialogs() {
      this.currentDialogType = null;
    },
  },
};
</script>

<style>
.selected-row {
  background-color: #ccca;
  /* グレーの背景色を指定 */
}
</style>

--constants.js--

/**
 * APIのベースURL
 */
const API_BASE_USER = '/api';

import { createVuetify } from 'vuetify'

export const vuetify = createVuetify({
  theme: {
    //
  },
})

export default {
  /**
   * メッセージ区分
   */
  messageType: {
    info: 1,
    confirm: 2,
    alert: 3,
  },

// ダイアログ区分
  dialogType: {
    NEW: '新規',
    UPDATE: '更新',
    CONFIRM: '確認',
    DELETE: '削除',
  },
};


最後に

長文失礼いたしました。解決できるよう精進いたします。

0

1Answer

多分ですが、レコードをクリックしたときにRoleTable.vueselectedRoleが更新されていないですね。

RoleTable.vueselectRole(role)内で// this.selectedRole = role;のコメントアウトを外すとうまく動作しませんか?

1Like

Comments

  1. @Tunapon

    Questioner

    @UMA9626さん
    ご教示いただいた内容で選択したレコードの色変更が有効になりました。
    私の誤操作でコメントアウトした可能性が高いです。。

    レベルが低くすみません。大変助かりました。ありがとうございます!!

  2. お役に立ててよかったです!
    解決しましたら質問をクローズしてもらえればと思います:bow:

Your answer might help someone💌