質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

CSS3

CSS(Cascading Style Sheet)の第3版です。CSS3と略されることが多いです。色やデザインを柔軟に変更することが可能になります。

Nuxt.js

Nuxt.jsは、ユニバーサルなSPAが開発可能なVue.jsベースのフレームワーク。UIの描画サポートに特化しており、SSRにおけるサーバーサイドとクライアントサイドのUIレンダリングなどさまざまな機能を持ちます。

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

1985閲覧

Nuxt, Vueで実現したい挙動のコードがかなり冗長になってしまい、アドバイスいただきたいです。

aa316316

総合スコア39

Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

CSS3

CSS(Cascading Style Sheet)の第3版です。CSS3と略されることが多いです。色やデザインを柔軟に変更することが可能になります。

Nuxt.js

Nuxt.jsは、ユニバーサルなSPAが開発可能なVue.jsベースのフレームワーク。UIの描画サポートに特化しており、SSRにおけるサーバーサイドとクライアントサイドのUIレンダリングなどさまざまな機能を持ちます。

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2019/02/19 06:58

編集2019/02/19 08:37

概要

Nuxtのプロジェクト内で以下画像のような画面を作成しています。
ひとまず動作するようにしようと思い、以下コードのように記述したのですが、繰り返しも多くかなりダメなコードになってしまっていると思います。
以下を実現させるために、よりよい書き方等あればと思い質問させていただきました。
どなたかアドバイスいただけますでしょうか。

不足点は以降発見次第追記させていただきます。
どうぞ、よろしくお願いいたします。

実現したいこと

前提条件

  • 各カード(li要素)、コンポーネントにはjsonからIDを取得し、そのIDに応じた要素を表示させる。
  • IDの数は増減する可能性がある。

期待している挙動

  • 各カードをクリックすると、対応したコンポーネント(モーダル)を表示。
  • コンポーネントのボタンをクリックすると、コンポーネントを閉じる(非表示)
  • コンポーネントのAgreeボタンをクリックすると対応するカードの状態をtrueに変更(それに伴うスタイルの付与)
  • 全てのカードの状態がtrueになると、画面下部のボタンをactivate

サンプル画面

以下実装中のサンプル画面です。

  • 各カードをクリックすると、それに対応する子コンポーネントがモーダルとして表示されます。
  • モーダル内のボタンをクリックすると、またそれに対応したカードの状態(data)をtrueに変更し、v-ifによる文章の変更及びstyleの付与を行っております。
  • 全てのカードの状態がtrueになるとボタンがアクティベートされクリック可能になります(この部分は現在実装している下記コードではid数の増減に対応できていません)

イメージ説明

該当のソースコード

各コード、style等不要と思われる部分は消しています。

親コンポーネント
以下コードで、特に繰り返しになってしまっている記述についてどうにか省略できないか考えております。
現在はjsonから取得するidを3つに設定しているため、少しすっきりして見えますが、idを10個など取得すると、かなり可読性の悪いコードになってしまっています。

追記
だんだん短くなってきました...!

vue

