- 投稿日:2019-11-28T22:12:48+09:00
Vue.js でマインスイーパっぽいものを作ってみた
?初めに
コーディングの練習のために、個人的に慣れ親しんでいる Vue.js で単純な実装のゲームとされるマインスイーパを作ってみました。
マインスイーパの仕様をきちんと調べたわけではないため、動きに若干不安がありますが、そのあたりは漸次改善していく予定です。
?本題
環境
バージョン Node v12.11.1 npm 6.11.3 vue-cli 4.0.5 成果物
それっぽい動きしています。
実装のポイント
マインスイーパーの各マスを作る
<template v-for="(cell, yi) in row"> <td v-if="started" :key="yi" class="cell cell--started" :class="{ 'cell--opened': cell.isOpen }" @click="open(xi, yi)" @click.right.prevent="mark(xi, yi)" > <span v-if="cell.isOpen"> {{ cell.isBomb ? "?" : cell.bombCount === 0 ? "" : cell.bombCount }} </span> <span v-if="cell.isMark"> ? </span> </td> <td v-if="!started" class="cell cell--unstarted" :key="yi" > </td>マインスイーパーの盤面自体は table 要素で作っています。
地雷が配置される各マスは、td 要素で表現しています。
v-if によりフラグを切り替えることで、以下の状態を切り替えています。
- そもそもゲームが開始されているかどうか
- マスが開かれているかどうか
- 旗が立てられているかどうか
また以下のように、マスをクリックすることで、マスを開いたり旗を立てたりする処理を発火させるようにしています。
- 左クリック ... マスを開く処理を発火する
- 右クリック ... 旗を立てる処理を発火する
地雷を配置する
/** * 地雷を配置します. */ _layMines() { // ランダムな整数値を返す const getRandomInt = max => { return Math.floor(Math.random() * Math.floor(max)); } for (let i = 0; i < this.numOfBomb; i++) { const x = getRandomInt(this.maxX); const y = getRandomInt(this.maxY); // 既に地雷が置かれている場合、別のセルに置くようにする if (this.area[x][y].isBomb) { i--; continue; } this.area[x][y].isBomb = true; // 地雷に隣接するセルの周囲の地雷数を増やす // 上のセル if (x-1 >= 0) { this.area[x-1][y].bombCount++; if (y-1 >= 0) this.area[x-1][y-1].bombCount++; if (y+1 < this.maxY) this.area[x-1][y+1].bombCount++; } // 横のセル if (y-1 >= 0) this.area[x][y-1].bombCount++; if (y+1 < this.maxY) this.area[x][y+1].bombCount++; // 下のセル if (x+1 < this.maxX) { this.area[x+1][y].bombCount++; if (y-1 >= 0) this.area[x+1][y-1].bombCount++; if (y+1 < this.maxY) this.area[x+1][y+1].bombCount++; } } } }設定された地雷の数だけ繰り返して地雷を盤面に設置しています。
ランダムな x 行目・ y 列目を取得し、その x 行 y 列のマスにボムであるフラグを立てます。
フラグを立てた後、周囲8マスの「周囲に地雷がある数」を +1 させています。マスを開く
/** * セルを開きます. * @param {Number} x 何行目. * @param {Number} y 何列目. */ open(x, y) { // ?だったら終了 if (this.area[x][y].isBomb) { alert("Bomb!!!"); for (let i = 0; i < this.maxX; i++) { for (let j = 0; j < this.maxY; j++) { this.area[i][j].isOpen = true; } } } this.area[x][y].isOpen = true; if (this.area[x][y].bombCount !== 0) return; // 周囲に地雷がない空きセルを開く // 左のセル if (x-1 >= 0 && this.area[x-1][y].autoOpenable()) { this.open(x-1, y); } // 右のセル if (x+1 <= this.maxX-1 && this.area[x+1][y].autoOpenable()) { this.open(x+1, y); } // 上のセル if (y-1 >= 0 && this.area[x][y-1].autoOpenable()) { this.open(x, y-1); } // 下のセル if (y+1 <= this.maxY-1 && this.area[x][y+1].autoOpenable()) { this.open(x, y+1); } },マスを開く処理では、単にクリックされたマスを開くとともに、「周囲に一つも地雷がない」隣接するマスを再帰的に開いていく処理を行っています。
具体的には、以下のような再帰処理を行っています。
1回目に実行される(A)では、クリックされたマスに対して判定処理を行います。
- (A)マスの上下左右を開くことができるか判定する。
- 開くことができる場合
- マスを開く。
- 開いたマスに対して、(A)の処理を行う。
- 開くことができない場合
- 終了。
マスを開くことができるか判定する処理は、以下の通りです。
/** * 自動で開くことができるか判定して返します. * @return {Boolean} */ autoOpenable() { return !this.isOpen && !this.isBomb; }旗を立てる
/** * セルに旗を立てたり、立てなかったりします. * @param {Number} x 何行目. * @param {Number} y 何列目. */ mark(x, y) { if (!this.area[x][y].isOpen) { this.area[x][y].isMark = !this.area[x][y].isMark; } },旗を立てる処理はごく単純で、マスがまだ開かれていない場合、旗を立てるか立てないかを切り替えるようにしています。
?おわりに
今回はマインスイーパ(?)を作りましたが、今後はテトリスやぷよぷよも作ってみたいと思います。
Vue.js で作るのもいいですし、生のJSやSVG、canvasで作るのも面白そうです。参考文献
- 投稿日:2019-11-28T22:11:35+09:00
Vue.js【 transitionを使ってメニューバーを作成 】
はじめに
三ヶ月近く触っているとVue.jsに慣れ始めてきました。進捗は相変わらず遅いですが、、、
そんなわけで以前から作れるようになりたいと思っていたメニューバーを作成していきたいと思います。動作
完成コード
htmlファイルにコピペしてもらえば動くはずですので是非試してみてください。
index.html<!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>MENU BAR</title> </head> <body> <div id="app"> <nav class="entireMenuBar"> <ul> <div class="parentMenuBar1"> <li @click="{menuToggle1 = !menuToggle1,menuToggle2 = false}">親要素1</li> <transition> <div class="childMenuBar2" v-show="menuToggle1"> <ul> <li>子要素1-1</li> <li>子要素1-2</li> <li>子要素1-3</li> </ul> </div> </transition> </div> </ul> <ul> <div class="parentMenuBar2"> <li @click="{menuToggle2 = !menuToggle2,menuToggle1 = false}">親要素2</li> <transition> <div class="childMenuBar2" v-show="menuToggle2"> <ul> <li>子要素2-1</li> <li>子要素2-2</li> <li>子要素2-3</li> </ul> </div> </transition> </div> </ul> </nav> <div id="main" @click="{menuToggle2 = false ,menuToggle1 = false}"> <br><br><br><br><br><br> <pre>{{ $data }}</pre> </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ data:{ menuToggle1:false, menuToggle2:false, } }).$mount('#app') </script> </body> <style media="screen"> .childMenuBar1,.childMenuBar2{ position:relative; margin-top:6px; border-radius:2px; background-color:lightblue; z-index:-1; } body{ margin:0; padding:0; background-color:lightgray; } ul{ list-style:none; padding:0; margin:0; } li{ margin:0; cursor:pointer; } .entireMenuBar{ background-color: #eee; padding:5px; display:flex; height:30px; } .parentMenuBar1,.parentMenuBar2{ width:80px; padding:5px; margin:0px 0px 0px 20px; border-radius:5px; } #main{ height:100%; } .v-enter-active,.v-leave-active{ transition: transform .2s ease-out; } .v-enter,.v-leave-to { transform: translateY(-90px); } </style> </html>トランジションに注目
以下のように書くことでCSSでアニメーションを設定するように要素を動かすことができます。
index.html.v-enter-active,.v-leave-active{ transition: transform .2s ease-out; } .v-enter,.v-leave-to { transform: translateY(-90px); }
.v-enter-active,.v-leave-active
の中で動きの種類とそれにかける時間を指定しています。
.v-enter,.v-leave-to
の中で動きの開始時と終了時の状態を指定しています。動きの流れ的には、、、
アニメーション開始時
1,v-enter
2,v-enter-activeアニメーション終了時
3,v-leave-active
4,v-leave-toといった流れです。
そしてこの動きをつけたい要素を
<transition>動きをつけたい要素</transition>
のようにトランジションタグで囲んであげれば
動画のような動きをしてくれる要素の完成です。以上です。最後までお読みいただきありがとうございました。
- 投稿日:2019-11-28T21:58:27+09:00
ポケモン X Vue
https://www.youtube.com/watch?v=8pFkiNvxnD0
のビデオを見て感動したので記事を書いています。
githubページが載っているので、誰のためでもなくただ自分のために書きます。vueのロジックだけです。
src/componentsの中だけの説明です。
まず、Pokemon.vueには
Pokemon.vue<template> <div class="container"> <h1>Pokemon's</h1> <PokemonSearch :apiUrl="apiUrl" @setPokemonUrl="setPokemonUrl" /> <PokemonList :imageUrl="imageUrl" :apiUrl="apiUrl" @setPokemonUrl="setPokemonUrl" /> <PokemonDetail v-if="showDetail" :pokemonUrl="pokemonUrl" :imageUrl="imageUrl" @closeDetail="closeDetail" /> </div> </template> <script> import PokemonSearch from './PokemonSearch.vue'; import PokemonList from './PokemonList.vue'; import PokemonDetail from './PokemonDetail.vue'; export default { data: () => { return { imageUrl: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/', apiUrl: 'https://pokeapi.co/api/v2/pokemon/', pokemonUrl: '', showDetail: false } }, components: { PokemonSearch, PokemonList, PokemonDetail }, methods: { setPokemonUrl(url) { this.pokemonUrl = url; this.showDetail = true; }, closeDetail() { this.pokemonUrl = ''; this.showDetail = false; } } } </script>見てわかる通り。
PokemonList.vue<template> <div class="detail"> <div class="detail-view" v-if="show"> <div v-if="pokemon" class="image"> <img :src="imageUrl + pokemon.id + '.png'" alt=""> </div> <div v-if="pokemon" class="data"> <h2>{{ pokemon.name }}</h2> <div class="property"> <div class="left">Base Experience</div> <div class="right">{{ pokemon.base_experience }} XP</div> </div> <div class="property"> <div class="left">Height</div> <div class="right">{{ pokemon.height / 10 }} m</div> </div> <div class="property"> <div class="left">Weight</div> <div class="right">{{ pokemon.weight / 10 }} kg</div> </div> <h3>Pokemon Types</h3> <div class="types"> <div class="type" v-for="(value, index) in pokemon.types" :key="'value'+index"> {{ value.type.name }} </div> </div> <h3>Abilities</h3> <div class="abilities"> <div class="ability" v-for="(value, index) in pokemon.abilities" :key="'value'+index"> {{ value.ability.name }} </div> </div> </div> <h2 v-else>The pokemon was not found</h2> <button class="close" @click="closeDetail">close</button> </div> <i v-else class="fas fa-spinner fa-spin"></i> </div> </template> <script> export default { props: [ 'pokemonUrl', 'imageUrl' ], data: () => { return { show: false, pokemon: {} } }, methods: { fetchData() { let req = new Request(this.pokemonUrl); fetch(req) .then((resp) => { if(resp.status === 200) return resp.json(); }) .then((data) => { this.pokemon = data; this.show = true; }) .catch((error) => { console.log(error); }) }, closeDetail() { this.$emit('closeDetail'); } }, created() { this.fetchData(); } } </script>このプログラムはポケモンのイラストをリスト状に展開してそれをクリックすると
そのポケモンのデータが表示されているモーダルが
表示されるというプログラムになっています。つまり、このプログラムは後述するcomponents/PokemonList.vueからデータを渡されて
そのままそのデータを表示する流れとなっています。
methods: {
fetchData() {
let req = new Request(this.pokemonUrl);
fetch(req)
.then((resp) => {
if(resp.status === 200)
return resp.json();
})
.then((data) => {
this.pokemon = data;
this.show = true;
})
.catch((error) => {
console.log(error);
})
},
methodsのfetchData()でデータを取得していることが解ると思います。PokemonList.vue <template> <div class="list"> <article v-for="(pokemon, index) in pokemons" :key="'poke'+index" @click="setPokemonUrl(pokemon.url)"> <img :src="imageUrl + pokemon.id + '.png'" width="96" height="96" alt=""> <h3>{{ pokemon.name }}</h3> </article> <div id="scroll-trigger" ref="infinitescrolltrigger"> <i class="fas fa-spinner fa-spin"></i> </div> </div> </template> <script> export default { props: [ 'imageUrl', 'apiUrl' ], data: () => { return { pokemons: [], nextUrl: '', currentUrl: '' } }, methods: { fetchData() { let req = new Request(this.currentUrl); fetch(req) .then((resp) => { if(resp.status === 200) return resp.json(); }) .then((data) => { this.nextUrl = data.next; data.results.forEach(pokemon => { pokemon.id = pokemon.url.split('/') .filter(function(part) { return !!part }).pop(); this.pokemons.push(pokemon); }); }) .catch((error) => { console.log(error); }) }, scrollTrigger() { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if(entry.intersectionRatio > 0 && this.nextUrl) { this.next(); } }); }); observer.observe(this.$refs.infinitescrolltrigger); }, next() { this.currentUrl = this.nextUrl; this.fetchData(); }, setPokemonUrl(url) { this.$emit('setPokemonUrl', url); } }, created() { this.currentUrl = this.apiUrl; this.fetchData(); }, mounted() { this.scrollTrigger(); } } </script>まずポケモンのデータをぶん回しているところから
<div class="list">
<article v-for="(pokemon, index) in pokemons"
:key="'poke'+index"
@click="setPokemonUrl(pokemon.url)">
<img :src="imageUrl + pokemon.id + '.png'" width="96" height="96" alt="">
<h3>{{ pokemon.name }}</h3>
</article>
<div id="scroll-trigger" ref="infinitescrolltrigger">
<i class="fas fa-spinner fa-spin"></i>
</div>
methods: { fetchData() { let req = new Request(this.currentUrl); fetch(req) .then((resp) => { if(resp.status === 200) return resp.json(); }) .then((data) => { this.nextUrl = data.next; data.results.forEach(pokemon => { pokemon.id = pokemon.url.split('/') .filter(function(part) { return !!part }).pop(); this.pokemons.push(pokemon); }); }) .catch((error) => { console.log(error); }) },の中fetchData()が面白いので説明すると
```fetch(req)
.then((resp) => {
if(resp.status === 200)
return resp.json();
})
.then((data) => {this.nextUrl = data.next; data.results.forEach(pokemon => { pokemon.id = pokemon.url.split('/') .filter(function(part) { return !!part }).pop(); this.pokemons.push(pokemon); }); })ポケモンのurlごとのデータを分割して最後の要素を取得しています。 MDNのサイトを参照のこと https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/filter https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/pop 以上です。 お疲れ様でした。
- 投稿日:2019-11-28T20:20:46+09:00
Vue.jsの開発環境をDockerで構築する手順
Vue.jsの開発環境をDockerで構築する手順を整理します。
実際に環境構築した後のソースコードはこちら
事前準備
Dockerで開発環境を構築するために必要な事項を確認します。
Vue CLI
今回は Vue CLI でアプリを作ります。
Vue CLI をインストールしてない場合は、そのインストールからおこないます。(ついでに @vue/cli-init もインストールします。)
sudo npm install -g @vue/cli @vue/cli-initバージョン確認できれば、正常にインストールできています。
vue -V @vue/cli 4.0.5インストール後、
webpack-simple
でアプリを作成します。vue init webpack-simple your_app_nameDocker
Docker をインストールしていない場合は、Docker Desktop on Mac もしくは Docker Desktop on Windows 等からインストールしてください。
バージョン確認できれば、正常にインストールできています。
docker version合わせて
docker-compose
コマンドが使えることも確認しておきます。docker-compose --version開発環境 Docker 化の手順
それでは本題に入り、Vue.jsの開発環境をDockerで構築する手順を整理します。
1. vue-cli-service のインストール
まず Docker コンテナ内で Vue.js を起動するのに必要な
vue-cli-service
をインストールします。npm install --save @vue/cli-serviceインストール後、
package.json
にscripts.serve
の設定を追加します。(該当箇所以外は省略してます。)package.json{ "scripts": { "serve": "node_modules/.bin/vue-cli-service serve" } }2. webpack のインストール
アプリ起動時にコンパイルエラーが出ないように、webpack を追加しておきます。
npm add webpack@latest3. Docker の設定
必要な下準備が整ったので、Docker の設定ファイルを書いていきます。
Dockerfile.dev の作成
アプリのルートディレクトリに
Dockerfile.dev
を作成します。Dockerfile.devFROM node:10.17.0-alpine3.9 # make the 'app' folder the current working directory WORKDIR /app # copy both 'package.json' and 'package-lock.json' (if available) COPY package*.json ./ # install project dependencies RUN npm install # copy project files and folders to the current working directory (i.e. 'app' folder) COPY . . CMD ["npm", "run", "serve"]
FROM
Docker イメージのベース(バージョンは DockerHub で選んでください)WORKDIR
ワーキングディレクトリの作成COPY package*.json ./
依存関係のコピーRUN npm install
依存関係のインストールCOPY . .
Docker コンテナ内にファイルを渡すCMD ["npm", "run", "serve"]
コマンドでサーバ起動.dockerignore の作成
また、
.dockerignore
を作成し、無視してOKなファイルを Docker に知らせます。.dockerignorenode_modules .git .gitignoreDocker の設定は以上です。
4. Dockerコンテナ内で起動
コマンドを叩けば、Docker コンテナ内で Vue.js のアプリを起動できます。
docker build
まず、
Dockerfile.dev
から Docker イメージをビルドします。docker build --tag your_app_name:latest --file Dockerfile.dev .
--tag
タグ名(タグをつけてbuild
すれば、その名前でrun
できる)--file
設定ファイルを指定(デフォルトはDockerfile
)docker run
ビルドした Docker イメージを使ってコンテナを起動します。
docker run --rm -it --name your_app_container -p 8080:8080 -v ${PWD}:/app -v /app/node_modules your_app_name:latest
--rm
終了時にコンテナを自動削除-i
ターミナルのテキストコマンドをコンテナに入力-t
いい感じのフォーマットでターミナルに出力--name
コンテナに名前をつける-p
ポートマッピング-v
Docker ボリューム(ローカルのコードを参照させれば、変更をただちに反映可)$(PWD)
present working directory問題なければ、ブラウザから
http://localhost:8080/
でアクセスできます。Yay!ターミナルで
Ctrl + C
でコンテナを終了できます。docker-compose でより簡単に起動
毎回毎回
docker build...
、docker run...
とコマンドを叩くのは面倒なので、docker-compose を使ってより簡単に起動できるようにします。docker-compose の設定
docker-compose.yml
をルートディレクトリに作成し、Docker コマンドの内容を YML 形式で記述します。docker-compose.ymlversion: '3.7' services: web: container_name: web build: context: . dockerfile: Dockerfile.dev volumes: - '.:/app' - '/app/node_modules' ports: - '8080:8080'設定はこれだけです。
docker-compose コマンド
これにより以下のコマンドが使えます。
docker-compose up
コンテナの起動します。
docker-compose up先ほどと同様に
http://localhost:8080/
でアクセスできます。かなりシンプルになりました。
docker-compose up --build
コンテナを再ビルドして起動します(変更を反映させられる)。
docker-compose up --builddocker-compose up -d
コンテナをバックグラウンドで起動します。
docker-compose up -ddocker-compose down
コンテナを終了させます。
docker-compose downdocker-compose にテストを追加
docker-compose.yml
にtest
サービスを追加し、そのコンテナ内でテストを実行できるようにします。docker-compose.ymlversion: '3.7' services: web: container_name: web build: context: . dockerfile: Dockerfile.dev volumes: - '.:/app' - '/app/node_modules' ports: - '8080:8080' test: container_name: test build: context: . dockerfile: Dockerfile.dev volumes: - '.:/app' - '/app/node_modules' command: ["npm", "run", "test"]※ *Vue.js でのテストのセットアップ手順は、「Vue.js ユニットテストの基本まとめ」参照
次のコマンドでテスト用のコンテナサービスを起動できます。
docker-compose up testサービス名を指定しない場合は、web・test の両サービスが起動します。
docker-compose upトラブルシューティング
発生する可能性のあるエラーとその解決方法をまとめておきます。
エラー(その1)
エラー内容
docker run
実行時にvue-cli-service
がないと怒られる場合。sh: vue-cli-service: command not found解決方法
vue-cli-service
をインストールすることで解決します。npm install --save @vue/cli-serviceエラー(その2)
エラー内容
docker run
実行時にscript: serve
がないと怒られる場合。npm ERR! missing script: serve解決方法
package.json
にscripts.serve
を追加することで解決します。package.json{ "scripts": { "serve": "node_modules/.bin/vue-cli-service serve" } }エラー(その3)
エラー内容
docker run
実行時にcompilation.templatesPlugin...
のコンパイルエラーが起きる場合。ERROR Failed to compile with 1 errors TypeError: compilation.templatesPlugin is not a function解決方法
Webpack を追加することで解決します。
npm add webpack@latestエラー(その4)
エラー内容
docker-compose up
実行時にコンテナ名が被っていると怒られる場合。ERROR: for web Cannot create container for service web: Conflict. The container name "/web" is already in use by container "f4acb3dbb5". You have to remove (or rename) that container to be able to reuse that name.解決方法
名前が重複しているコンテナを取り除くことで解決します。
docker rm <CONTAINER_ID>
- 投稿日:2019-11-28T16:24:04+09:00
vue+気象情報APIでお天気appを作ってみた
自分に対する備忘録という目的で記事を投稿させていただきます。
(試験的に限定共有ではなく本投稿させていただきます)vueバージョン:vue2.5.16
使用するAPI:OpenWeatherMap初期状態
index.htmlとmain.jsを用意
またOpenWeatherMapを用いるにはAPIキーが必要になるためあらかじめ登録を済ませてAPIキーを参照できるようにすることindex.html
<html lang="ja"> <head> <meta charset="utf-8"> <title>WeatherApp</title> </head> <body> <div id="app"> {{ message }} </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="main.js"></script> </body> </html>main.js
var app = new Vue({ el: '#app', data: { message: "Hello Vue.js!" }, })htmlファイルを開き、"Hello Vue.js!"と表示されればひとまずOK
APIからデータを取得する
mounted:...でページが読み込まれた時の記述を追加する
main.js
var app = new Vue({ el: '#app', data: { message: "Hello Vue.js!" }, + mounted: function(){ + axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${"Tokyo"},jp&units=metric&appid=APIキー`) + .then(function(response){ + console.log(response) + }) + .catch(function(error){ + console.log(error) + }) + }, })axios.getのところでAPIを叩く
q=Tokyo,q=Osakaのように書くことで気象情報を取り出す場所を指定することができる。
今回は後から変数を用いるので変数展開で書く。
"appid="の後にAPIキーを記述すること。htmlファイルを開きconsole画面を見ると現在の気象情報データをとってこれているはず。
data/name:場所
data/weather[0]/main:現在の天気
が格納されているのでこれらを画面に表示するようにする天気を表示させる
main.js
var app = new Vue({ el: '#app', data: { + city: null, + condition:null, }, mounted: function(){ axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${"Tokyo"},jp&units=metric&appid=APIキー`) .then(function(response){ + this.city = response.data.name + this.condition = response.data.weather[0].main + }.bind(this)) .catch(function(error){ console.log(error) }) }, })index.html
<html lang="ja"> <head> <meta charset="utf-8"> <title>WeatherApp</title> </head> <body> <div id="app"> + <div class="weather_info"> + <p class="weather_city">{{ city }}</p> + <p class="weather_condition">{{ condition }}</p> + </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="main.js"></script> </body> </html>これでindex.htmlを開くと場所名(Tokyo)と現在の天気が表示されている筈。
さらにここから現在の気温を表示させてみる。
(気温は先ほどのconsole画面の中のdata/main/tempに格納されている)main.js
var app = new Vue({ el: '#app', data: { city: null, + temp: null, condition:null, }, mounted: function(){ axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${"Tokyo"},jp&units=metric&appid=APIキー`) .then(function(response){ this.city = response.data.name this.condition = response.data.weather[0].main + this.temp = response.data.main.temp }.bind(this)) .catch(function(error){ console.log(error) }) }, + filters: { + roundUp(value){ + return Math.ceil(value) + } + } })index.html
<html lang="ja"> <head> <meta charset="utf-8"> <title>WeatherApp</title> </head> <body> <div id="app"> <div class="weather_info"> <p class="weather_city">{{ city }}</p> <p class="weather_condition">{{ condition }}</p> + <p class="weather_temp">{{ temp | roundUp }}°C</p> </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="main.js"></script> </body> </html>気温は少数も含まれているので
今回はfiltersオプションを用いて小数点切り上げして出力するようにしている。
これでindex.htmlを開くと以下のように表示される気象データの場所を選択できるようにする
今の状態だとTokyoの気象情報しか持ってこれないので
場所を選択できるようにしてみる。main.js
var app = new Vue({ el: '#app', data: { city: null, temp: null, condition:null, + selectedCity: '', }, + methods:{ + select: function() { axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${this.selectedCity},jp&units=metric&appid=APIキー`) .then(function(response){ this.city = response.data.name this.temp = response.data.main.temp this.condition = response.data.weather[0].main }.bind(this)) .catch(function(error){ console.log(error) }) } }, filters: { roundUp(value){ return Math.ceil(value) } } })index.html
<html lang="ja"> <head> <meta charset="utf-8"> <title>WeatherApp</title> </head> <body> <div id="app"> <div class="weather_info"> <p class="weather_city">{{ city }}</p> <p class="weather_condition">{{ condition }}</p> <p class="weather_temp">{{ temp | roundUp }}°C</p> </div> + <select v-model="selectedCity" v-on:change="select"> + <option disabled value="">Please select one</option> + <option>Tokyo</option> + <option>Osaka</option> + <option>Yokohama</option> + <option>Sapporo</option> + </select> + <span>Selected: {{ selectedCity }}</span> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="main.js"></script> </body> </html>v-model="selectedCity"
でselectedCityの値を双方向データバインディングさせる。
axios.getでselectedCityを指定しているので、このselectedCityの値が気象情報を取得する場所となる。
v-on:change="select"と書かれている通り、選択ボックスをクリックするとAPIを叩くようになっている。index.htmlを開くとこのように動く筈。
- 投稿日:2019-11-28T16:24:04+09:00
vue+気象情報APIで簡易お天気appを作ってみた
自分に対する備忘録という目的で記事を投稿させていただきます。
(試験的に限定共有ではなく本投稿させていただきます)vueバージョン:vue2.5.16
使用するAPI:OpenWeatherMap参考文献
この記事は以下の情報を参考にして執筆しました。
https://www.tam-tam.co.jp/tipsnote/html_css/post16405.html初期状態
index.htmlとmain.jsを用意
またOpenWeatherMapを用いるにはAPIキーが必要になるためあらかじめ登録を済ませてAPIキーを参照できるようにすることindex.html
<html lang="ja"> <head> <meta charset="utf-8"> <title>WeatherApp</title> </head> <body> <div id="app"> {{ message }} </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="main.js"></script> </body> </html>main.js
var app = new Vue({ el: '#app', data: { message: "Hello Vue.js!" }, })htmlファイルを開き、"Hello Vue.js!"と表示されればひとまずOK
APIからデータを取得する
mounted:...でページが読み込まれた時の記述を追加する
main.js
var app = new Vue({ el: '#app', data: { message: "Hello Vue.js!" }, + mounted: function(){ + axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${"Tokyo"},jp&units=metric&appid=APIキー`) + .then(function(response){ + console.log(response) + }) + .catch(function(error){ + console.log(error) + }) + }, })axios.getのところでAPIを叩く
q=Tokyo,q=Osakaのように書くことで気象情報を取り出す場所を指定することができる。
今回は後から変数を用いるので変数展開で書く。"appid="の後にAPIキーを記述すること。
htmlファイルを開きconsole画面を見ると現在の気象情報データをとってこれているはず。
data/name:場所
data/weather[0]/main:現在の天気
が格納されているのでこれらを画面に表示するようにする天気を表示させる
main.js
var app = new Vue({ el: '#app', data: { + city: null, + condition:null, }, mounted: function(){ axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${"Tokyo"},jp&units=metric&appid=APIキー`) .then(function(response){ + this.city = response.data.name + this.condition = response.data.weather[0].main + }.bind(this)) .catch(function(error){ console.log(error) }) }, })index.html
<html lang="ja"> <head> <meta charset="utf-8"> <title>WeatherApp</title> </head> <body> <div id="app"> + <div class="weather_info"> + <p class="weather_city">{{ city }}</p> + <p class="weather_condition">{{ condition }}</p> + </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="main.js"></script> </body> </html>これでindex.htmlを開くと場所名(Tokyo)と現在の天気が表示されている筈。
さらにここから現在の気温を表示させてみる。
(気温は先ほどのconsole画面の中のdata/main/tempに格納されている)main.js
var app = new Vue({ el: '#app', data: { city: null, + temp: null, condition:null, }, mounted: function(){ axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${"Tokyo"},jp&units=metric&appid=APIキー`) .then(function(response){ this.city = response.data.name this.condition = response.data.weather[0].main + this.temp = response.data.main.temp }.bind(this)) .catch(function(error){ console.log(error) }) }, + filters: { + roundUp(value){ + return Math.ceil(value) + } + } })index.html
<html lang="ja"> <head> <meta charset="utf-8"> <title>WeatherApp</title> </head> <body> <div id="app"> <div class="weather_info"> <p class="weather_city">{{ city }}</p> <p class="weather_condition">{{ condition }}</p> + <p class="weather_temp">{{ temp | roundUp }}°C</p> </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="main.js"></script> </body> </html>気温は少数も含まれているので
今回はfiltersオプションを用いて小数点切り上げして出力するようにしている。
これでindex.htmlを開くと以下のように表示される気象データの場所を選択できるようにする
今の状態だとTokyoの気象情報しか持ってこれないので
場所を選択できるようにしてみる。main.js
var app = new Vue({ el: '#app', data: { city: null, temp: null, condition:null, + selectedCity: '', }, + methods:{ + select: function() { axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${this.selectedCity},jp&units=metric&appid=APIキー`) .then(function(response){ this.city = response.data.name this.temp = response.data.main.temp this.condition = response.data.weather[0].main }.bind(this)) .catch(function(error){ console.log(error) }) } }, filters: { roundUp(value){ return Math.ceil(value) } } })index.html
<html lang="ja"> <head> <meta charset="utf-8"> <title>WeatherApp</title> </head> <body> <div id="app"> <div class="weather_info"> <p class="weather_city">{{ city }}</p> <p class="weather_condition">{{ condition }}</p> <p class="weather_temp">{{ temp | roundUp }}°C</p> </div> + <select v-model="selectedCity" v-on:change="select"> + <option disabled value="">Please select one</option> + <option>Tokyo</option> + <option>Osaka</option> + <option>Yokohama</option> + <option>Sapporo</option> + </select> + <span>Selected: {{ selectedCity }}</span> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="main.js"></script> </body> </html>v-model="selectedCity"
でselectedCityの値を双方向データバインディングさせる。
axios.get...の中の変数展開でselectedCityを指定しているので、このselectedCityの値が気象情報を取得する場所となる。
v-on:change="select"と書かれている通り、選択ボックスをクリックするとAPIを叩くようになっている。index.htmlを開くとこのように動く筈。
- 投稿日:2019-11-28T11:41:30+09:00
v-ifで同一のコンポーネントの表示を切り替えた時にcreatedが発火しない問題とその解決
たぶんすごい基礎中の基礎なんだろうけど、結構詰まったので。
どういうことか
例えばこんなコンポーネントがあるとする。
渡したvalueを表示するだけのコンポーネントTestComponent.vue<template> <div> {{ value }} </div> </template> <script> export default { props: { value: { type: String } }, created() { console.log("created"); } }; </script>こんな感じで利用する
Main.vue<template> <div> <button @click="click">Click</button> <div> <testComponent value="a" v-if="testOpen"/> <testComponent value="b" v-else/> </div> </div> </template> <script> import testCompoment from "TestComponent" export default { data() { return { testOpen: false } }, components: { testComponent }, methods: { click() { this.testOpen = !this.testOpen; } } }; </script>クリックすると片方のコンポーネントが表示され、もう片方が消え、画面表示のa,bが切り替わる画面
予測していた動作
クリックをするたびにcreatedが呼ばれ、画面表示のa,bが切り替わる。
実際の動作
createdが最初しか呼ばれない。
See the Pen wvvVJXj by 7tsuno (@7tsuno) on CodePen.
なぜか
Vue は要素を可能な限り効率的に描画しようとする。
ここでは二つのtestComponent
要素が同じものだとされ、初期化処理が走らないようになっている。解決策
コンポーネントに別々のkeyをつける。
keyをつけることでvueに"この2つの要素は完全に別個のもので、再利用しないでください"と伝えることが出来る。Main.vue<template> <div> <button @click="click">Click</button> <div> <testComponent value="a" v-if="testOpen" key="testA"/> <testComponent value="b" v-else key="testB"/> </div> </div> </template> <script> import testCompoment from "TestComponent" export default { data() { return { testOpen: false } }, components: { testComponent }, methods: { click() { this.testOpen = !this.testOpen; } } }; </script>参考
- 投稿日:2019-11-28T10:47:20+09:00
Vue.jsについて学んだことまとめ
目次
- Vue.jsとは
- Vue.jsの基本
今回の学習のゴール
- Vue.jsとは何か、何ができるかを知る
- 実際の基本的な使い方について知る
1. Vue.jsとは
Vue (発音は / v j u ː / 、 view と同様)はユーザーインターフェイスを構築するためのプログレッシブフレームワークです。他の一枚板(モノリシック: monolithic)なフレームワークとは異なり、Vue は少しずつ適用していけるように設計されています。
Vue.js
それぞれの意味
- ユーザーインターフェイス(UI): コンピュータとユーザーがそれぞれ情報をやり取りする際の入力や表示方法などの仕組み
- ex. GUI, CUI
- Vue.jsはUIを構造化し、UIを個別にコンポーネント化できるため、システム全体をコンポーネントの集合として開発できる
- プログレッシブフレームワーク : Vue.js作者のEvan You氏が提唱する概念
- サポートするツールやライブラリによって、どんな規模でもどの段階のアプリケーションでも柔軟に対応できる
特徴
- View層にフォーカスしたJavaScriptライブラリ
- View層 : レスポンスをクライアントにとって都合のいい画面に変換する層(実際のユーザーの目に見える領域)
- 再利用可能な部品(コンポーネント)として様々な機能を実装できる
- HTMLをベースとするテンプレート構文を利用することで様々な表示をHTMLの中に組み込むことができる
- DOM要素とJavaScriptのデータを結びつけるリアクティブなデータバインディング
- リアクティブなデータバインディングとは、HTMLテンプレート内で対象となるDOM要素にバインディングを指定することで、Vue.jsがそのデータの変更を検出する度に、バインディングされているDOM要素が自動で表示内容を更新すること
- DOM : Document Object Modelの略称で、プログラムからHTMLを自由に操作するための仕組み
- リアクティブ : 再度アクティブになるという意味
- データが更新されると自動的にそれらのデータを利用している画面の表示も自動的に更新されるので、データを操作するだけで表示を更新できる
- バインディング : 複数の事柄どうしを結びつけたり対応づけたりすること
- Vue.jsは基本的にJavaScriptのスクリプトを使って、HTMLのタグを操作するものと考えて良い
2. Vue.jsの基本
Vueの導入
- HTMLファイルの
<head>
タグ内に以下を記述html// 開発バージョン <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> // 本番バージョン <script src="https://cdn.jsdelivr.net/npm/vue"></script>プロジェクトとしての開発
- プロジェクトの作成
- プログラムの作成に必要なスクリプトファイルやイメージなどのファイル、各種のライブラリやフレームワークなどをまとめて管理するためのもの
$ vue create vue-app # vue-appというプロジェクトを作成
- プロジェクトを実行
$ npm run serve # localhostに「Welcome to Your Vue.js APP」というデフォルトのページが表示される
プロジェクトの実行によって生成されたvue-appフォルダの中身はざっと以下の通り
- フォルダ
- .git : Gitというツールで使用するフォルダ
- node_modules : npmで管理されるモジュール類がまとめられたフォルダ
- public : HTMLやCSSなど公開されるファイル類が保管されたフォルダ
- src : Vue.jsで作成したファイルなどがまとめられたフォルダ
- ファイル
- .eslintrc.js : ESLintで使うファイル
- .gitignore : Gitというツールで使用するファイル
- package.json : npmでパッケージ管理するための設定ファイル
- postcss.config.js : PostCSSというツールの設定ファイル
- READM .md
作成したプロジェクトをWebサーバーにアップして公開する
$ npm run build # distというフォルダが作成される
- ビルドによって生成されたdistフォルダの中身はざっと以下の通り
- フォルダ
- CSS
- js
- img
- ファイル
- favicon.ico
- index.html : デフォルトで表示されるHTMLファイル
宣言的レンダリング
- 単純なテンプレートにレンダリングする対象を宣言的に記載することでデータを変更するたびに、リアクティブなDOMレンダリングと、ユーザによる入力データの同期が可能である
- レンダリング : 画面に表示すべき内容を、与えられたデータから計算しながら描き出すこと
- 宣言的(Declarative) : 何がしたいのかという目的を宣言すること(処理の仕方はコンピュータに任せる)
- 反対語である命令的(Imperative)は、どのようにしたいのかという方法を細かく指示すること
→ 要は、画面に表示したい内容を以下のようにテンプレートに当てはめるだけで、簡単に描画することができるってことが言いたいのだろう
html<div id="app"> <!-- IDを使って操作の対象となるタグを指定する --> {{ message }} <!-- mustache構文 HTMLの中に値を埋め込むためのもの --> </div>jsvar app = new Vue({ // Vueというオブジェクトを作成し変数appに代入 // オプションを記載 使用するデータやメソッドを定義する el: '#app', // アプリケーションを紐付ける要素(element) DOM要素を示すセレクタを指定する data: { // アプリケーションで使用するデータ dataに用意された値が対象となるタグ内の{{}}に置き換えられる message: 'Hello Vue!' // この値に応じて描画が更新される } })表示内容Hello Vue!renderプロパティを用いたレンダリング
- renderは、Vueに用意されているレンダリングを行うためのプロパティ
- 上記の例はHTMLに用意された{{}}に値をはめ込んでいるだけだが、renderを使用すると表示内容そのものを作成することができる
- 以下の例では、renderが
<p>・・・・・data.message・・・・・</p>
のようなタグそのものを作成しているhtml<body> <h1>Vue.js</h1> <div id="app"> {{ message }} </div> <hr> <button onclick="doAction();"> <!-- onclick属性でbutton要素がクリックされたかをイベントとして判断できる doAction()関数でアクションを実行--> click </button> </body>jsvar data = { message: 'Hello Vue!' }; var app = new vue ({ el: '#app', data: data, // dataプロパティの値には変数dataを指定 render: (element) => { // renderプロパティで表示内容を作成 elで設定したタグの部分にレンダリングしたものをはめ込むことができる アロー関数の引数elementには関数が渡されている return element("p", // returnで関数elementの実行結果を返す 引数には追加するタグ名とその属性、追加するタグに表示する値を指定 {'style':'font-size:20pt; coler:red;'}, // 第2引数の属性は省略可 data.message); } }) function doAction() { // doAction()関数が呼び出されると data.message = "You Clicked!!!"; // 変数dataのmessageが上書きされる }ページをリロードしたときの表示内容Hello Vue!→ renderで組み込んだpタグの内容(=変数dataに用意したmessageのテキスト)が、
font-size
は20pt、coler
はredで表示されるclickボタンを押下したときの表示内容You Clicked!!!→ リアクティブ機能が働いていることがわかる
※ returnの戻り値は
VNode
というオブジェクト
VNode
は関数returnで生成されるもので、仮想DOMとして扱われるオブジェクトのこと
- DOMとはData Object Modelの略で、HTMLなどの1つ1つの要素(タグ)をプログラミング言語で扱えるようにするために用意された仕組み
- JavaScriptではHTMLを読み込んだ段階で、各ダグの要素が作成され、それらの組み込み状態などがHTMLと全く同じ形で再現される(組み込まれて構造的に作られた構造をDOMツリーという)
- Vue.jsではDOMツリーが構築された後、それぞれの要素に対する仮想DOMの要素が作られ、仮想DOMのDOMツリーが作成される
- 仮想DOMの値が変更されれば、常にDOMの値も更新されるような対応がなされており、HTMLの表示タグも変更される
- DOMツリーは状態を常にチェックできないが(状態を常にチェックしたい場合はその処理を記述する必要がある)、仮想DOMは常に状態が管理され、変更があるたびに実際のDOMに変更内容が反映される
- 表示内容を複数のタグを組み合わせて表示させたい場合は、次のように子ノードを組み込む
return 関数 ( タグ名, { 属性 }, [ VNode, VNode, ・・・・・] )
例var app = new vue ({ el: '#app', data: data, render: (element) => { return element("ol", // <li>タグを<ol>タグの子ノードとして組み込む // element関数を使って<li>タグのVNodeのオブジェクトを作成 配列に値をまとめる [ element("li", "item 1"), element("li", "item 2"), element("li", "item 3") ]); } })表示内容1. item 1 2. item 2 3. item 3要素の属性をバインディング
- タグのコンテンツとしてテキストを表示させる場合は{{}}記号を使用したが、タグの属性の値に変数などを設定したい場合はv-bind:を使う
html<h1>Vue.js</h1> <div id="app"> <p v-bind:style="style">{{ message }}</p> <!-- v-bind:style属性にstyle変数の中身が設定される --> </div> <hr> <input type="number" onChange="doChange(event);" value="20"> <!-- onChange属性の値に入力フォームの内容が変更された場合の処理を記述 -->jsvar data = { message: 'Hello Vue!' style: 'font-size:20pt; color:red;' }; var app = new vue ({ el: '#app', data: data }); function doCahnge(event) { data.style = 'font-size:' + event.target.value + 'pt; color:red;'; // event.target.valueでイベントが発生した対象(入力フォーム)の値を取り出す style属性の値が変更される }→ inputタグに入力されたnumberに合わせてfont-sizeが変更する
条件分岐とループ
- v-if ディレクティブは、条件に応じて描画したい場合に使用され、ディレクティブの式が真を返す場合のみ描画される
html<div id="app"> <span v-if="seen">Now you see me</span> <!-- 値には真偽値として扱える変数などの条件を記述 trueであれば表示内容が表示される--> </div>jsvar app = new Vue({ el: '#app', data: { seen: true } })表示内容Now you see me
- v-forディレクティブは、リストの項目を配列内のデータを使って表示させることができる
html<div id="app"> <ol> <li v-for="todo in todos"> <!-- "変数 in 配列"と記述し、配列の値を順に取り出し変数に入れてダグを出力する --> {{ todo.text }} </li> </ol> </div>jsvar app = new Vue({ el: '#app', data: { todos: [ { text: 'Learn JavaScript' }, { text: 'Learn Vue' }, { text: 'Build something awesome' } ] } })表示内容1. Learn JavaScript 2. Learn Vue 3. Build something awesomeユーザー入力の制御
- v-onディレクティブを使ってイベントリスナを加え、Vueインスタンスのメソッドを呼び出す
- イベントリスナ : イベントに対して処理を結びつける仕組み
html<div id="app"> <p>{{ message }}</p> <button v-on:click="reverseMessage">Reverse Message</button> <!-- v-on:クリック名 = "処理(今回は関数の名前)"と記述する --> </div>jsvar app = new Vue({ el: '#app', data: { message: 'Hello Vue.js!' }, methods: { // v-onの値に記述した関数の名前はmethodsプロパティに用意する reverseMessage: function () { this.message = this.message.split('').reverse().join('') } } })ボタンを押した時の表示内容!sj.euV olleH
- v-modelディレクティブを使って入力とアプリケーションの状態の「双方向バインディング」を行う
- inputタグに入力された値をVueのdataプロパティの値にバインドする機能である
- リアルタイムにバインドされた変数の値が更新されるので、下記の例ではpタグで表示される内容とinputタグに入力されている内容がリアルタイムで同等の値になる
html<div id="app"> <p>{{ message }}</p> <input v-model="message"> <!-- 変数messageにバインド --> </div>jsvar app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } })コンポーネントシステム
- 自己完結的で、再利用可能である小さい単位のコンポーネントを組み合わせることによって、大規模のアプリケーションを構築できる
- 以下の例は変数をコンポーネントに渡す処理
html<h1>Vue.js</h1> <div id="app"> <hello/> <!-- helloコンポーネントを使用 --> </div>jsVue.component('hello', // helloコンポーネントを定義 { data:function() { // コンポーネントのdataプロパティに変数の値を関数として用意する必要がある return { message: 'New message' // 変数の情報 }; }, template: '<p class="hello">{{message}}</p>', // templateに設定した内容がコンポーネントとして表示される dataで設定したmessageの値が埋め込まれている }); var app = new Vue({ // コンポーネント用のタグをレンダリングするためにVueオブジェクトの処理も行う el: '#app', });参照
- 投稿日:2019-11-28T01:07:59+09:00
超初心者でも出来るVueを使ったカレンダー作成
はじめに
フツーの会社員として働きながら、プログラミンを学び始めて2か月が経過しました。少しづつコード
は勉強していますが、まだまだやってみたことと実力の差が正直大きいです。そんな時、WEBフロントエンド用のフレームワークであるVue.jsを知ったので、試してみることにしました。Vueとは?(超初心者の備忘録的に記載してます)
フロントエンド(front end)とはWebサービスやWebアプリケーションなどでユーザーがボタンを押したり入力をしたりする部分、またソフトウェアなどと直接やり取りをする部分のことを指します。
ユーザーがWebサービスなどを利用する時に目に見える表の部分はフロントエンドの部分です。(参考)
Vue.jsはフロントエンド開発支援のためのツールで、「こんな感じのパーツが欲しいなあ」と思うものに対し簡単に作れるように準備されています。ライブラリをインストールしてコードをコピーし、自分がカスタマイズしたい部分だけ変更すれば、あら不思議、作りたいものが簡単にできちゃいます(部分的だけど)。
今までコツコツ手計算していた難しい計算問題を一気にエクセルが解決してくれるくらいの有難さですね。
もうちょっとちゃんとした説明は下記のサイトにあります。
https://techacademy.jp/magazine/21456
https://jp.vuejs.org/v2/guide/仕様
年末ということもあり、今の実力では作れないカレンダーをに挑戦しました。
・2か月分表示される
・カレンダーから日付をピックすることもできる。環境
Node.js v10.16.3
Windows 10 pro
Visual Studio Code v1.39.1
Vue.js方法
「Vue.jsのカレンダライブラリv-calendarを使って、日付ごとのデータをカレンダー上に表示」も参考にさせていただきました。
もっとスマートなやり方があるとは思いますが、初心者が再現しやすいように書いておきます。1.ターミナルを起動し、npm init -yでpackage.jasonをインストール
2.次に同じくターミナルでnpm install v-calenderでVue.jsのカレンダーライブラリーをインストール
3. index.jsファイルを作成(下記index.jsコード参照)
4. main.jsファイルを作成(下記main.jsコードを参照)
5. publicフォルダを作成し、中にindex.html作成
6. heroku化のために.gitignore というファイル名でindex.jsなどと同じ場所に保存します(コードは一番下のコードを参照)。
7. Heroku化してサーバーにアップする。コード
index.jsvar express = require('express'); var app = express(); // public というフォルダに入れられた静的ファイルはそのまま表示 app.use(express.static(__dirname + '/public')); // bodyParser var bodyParser = require('body-parser'); app.use(bodyParser.json()); app.post('/post', function(req, res) { for (key in req.body) { console.log(key, '=', req.body[key]); } res.end(); }); // app.listen(8080); app.listen(process.env.PORT || 8008); console.log("server start! (heroku)");main.jsimport Vue from 'vue' import App from './App.vue' import VCalendar from 'v-calendar' Vue.use(VCalendar) Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app') ```index.html <html> <style> </style> <head> //ブラウザーのタグの所の名前 <title>Hello Server!</title> <script src="https://unpkg.com/vue"></script> <meta charset='utf-8'> <meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no'> <meta http-equiv='x-ua-compatible' content='ie=edge'> <!-- IMPORTANT: No CSS link needed as of v1 Beta (@next) - It's all inlined --> <!-- Pre v1.0.0 versions need the minified css --> <!-- <link rel='stylesheet' href='https://unpkg.com/v-calendar/lib/v-calendar.min.css'> --> </head> </head> <body> //カレンダーの上の文言 <div>Calender</div> <div id='app'> <v-calendar :columns="$screens({ default: 1, lg: 2 })" /></v-calendar> <v-date-picker :mode='mode' v-model='selectedDate' /> </div> <!-- 1. Link Vue Javascript --> <script src='https://unpkg.com/vue/dist/vue.js'></script> <!-- 2. Link VCalendar Javascript (Plugin automatically installed) --> <!-- @next v1 Beta --> <script src='https://unpkg.com/v-calendar@next'></script> <!-- Latest stable (Right now, this is very different from the v1 Beta)--> <!-- <script src='https://unpkg.com/v-calendar'></script> --> <!-- Hardcoded version --> <!-- <script src='https://unpkg.com/v-calendar@1.0.0-beta.14/lib/v-calendar.umd.min.js'></script> --> <!--3. Create the Vue instance--> <script> new Vue({ el: '#app', data: { // Data used by the date picker。datePickerは複数選択できる mode: 'multiple', selectedDate: null, } }) </script> <template> <vc-calendar :attributes='attributes5' style="width:500px;"> <template slot='header-title' slot-scope='page'> {{page.yearLabel}}年{{page.monthLabel}} </template> <template slot='day-content' slot-scope='props'> <div class="vc-day-content"> <div v-bind:style="addStyleTextColor(props.day.weekday)"> {{ props.day.day }}</div> </div> <div v-if="props.day.day % 2 ==0" style="text-align:center;"> <img src="../assets/gatsby.svg"> </div> <div v-else style="text-align:center"> <img src="../assets/slack.svg"> </div> </template> </vc-calendar> </template> </body> </html># Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # TypeScript v1 declaration files typings/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env # next.js build output .next出力結果
2か月分のカレンダーを表示し、選択した日付が表示されています。
感想
自分ではどうやってコードを書いてカレンダーを作成できるのかわからない・・・。だけど「出来ちゃうんだあ~」。勉強不足を棚に上げてうれしいですね。無料でこんなツールを提供いただけるなんて、プログラミング業界はなんて心が広いのでしょうか。
ただ今回は、ほぼカレンダーが表示されているだけなので、今後はもう少しレベルアップして予定をインプットできるようにもしていきたいです。