20220116のvue.jsに関する記事は6件です。

【Vue.js】privateな変数の双方向バインディング方法

はじめに JavaScriptでprivateな変数やメソッドが作れるようになったようです。 私は業務でVue.jsを触っているため、「双方向バインディングする場合はどのように書くんだろう?」 と気になったので試してみました。 結果 getterとsetterを用意して、「v-model="クラス名.変数名"」で良いみたいでした。 検証 変数に「#」を付けることでprivateになるようです。 一旦setterは用意せず、以下のようなクラスを作成しました。 module.exports = class Test{ #detail; #tag; constructor() { this.#detail = null; this.#tag = null; } get detail() { return this.#detail; } get tag() { return this.#tag; } }; 画面側は以下のように実装しました。 <template> <div> <div class="control column"> <base-select v-model="test.tag" :options="testTags"></base-select> </div> <div class="control column"> <base-input v-model="test.detail" ></base-input> </div> </div> </template> <script> import BaseSelect from "~/components/BaseSelect.vue"; import BaseInput from "~/components/BaseInput.vue"; export default { components: { BaseSelect, BaseInput }, data() { const Test= require("~/server/api/models/test"); return { testTags: require("~/assets/testTags.json"), test: new Test(), }; } }; </script> この状態で画面上で値を変更してみると以下のようなエラーが発生します。 TypeError: Cannot set property tag of #<Test> which has only a getter 「setterを用意しないと値がセットできないぞ」ということです。 ということでsetterを用意したところ、想定通りに値を変更することができました。 結果に書いている通り「v-model="クラス名.変数名"」で良いみたいでした。 追加検証 次に「#」を外して同様に実行してみます。 getterもsetterも無い以下のようなクラスを用意します。 module.exports = class Test{ constructor() { this.detail = null; this.tag = null; } }; 画面のコードは同じもので、再度画面から値を変更してみるとエラーも何も発生せず変更することができます。 しかし、クラス内の変数をgetterとsetterを介さず直接操作をすることになってしまいます。(と思っています。) おわりに 親側にsetterを呼ぶメソッドを書いて、子側でそのメソッドを呼ぶことで値が変更できる、 そんな実装になるのかと思ったのですが、その必要はなくシンプルでよさそうでした。 参考になりましたら幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js 入門(第1回) - Vue.jsでHello World と双方向データバインディング(v-model) -