1<template> 2 <section class="container"> 3 4 <!-- componentを配置 / 子コンポーネントでボタンをクリック時にイベントハンドラを受けとり、イベントを発火 --> 5 <agree-document v-if="isShow.id01" @doAgree="closeModal"></agree-document> 6 <agree-document2 v-if="isShow.id02" @doAgree="closeModal"></agree-document2> 7 <agree-document3 v-if="isShow.id03" @doAgree="closeModal"></agree-document3> 8 9 <div class="wrapper"> 10 <ul> 11      <!-- jsonから取得したidの数だけv-forで繰り返し --> 12 <li v-for="id in ids" :key="id"> 13 14       <!-- カードの上に重ね、クリックするとコンポーネントを表示 --> 15 <div v-if="id == id" @click="openModal(id)" class="list__item-link"></div> 16 17       <!-- カードタイトル --> 18 <p v-if="id == 'id01'">カード1</p> 19 <p v-if="id == 'id02'">カード2</p> 20 <p v-if="id == 'id03'">カード3</p> 21 22 <div v-if="id == id"> 23 <p v-if="!isAgreed[id]">Not agree</p> 24 <p v-if="isAgreed[id]" class="isAgreed">Agreed</p> 25 </div> 26 27 </li> 28 </ul> 29 30 <!-- button --> 31 <button 32 :class="{ isActive: completed }" 33 > 34 <span>次へ</span> 35 </button> 36 </div> 37 </section> 38</template> 39 40<script> 41import { request } from "~/data/index"; // 別ファイルで定義したrequest関数を取得 42 43// コンポーネントの読み込み 44import AegreDocument from "~/components/AgreeDocument.vue"; 45import AegreDocument2 from "~/components/AgreeDocument2.vue"; 46import AegreDocument3 from "~/components/AgreeDocument3.vue"; 47 48export default { 49 components: { 50 AgreeDocument, 51 AgreeDocument2, 52 AgreeDocument3 53 }, 54 data() { 55 return { 56 ids: [], 57 isShow: { 58 id01: false, 59 id02: false, 60 id03: false 61 }, 62 isAgreed: { 63 id01: false, 64 id02: false, 65 id03: false 66 } 67 }; 68 }, 69 computed: { 70 // ここもハードで処理を書いているため、 71 completed() { 72 return ( 73 this.isAgreed.id01 && 74 this.isAgreed.id02 && 75 this.isAgreed.id03 76 ); 77 } 78 }, 79 mounted() { 80 // 別のjsファイルで作成した関数を使用し、jsonからresponseを取得 81 this.$nextTick(() => { 82 const params = {}; 83 request(this.$axios, this.$store, params, this.$router) 84 .then(response => { 85 this.ids = response.data.ids; 86 }) 87 }); 88 }, 89 methods: { 90 openModal(id) { 91 Object.keys(this.isShow).forEach(key => { 92 if (key === id) { 93 this.isAgreed[key] = true; 94 this.isShow[key] = !this.isShow[key]; 95 } 96 }); 97 }, 98 closeModal() { 99 Object.keys(this.isShow).forEach(key => { 100 if (this.isShow[key] === true) this.isShow[key] = false; 101 }); 102 } 103 } 104}; 105</script> 106 107<style scoped> 108.list__item-link { 109 background-color: transparent; 110 width: 100%; 111 height: 100%; 112 position: absolute; 113 top: 0; 114 left: 0; 115} 116 117/* ボタン通常時 */ 118.btn { 119 pointer-events: none; 120} 121/* ボタンactive時 */ 122.isActive { 123 color: #fff; 124 background: blue; 125 cursor: pointer; 126 pointer-events: auto; 127} 128/* コンポーネントで同意後にtextの色をactiveにする */ 129.isAgreed { 130 color: red; 131} 132</style> 133

取得するjsonファイル

json

1{ 2 "data": { 3 "ids": [ 4 "id01", 5 "id02", 6 "id03" 7 ] 8 } 9}

モーダルとして表示する子コンポーネント
以下コンポーネントはjsonから取得するidの数だけ作成してあります。

vue

1<template> 2 <section> 3 4 <div v-if="id[index] == 'id01'"> 5 <h1>ID1のタイトル</h1> 6 </div> 7 <button type="button" class="btn btn-next" @click="doAgree"> 8 <span class="btn-text">Agree</span> 9 </button> 10 11 </section> 12</template> 13 14<script> 15import { request } from "~/data/index"; // 別ファイルで定義したrequest関数を取得 16 17export default { 18 data() { 19 return { 20 ids: [], 21 index: 0 22 }; 23 }, 24 mounted() { 25 this.$nextTick(() => { 26 const params = {}; 27 request(this.$axios, this.$store, params, this.$router) 28 .then(response => { 29 this.ids = response.data.ids}) 30 }); 31 }, 32 methods: { 33 // ボタンクリック時に、親コンポーネントのイベントを発火 34 doAgree() { 35 this.$emit("doAgree"); 36 } 37 } 38}; 39</script>

補足情報(ツールのバージョン)

"nuxt": "^2.4.2",

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

v-if="id == id" など、必ず true になる記述があるのが冗長です。
isShowisAgreed の数は、ids で決まるようなので、最初は空にしておき、
ids を取得したタイミングで代入すべきです。
あとは、HTML構造(ulがリストでなかったり)がおかしい部分があるので、見直したほうがいいと思います。


前提条件等が細かく書いていないので、そのまま当てはまるとは思いませんが、
参考までにどうぞ。
Nuxt.js を新しく立ち上げて、該当のファイルだけ編集すれば動くと思います。

AgreeDocument1,2,3の詳細がわかりませんが、
同じデザインのモーダルとして共通化しています。

/* /pages/index.vue */ <template> <div> <agree-document v-if="currentIndex !== null" :index="currentIndex" @agree="handleAgree($event);"></agree-document> <div class="wrapper"> <ul class="cards"> <li v-for="(wrappedId, index) in wrappedIds" :key="wrappedId.id" @click="openModal(index);" class="card"> <p>カード{{ index + 1 }}</p> <div> <p class="{ 'agreed': wrappedId.agreed }"> {{ wrappedId.agreed ? "Agreed" : "Not agreed" }} </p> </div> </li> </ul> <button :disabled="!completed"> <span v-if="completed">次へ</span> <span v-else>すべてに同意してください</span> </button> </div> </div> </template> <script> import AgreeDocument from "~/components/AgreeDocument"; export default { components: { AgreeDocument }, data() { return { wrappedIds: [], currentIndex: null // 現在のインデックスを保存するために使用します。 }; }, computed: { completed() { // Array.prototype.every() で調べてみてください。 return this.wrappedIds.every(wrappedId => wrappedId.agreed); } }, mounted() { // HTTP通信の代わりです。    // `ids` の数を増減しても対応しています。試してみてください。 const requestMock = Promise.resolve({ data: { ids: ["id01", "id02", "id03"] } }); requestMock.then(response => {     // { id: id, agreed: false } のオブジェクトとして、配列化します。 // `index`さえあれば「id名」も「そのid名について同意しているかどうか」を取得できます。 this.wrappedIds = response.data.ids.map(id => ({ id, agreed: false })); }); }, methods: { openModal(index) { this.currentIndex = index; }, closeModal() { this.currentIndex = null; }, handleAgree(index) { this.wrappedIds[index].agreed = true; this.closeModal(); } } }; </script> <style scoped> .cards { width: 100%; display: flex; padding: 0; margin: 0; flex-align: center; margin-bottom: 15px; } .card { box-sizing: border-box; width: 140px; list-style: none; padding: 30px; border: 1px solid #000; border-radius: 5px; margin: 0 10px; } </style>
/* /components/AgreeDocument.vue */ <template> <div @click="$emit('agree', index);" class="modal"> <p>Click to agree</p> </div> </template> <script> export default { name: "AgreeDocument", props: { index: { type: Number, required: true } } }; </script> <style scoped> .modal { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 100; width: 100%; height: 100%; display: flex; flex-direction: column; flex-align: center; align-items: center; color: #fff; background-color: rgba(0, 0, 0, 0.8); } </style>

投稿2019/02/19 15:14

NozomuIkuta

総合スコア1260

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aa316316

2019/02/20 05:49

>> NozomuIkutaさん ご回答ありがとうございます! 上記参考にさせていただき、リファクタを行ったところ、想定していた動作をかなり短いコードで実現することができました!とても助かりました。 特にdata内にisShow, isAgreedなど全ての状態を定義していたことで、ボタンのactivateなどid数が変わったときに対応できていませんでしたが、上記のように書くことで解決することができました! ただ、子コンポーネント(モーダル)内で必要な処理が増え(idのmaxlengthの受け渡しや戻るボタンの設置 etc)index.vueのhtml内でコンポーネントを読み込む際に、バインディングする値が増える+コンポーネントを10個程度読み込むことになりそうなので、その部分がまた長くなってしまっていますがそれはしょうがなさそうですね、、 大変助かりました、ありがとうございました。
NozomuIkuta

2019/02/20 14:57

子コンポーネントの仕様詳細がわからないので回答はできませんが、いくらか参考になったようでよかったです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問