はじめに Vue.jsに少し触れてみたので、その内容を「Vue.js入門」として、数回にわけて書いていこうと思います。 Vue.jsはユーザーインターフェイスを構築するためのプログレッシブフレームワークとのことです。 1. Vue.jsでHello World 1.1. ディレクトリの作成 ルートディレクトリ配下にjsディレクトリとindex.htmlファイルを作成します。 jsディレクトリ配下にmain.jsファイルを作成します。 C:\Data\git\practice_vue.js\lesson\lesson1>tree /F フォルダー パスの一覧: ボリューム Windows ボリューム シリアル番号は F039-627E です C:. │ index.html │ └─js main.js C:\Data\git\practice_vue.js\lesson\lesson1> 1.2. HTMLの作成 index.htmlに以下を記載します。 index.html <!DOCTYPE html> <html lang="jp"> <head> <meta charset="utf-8"> <title>Vue.jsでHello World!</title> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script> <!-- (1) --> </head> <body> <div id="app"> <!-- (2) --> <p>{{ message }}</p> <!-- (3) --> </div> <script src="js/main.js"></script> <!-- (4) --> </body> </html> (1)の部分はVue.jsのを読み込んでいます。 (2)の部分でidを指定します。このidの名称(ここではapp)で、後で作成するVueのインスタンスとを紐付けます。 (4)の部分はVue.jsで記述された'main.js'ファイルを読み込んでいます。 (3)の部分は(4)で読み込んでいる'main.js'の中で定義されたmessageという名前のデータを表示しています。 1.3. jsの作成 jsディレクトリ配下のmain.jsに以下を記載します。 js/main.js var vm = new Vue({ // (1) el: '#app', // (2) data: { message: 'Hello World!' // (3) } }); (1)でVueコンストラクタからインスタンスを作成しています。 (2)elに'#app'を設定し、index.htmlのid="app"の部分と紐付けます。 (3)dataのmessageに'Hello World!'を設定ます。 1.4. 動作確認 ブラウザでindex.htmlを開くと以下のようにHello World!が表示されます。 2. 双方向データバインディング 2-1. index.htmlを修正 index.htmlの<div id="app">の中に<input type="text" v-model="message">を追加します。 以下のindex.htmlの(1)の部分。 index.html <!DOCTYPE html> <html lang="jp"> <head> <meta charset="utf-8"> <title>Vue.jsでHello World!</title> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script> </head> <body> <div id="app"> <input type="text" v-model="message"> <!--(1) --> <p>{{ message }}</p> </div> <script src="js/main.js"></script> </body> </html> (1)のv-modelはVueインスタンスのデータと、フォーム部品の値を関係づけます。 ここではVueインスタンスのデータmessageをフォーム部品inputに値を関係づけます。 これを「双方向データバインディング」といいます。 2.2. 動作確認 ブラウザでindex.htmlを開くと以下のようにHello World!が表示されます。 テキストボックスに何か入力すると、その内容が即時反映されます。 あとがき 次回は算出プロパティ(computed)と監視(watch)について書きたいと思います。 参考 Vue.js とほほのVue.js入門 リンク Vue.js 入門(第2回) - 算出プロパティ(computed)と監視(watch) -
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails × Vue で vue Router を使用する

rails アプリの中でVueを使用する場合にVue Routerを使ったページの切り替えができるまでの過程を書いていきます。 Vue Routerとは Vue Routerとは、Vue.jsを使ったアプリケーションでルーティング制御をするためのプラグインのことです。これを利用することでリクエストされたURLごとに表示するページを切り替えることができるようになります。 Vue Routerを使いページの切り替えができるようになるまで Vue Routerでページの切り替えをするには Vue Routerのインストール Vue Routerの設定 application.jsファイルでVue Router の設定をimportする 表示したいページ(コンポーネントの用意) rails側のルーティングの設定 が必要となるので順番に進めていきます。 Vue routerのインストール まずは Vue Routerをインストールしていきます。インストールの仕方は様々だと思いますが自分の環境では以下のコマンドでインストールを行いました。 yarn add vue-router これだけでインストールは完了です。次のステップに進みます。 Vue Routeの設定 インストールしたVue Routerで実際のルーティングを制御するファイルを作成していきます。まだ作成していないコンポーネントをインポートしますがこちらは後で作成します。 app/javascript/router.js というファイルを作成し、中身を import Vue from 'vue' import Router from 'vue-router' import Top from "../pages/top" import Test from '../pages/test' Vue.use(Router) export default new Router({ name: "Router", mode: 'history', routes: [ { path: '/', name: "top", component: Top, }, { path: '/test', name: "test", component: Test, }, ] }) とします。 application.jsファイルに Vue Router の設定をimportする 先ほど設定したURLごとのコンポーネントの切り替えを反映するためのコードを app/javascript/application.js に追加します。 import Vue from 'vue' import App from '../app.vue' import Router from'../router.js' // 追加 router.jsファイルのimport import 'bootstrap/dist/css/bootstrap.css' Vue.config.productionTip = false document.addEventListener('DOMContentLoaded', () => { const app = new Vue({ render: h => h(App), router: Router // 追加 router.jsに書いたルーティングごとに切り替えを使用できるようになる }).$mount() document.body.appendChild(app.$el) }) 表示したいページの準備 URLごとに表示するページ(コンポーネント)を作成していきます。 トップページの作成 ますはトップページを作成していきます。 app/javascript/pages/top.vue というファイルを作成し、中身を <template> <p>topページです</p> </template> <script> export default { name: "Top", } とします。 切り替え先ページの作成 app/javascript/pages/test.vue というファイルを作成し、中身を <template> <p>testページです</p> </template> <script> export default { name: "Test", } とします。 最後にHTMLファイルに実際に読み込まれる app/javascript/app.vue ファイルの変更を行います。(上で用意した二つのコンポーネントはこのファイルの中に埋め込まれる) <template> <div> <router-view /> // この部分にURLごとのファイルが埋め込まれる </div> </template> <script> export default { name: "App" } </script> Vue Routerを使用すると各コンポーネントは<div>タグの中に記述されている<router-view />の中に埋め込まれるようになるなります。これでページの準備は完了です。 rails側のルーティングを設定する rails側ではどんなURLにリクエストが来たとしても表示するページはひとつとなります。その中で埋め込むページを切り替えるのはVue側の役目となります。なのでrouter.rbの中身を以下とします。 Rails.application.routes.draw do root to: 'home#index' get '*path', to: 'home#index' # どのURLにリクエストが来たとしてもhome#indexアクションを使用する end これで全ての設定が完了です。 サーバーを起動し、http://localhost:3000、http://localhost:3000/test にアクセスすると表示が切り替わるようになります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【vue-awesome-swiper】サムネイル付きスライダーの実装

はじめに swiperを用いたスライダーの実装の記事はたくさんあったが、 サムネイル画像もスライダーにする実装ばかりでサムネイル画像を固定画像とするものが少なかったので 残しておきます。 環境 MacOS yarn 1.22.17 node v14.17.1 vue 2.6.14 vue-awesome-swiper 4.1.1 完成品 以下のようなサムネイル付きスライダーを作成します! 全体コード <template> <div class="room-swiper"> <Swiper ref="swiperRef" class="top" :options="swiperOption"> <SwiperSlide v-for="(slider, index) in sliderContents" :key="index" class="slide" > <img class="image" :src="slider" /> </SwiperSlide> </Swiper> <div class="thumbs"> <div v-for="(thumbs, index) in thumbsContents" :key="index" class="slide" :class="{ '-active': index === activeIndex }" > <img class="image" :src="thumbs" @click="clickHandler(index)" /> </div> </div> </div> </template> <script lang="ts"> import { defineComponent, ref } from '@nuxtjs/composition-api'; import SwiperClass from 'swiper'; import { Swiper, SwiperSlide } from 'vue-awesome-swiper'; export default defineComponent({ components: { Swiper, SwiperSlide, }, props: { sliderContents: { type: Array, required: true, }, }, setup() { type SwiperModule = typeof Swiper & { $swiper: SwiperClass; initSwiper: () => void; }; const swiperRef = ref<SwiperModule>(); const activeIndex = ref<number>(0); // 上のスワイパーは画面読み込み時2番目が表示されているため下のサムネイルも初期選択が2番目になるようにしている const thumbsContents = [ ...props.sliderContents.slice(1), props.sliderContents[0], ]; const swiperOption = { loop: true, loopedSlides: 3, spaceBetween: 10, slidesPerView: 3, autoplay: { delay: 5000, disableOnInteraction: false, }, on: { slideChange: () => { if (!swiperRef.value || !swiperRef.value.$swiper) return; activeIndex.value = swiperRef.value.$swiper.realIndex; }, }, }; const clickHandler = (index: number) => { if ( !swiperRef.value || !swiperRef.value.$swiper || !swiperRef.value.$swiper.controller ) return; swiperRef.value?.$swiper.slideToLoop(index); }; return { swiperRef, activeIndex, swiperOptionTop, clickHandler, thumbsContents, }; }, }); </script> 解説 スワイパー部分 propsで受け取ったsliderContents(画像の配列)をv-forで回しています。 また、optionを swiperOptionとして渡しています。 Swiper、SwiperSlide の使い方に関しては以下を参考にしてください。 https://github.com/surmon-china/vue-awesome-swiper <Swiper ref="swiperTop" class="top" :options="swiperOptionTop"> <SwiperSlide v-for="(slider, index) in sliderContents" :key="index" class="slide" > <img class="image" :src="slider" /> </SwiperSlide> </Swiper> オプション オプション一覧 const swiperTop = ref<SwiperModule>(); const activeIndex = ref<number>(0); const swiperOption = { loop: true, loopedSlides: 3, spaceBetween: 10, slidesPerView: 3, autoplay: { delay: 5000, disableOnInteraction: false, }, on: { slideChange: () => { if (!swiperTop.value || !swiperTop.value.$swiper) return; activeIndex.value = swiperTop.value.$swiper.realIndex; }, }, }; slideChangeオプションを指定することで、画像がスライドする時に発火してくれます。 realIndexにはスライドしている画像のインデックス番号が入ります。 on: { slideChange: () => { if (!swiperTop.value || !swiperTop.value.$swiper) return; activeIndex.value = swiperTop.value.$swiper.realIndex; }, }, サムネイル 画像クリック時にclickHandlerを発火させています。 今回はcssを省いていますが、v-forで回しているindex番号とactiveIndexが一致した時にクラスをつけるというイメージです。 そのクラスにopacityを指定すれば、それっぽくなると思います! <div class="thumbs"> <div v-for="(thumbs, index) in thumbsContents" :key="index" class="slide" :class="{ '-active': index === activeIndex }" > <img class="image" :src="thumbs" @click="clickHandler(index)" /> </div> </div> const clickHandler = (index: number) => { if ( !swiperTop.value || !swiperTop.value.$swiper || !swiperTop.value.$swiper.controller ) return; // 引数に指定したスライドに移動させる slideTo というメソッドを使って移動させる。loopの設定をしている場合はslideToLoop を使う swiperTop.value?.$swiper.slideToLoop(index); }; さいごに サムネイル画像付きのスライダーはなんかかっこいいですね!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vuetifyのコンポーネントをラップしたベースコンポーネントで、vuetifyと同じスロットを持ちたい

Vuetifyを使ってるときに下記のようなことを思うかもしれない。 Vuetifyコンポーネントをラップして独自の実装やデザインを組み込みこみたい けど、Vuetifyの豊富な機能と使い勝手を残したい こうした要望の一つとして、名前付きスロットをvuetifyコンポーネントと同じように使いたいということもある。 これは、v-slotの機能をうまく使うとさっくり実現できる。 (※理解不足な点があったり、一気に書いて文章見直してないので、おかしい点があるかもしれません。) 前置き:覚えておきたいv-slotの使い方 本題に入る前に覚えておきたいv-slotの使い方を頭に入れておく。 (基本的なv-slotの説明は省く) vue APIの$slotで、親から配信されたスロットコンテンツにアクセスする API - Vue.js $slotは、プログラム的にスロットによって配信されたコンテンツにアクセスするための手段。 具体的にみてみる。 こんな子コンポーネント(SlotTest.vue)を用意する。 <template> <p> <slot/> <slot name="slot1" /> <slot name="slot2" /> </p> </template> <script> export default { mounted() { // $slotsのキーを順番にコンロールログに出力 for (let slot in this.$slots) { console.log(slot); } }, } </script> 親要素でSlotTestにスロットコンテンツを渡す。 <template> <div id="app"> <SlotTest> <template> <p>デフォルトスロットです</p> </template> <template v-slot:slot1> <p>スロット1です</p> </template> <template v-slot:slot2> <p>スロット2です</p> </template> </SlotTest> </div> </template> <script> import SlotTest from "./components/SlotTest2"; export default { name: "App", components: { SlotTest, }, }; </script> 画面表示はこうなる。 気になるコンロールログにはこのように出力される。 こんなふうにスロットコンテンツにアクセスできる。 上記のように名前付きスロットのキー名にアクセスできるようになれば、下記のような使い方ができる。 名前付きスロットを動的に生成する 上記の子コンポーネントを下記のようにリファクタリングすることができる。 これにより、親で指定したslotを <template> <p> <template v-for="(slotContent, slotName) in $slots"> <slot :name="slotName" /> </template> </p> </template> <script> export default { mounted() { for (let slot in this.$slots) { console.log(slot); } }, } </script> これで親で指定したスロットコンテンツを動的に子コンポーネントで受け取れるようになる。 上記コードにおける疑問と自分なりの回答 上記コードで疑問に思ったのは、v-forで受け取ってるslotContentは使用してない。 ただ、使用してないからと言ってこれを削除するとスロットの中身を表示できなくなる。 <template v-for="(slotContent, slotName) in $slots"> <slot :name="slotName" /> </template> つまり、こちらで明示的に使用しなくても「受け取ったslotContentが<slot :name=”slotName”>に取って変わる」ということ。 だからv-forでのスロットコンテンツの受け取りは必須なんだと思う。 以上、多少の疑問は残りつつも、ひとまず動的にスロットを生成できるようになった。 以上の知識を利用して、冒頭の課題を解決する。 Vuetifyの知識を損なわずにVuetifyコンポーネントの機能を拡張する ようやく冒頭の話に戻ることができる。 たとえば、Vuetifyのv-texit-fieldコンポーネントでは、下記のような名前付きスロットを持っている。 append append-outer coutner label message prepend prepend-inner progress このv-text-fieldをラップしつつ、これらのスロットを使用したい場合はどうするか。 単純に考えると下記のようにする。 <template> <v-text-field> <slot name="append" /> <slot name="append-outer" /> <slot name="coutner" /> . . . <v-text-field> </template> けど、これじゃつらいし、漏れも起こりうる。 なので、このように書く。 <template> <v-text-field> <!-- v-slot:[slotName]で、v-text-fieldに名前付きスロットとして渡す --> <template v-for="(slotContent, slotName) in $slots" v-slot:[slotName]> <!-- 親から受け取ったスロットコンテンツが入る --> <slot :name="slotName" /> </template> <v-text-field> </template> これでv-text-fieldと同じように名前付きスロットを渡すことができる。 <CustomTextFiled> <template v-slot:append> <div>アペンド!</div> </template> <CustomTextFiled> 名前付きスロットを羅列するよりもはるかにすっきり。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

すでに作ったゲームサイトを、実際にJQueryからVueにしてみた

以前、こちらのサイトでカードを利用したボードゲームをオンラインで遊べるようにしてみた。 そして、ここでは以前作ったJQueryのオンラインのボードゲームをVueにしてみたらこうなる。 と言うイメージを書いてみました。 その後 まずはVueの勉強 JQueryをVueかにしようと思い、オンラインサイトを色々見てみた、、、 結果的に、良く分からなかった。やはりホントは違い必要最小限しか書いていないため、Vue.jsやらVUE CLIとか良く分からなかったので、Amazonで電子書籍を買った この本は中々良かったです。内容としては、最初にVue.jsをCDNを使う方法。 簡単なVueならこれで問題なし。 そして、Vue cliを使った本格的なプロジェクト構築。 その後にNuxt.jsを使ったページ管理ができるフレームワークの構築。 最後にはFirebaseとの接続など書いてった。 けど、今回はVue cliのプロジェクトまでわかれば良かったので、そこまで読んでからあとは実践。 その結果 まずはこちらがJQueryで頑張ったもの AsIs index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>THE GAME(ザ・ゲーム) ONLINE</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> <meta name="viewport" content="width=device-width,initial-scale=1"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}"> <script src="{{ url_for('static', filename='js/script.js') }}"></script> </head> <body> このゲームは1~5人用となっています。<br/> <div id='app2'> <div id='entry' style='display:none'> <input id="cName_inp" placeholder="ニックネームを入力してください"><br/> <br/> {% if gameid %} {% else %} <button id='createGame'>ゲームを作る(Make a Game)</button><br/> Game ID: <span id='gId'></span><br/> <input type='text' id='uriWgId' /><input type='button' id='clickCopy' value='copy'><br/> {% endif %} Game Status: <span id='gStatus'></span><br/> <hr/> <button id="joinGame">ゲームに参加する(Join the Game)</button><br/> {% if gameid %} <input type='hidden' id="gId_inp" value='{{ gameid }}'><br/> {% else %} <input id="gId_inp" placeholder="GameIDを入力してください"><br/> {% endif %} Your ID: <span id='cId'></span><br/> Your Name: <span id='cName'></span><br/> <hr/> <h2>Member Applying</h2> <span id='applyingList'></span> <br/> </div> <div id='sec1' style='display:none'> <hr/> <button id="startGame">ゲームを始める(Let's start the Game)</button><br/> <input type='checkbox' name='rule_type' id="rule_type" value='original'>オリジナルルール(Use Original Rule)<br/> 1.&nbsp;直前がぞろ目ならぞろ目を出せる(大小制限なし)<br/> 2.&nbsp;10単位での戻りが無制限(大小制限なし)<br/> </div> <div id='sec2' style='display:none'> <h2>Member Rotation</h2> <span id='routeList'></span> <hr/> Your Turn: <span id='yTurn'></span><br/> Your Cards:<br/> <span id='holdCards'></span><br/> <div id='sec3' style='display:none'> <input type='number' min='2' max='99' id="cardNum_inp" placeholder="番号を入力してください" style="width:200px;"><br/> <button id="putCard">カードを出す(Put the cards)</button><br/> <br/> <button id="nextPlayer">次の人へ(Go on Next user)</button><br/> </div> <hr/> Game Cards:<br/> <input type='radio' name='area' id='area' value='0'/>[100&nbsp;>&nbsp;2]&nbsp;<span id='gameCards0'></span><br/> <input type='radio' name='area' id='area' value='1'/>[100&nbsp;>&nbsp;2]&nbsp;<span id='gameCards1'></span><br/> <input type='radio' name='area' id='area' value='2'/>[1&nbsp;>&nbsp;99]&nbsp;<span id='gameCards2'></span><br/> <input type='radio' name='area' id='area' value='3'/>[1&nbsp;>&nbsp;99]&nbsp;<span id='gameCards3'></span><br/> Submited Cards in turn:<br/> <div id='submitCards'></div> <hr/> </div> <br/> <span id='message'></span><br/> </div> </body> </html> script.js var timeout = 1000; var timer = ''; $(function() { var gId = ''; var cId = ''; $('#entry').show(); $('#clickCopy').click(function(){ var text = $('#uriWgId').val(); var clipboard = $('<textarea></textarea>'); clipboard.text(text); $('body').append(clipboard); clipboard.select(); document.execCommand('copy'); clipboard.remove(); }); // Create Game $('#createGame').click(function() { $('#message').empty(); $.ajax('/create' + '/' + $('#cName_inp').val(), { type: 'get', } ) .done(function(data) { $('#gId').text(data); $('#cId').text(data); $('#cName').text($('#cName_inp').val()); $('#gStatus').text('waiting'); $('#uriWgId').val(location.href + data + '/join'); gId = data; cId = data; $('#sec1').show(); timer = setTimeout(status_check(gId, cId), timeout) }) .fail(function() { $('#message').text('エラーが発生しました'); }); }); // Join Game $('#joinGame').click(function() { $('#message').empty(); $.ajax('/' + $('#gId_inp').val() + '/join/' + $('#cName_inp').val(), { type: 'get', } ) .done(function(data) { _tmp = data.split(' ,'); $('#cId').text(_tmp[0]); $('#cName').text(_tmp[1]); $('#gStatus').text(_tmp[2]); gId = $('#gId_inp').val(); cId = _tmp[0]; timer = setTimeout(status_check(gId, cId), timeout) }) .fail(function() { $('#message').text('エラーが発生しました'); }); }); // Start Game $('#startGame').click(function() { $('#message').empty(); $.getJSON('/' + gId + '/start/' + $('input:checkbox[name="rule_type"]:checked').val(), { type: 'get', } ) .done(function(data) { }) .fail(function() { $('#message').text('エラーが発生しました'); }); }); // Put your card $('#putCard').click(function() { $('#message').empty(); // put your card $.ajax('/' + gId + '/' + cId + '/set/' + $('input[name="area"]:checked').val() + '/' + $('#cardNum_inp').val(), { type: 'get', } ) .done(function(data) { $('#message').text(data); }) .fail(function() { $('#message').text('エラーが発生しました'); }); $('#cardNum_inp').val('') }); // Next player $('#nextPlayer').click(function() { $('#message').empty(); $.ajax('/' + gId + '/next', { type: 'get', } ) .done(function(data) { // console.log(data) $('#message').text('次の人に移動しました'); }) .fail(function() { $('#message').text('エラーが発生しました'); }); }); }); var status_check = function(gId, cId){ setTimeout(function(){ $('#message').empty(); // all status $.getJSON('/' + gId + '/status', { type: 'get', } ) .done(function(data) { console.log(data) $('#gStatus').text(data.status); playerPos = 0; // Applying List $('#applyingList').empty(); for(var pIdx in data.players){ // console.log(data.players[pIdx]) $('#applyingList').append(data.players[pIdx].nickname + '(' + data.players[pIdx].playerid + ')' + ','); if(cId == data.players[pIdx].playerid){ playerPos = pIdx; } } if(data.status == 'started'){ $('#entry').hide() $('#sec2').show() // route List $('#routeList').empty(); for(var rIdx in data.routelist){ if(data.routelist[rIdx].playerid == data.routelist[data.routeidx].playerid){ $('#routeList').append('<b>&gt;' + data.routelist[rIdx].nickname + '</b><br/>'); }else{ $('#routeList').append(data.routelist[rIdx].nickname + '<br/>'); } } // game cards $('#gameCards0').text(data.hightolow[0]) $('#gameCards1').text(data.hightolow[1]); $('#gameCards2').text(data.lowtohigh[0]); $('#gameCards3').text(data.lowtohigh[1]); // hold cards $('#holdCards').text(data.players[playerPos].holdcards.join(',')) // submit cards $('#submitCards').text(data.submit.join(',')) // checking turn if(data.routelist[data.routeidx].playerid == cId){ $('#yTurn').text('your turn') $('#sec3').show(); if(data.submit.length >= 2){ $('#nextPlayer').prop('disabled', false); }else{ $('#nextPlayer').prop('disabled', true); } }else{ $('#yTurn').text('not your turn') $('#sec3').css('display', 'none'); } } }) .fail(function() { $('#message').text('エラーが発生しました'); }); timer = setTimeout(status_check(gId, cId), timeout) }, timeout); } ToBe App.vue <template> <v-container> <v-flex xs10> <v-card-title class="font-weight-bold">THE GAME</v-card-title> <v-card-text>このゲームは1~5人用となっています。</v-card-text> <br/> <div v-if="game == '***'"> <v-text-field v-model="cName_inp" label="ニックネームを入力してください" filled></v-text-field><br/> <div v-if="!game_id"> <v-btn v-on:click='createGame'>ゲームを作る(Make a Game)</v-btn><br/> </div> <div v-else> <v-btn v-on:click="joinGame">ゲームに参加する(Join the Game)</v-btn><br/> </div> </div> <div v-else> <!-- {{ game }} --> <div v-if="game.status == 'waiting'"> Your ID: {{ client_id }}<br/> Your Name: {{ nick_name }}<br/> <!-- clint:{{ client }} --> <div v-if="!guest"> <v-row> <v-col cols='12' sm='6'> <v-text-field v-model='uriWgId' readonly></v-text-field> </v-col> <v-col cols='12' sm='6'> <v-btn @click='clickCopy'>copy</v-btn> </v-col> </v-row> <br/> <v-btn v-on:click="startGame">ゲームを始める(Let's start the Game)</v-btn><br/> <v-checkbox v-model='rule_type'> <template v-slot:label> オリジナルルール(Use Original Rule)<br/> 1.&nbsp;直前がぞろ目ならぞろ目を出せる(大小制限なし)<br/> 2.&nbsp;10単位での戻りが無制限(大小制限なし)<br/> </template> </v-checkbox> </div> </div> <div v-else-if="game.status == 'started'"> Your ID: {{ client_id }}<br/> Your Name: {{ nick_name }}<br/> Last cards: {{ game.stocks.length }}<br/> <br/> Your Turn: {{ (turn? 'Your turn':'Not your turn') }} <br/> Your Cards:<br/> <table> <tr> <td v-for="(value, key) in client.holdcards" :key="key"> <ul> <template v-if='turn'> <li v-on:click='putCard(value)' class='card'> {{ value }} </li>&nbsp; </template> <template v-else> <li class='card'> {{ value }} </li>&nbsp; </template> </ul> <!-- <template v-if='turn'> <span v-on:click='putCard(value)' class='card'> {{ value }} </span>&nbsp; </template> <template v-else> <span class='card'> {{ value }} </span>&nbsp; </template> --> </td> </tr> </table> <br/> <br/> <template v-if="turn"> <v-btn v-on:click="nextPlayer">次の人へ(Go on Next user)</v-btn><br/> </template> </div> <hr/> <br/> <h2>Member Applying</h2> <span v-for="(player, key) in game.players" :key="key"> {{ player.nickname }}({{ player.playerid }})&nbsp; </span> <br/> <h2>Game Cards</h2> 下から配置したい場所をエリアを選択してください<br/> <table class='board'> <tr v-for="(hightolow, key) in game.hightolow" :key="key" class='board'> <td v-on:click='setArea(key)' class='board'> <template v-if="key === sel_area"> ★ </template> <ul> <li v-for="(card, key) in hightolow" :key="key"> <span class='card'> {{ card }} </span>&nbsp; </li> </ul> </td> </tr> </table> <table> <tr v-for="(lowtohigh, key) in game.lowtohigh" :key="key" class='board' > <td v-on:click='setArea(key+2)' class='board'> <template v-if="key+2 === sel_area"> ★ </template> <ul> <li v-for="(card, key) in lowtohigh" :key="key"> <span class='card'> {{ card }} </span>&nbsp; </li> </ul> </td> </tr> </table> </div> {{ error_message }} </v-flex> </v-container> </template> <script> const axios = require('axios'); export default { data: function(){ return { cName_inp: '', game: '***', error_message: '', guest: false, turn: false, rule_type: false, game_id: '', uriWgId: 'aaaa', area: '', sel_area: '', } }, mounted() { this.game_id = this.$route.query.game_id; this.guest = (this.$route.query.game_id != ''? true : false); console.log(process.env.NODE_ENV); }, methods: { clickCopy: function() { navigator.clipboard.writeText(this.uriWgId) .then(() => { // console.log("copied!"); alert('copied!'); }) .catch(e => { console.error(e); }) }, createGame: function() { axios.get(process.env.VUE_APP_API_BASE_URL+'create/'+this.cName_inp).then((res) => { this.game_id = res.data; this.client_id = this.game_id; this.guest = false; // this.uriWgId = 'http://localhost:5000/?game_id='+this.game_id; this.uriWgId = window.location.href+'?game_id='+this.game_id; console.log(res.data); setInterval(() => { this.status_check(); }, 1000); }) }, joinGame: function() { axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/join/'+this.cName_inp).then((res) => { console.log(res.data); this.client_id = res.data.split(',')[0].trim(); this.guest = true; setInterval(() => { this.status_check(); }, 1000); }) }, startGame: function() { axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/start'+(this.rule_type?'/original':'')).then((res) => { this.game = res.data; // console.log(this.game); }) }, nextPlayer: function() { axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/next'); this.error_message = ''; }, putCard: function(value) { if(this.sel_area === ''){ alert('下から配置したいエリアを選択してください'); return; } axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/'+this.client_id+'/set/'+this.sel_area+'/'+value) .then(res => { this.error_message = res.data; }) }, setArea: function(value) { this.sel_area = value; }, status_check: function() { axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/status').then((res) => { this.game = res.data; // console.log(this.game); this.client = res.data.players.find((v) => v.playerid.trim() == this.client_id.trim()); this.nick_name = this.client.nickname; if(this.game.status == 'started'){ this.turn = (this.game.routelist[this.game.routeidx].playerid == this.client_id? true: false); } }) }, }, } </script> Vue.jsでも少しゴミゴミした書き方になってしまいましたが、JQueryとVueの違いを理解してもらうには十分かと思います。 結果的にわかったこと コードをhtml/jsで分けて考えず、htmlを1つのjsと考えながらプラグラミングできる便利さ データの反映について、いちいちjs側で出力ロジックを記載しなくても、必要な変数を更新すれば勝手に反映される なんか、htmlへ表示する際に、わざわざ出力ロジックを書く必要がないのが大変便利。 ということで、今後はVue.jsをメインに使っていこうと思います。 また、Nuxt.jsや、Firebaseなどのリアルタイムデータベースを使ったものにバージョンアップしていきたいと思います。 なお、Vueで書き直した「THE GAME」はこちらのリンクから遊べます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む