- 投稿日:2020-10-26T23:25:57+09:00
アニメ情報のWebアプリ開発してみたけど上手くいかず、、(要改善)
出来たもの
「検索」で、指定した年度・クールごとのアニメ番組一覧を表示してくれるWebアプリ。
※実際に動くものはコチラにアップしてます。
利用ライブラリ
・Vue.js
CDN:https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js
・Bootstrap
CDN:https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js
CDN:https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/js/bootstrap.min.js
・axios
CDN:https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.0/axios.min.js
・ShangriLa Anime APIやりたかったこと
新アニメが始まった時にとりあえず1話見てみて、減らしていくことが多い。(1話すら見ないこともあるが。)
始まってすぐだと、タイトルだけだと分からないことが多いので、自分用で優先順位を管理できるようなWebアプリを作ってみたかった。
・年度、クールでアニメ番組を取得して取捨選択。
・優先順付けて並び変え。または削除。
みたいなことをやりたかった。出来なかったこと
四苦八苦の様子、参考にしたURLなどは自分用メモとして保管してます。
Webサイトのサムネイル画像を取得して表示したかったが、上手くいかず。。
色々調べて試してみて、最終的にはWebページのOGPイメージ(og:image)を取得すれば良い考えにシフトしたが時間が足りなくイメージ画像の取得が上手くいってません。改善点(今後に期待)
今回時間なくて断念したが、色々改造したい。
・ちゃんとサムネイル画像を表示できるようにしたい。
・ビジュアルをもっとかっこよくしたい。
・番組の曜日、時間、PV再生など追加したい。
・検索結果の保存。
・カードの並び変えや削除。コード
html<!-- 全体をVue.js有効にする --> <div class="container text-center text-white bg-dark" id="app"> <!-- タイトル行 --> <div class="row my-3"> <div class="col-sm-6 mx-auto"><h1>ANIME Watch</h1></div> </div> <!-- 検索フォーム --> <div class="form-inline justify-content-center"> <input v-model:value="Year" placeholder="2020" class="form-control"> <label class="col-sm-1">年</label> <select v-model:value="Cours" class="custom-select"> <option>春</option> <option>夏</option> <option>秋</option> <option>冬</option> </select> <div class="col-sm-2"> <button v-on:click="Search" class="btn btn-primary">検索</button> </div> </div> <!-- 全てのタスクをクリアするボタン --> <div class="row my-3"> <div class="col-sm-6 mx-auto"> <button v-on:click="clearAll" class="btn btn-danger">検索結果のクリア</button> </div> </div> <!-- タスク追加されると表示される部分 --> <div class="flex-container"> <p v-for="Anime in AniData">{{Anime.Title}}<br> <a v-bind:href="Anime.WebUrl" target="_blank"> <img v-bind:src="Anime.ImgUrl" width="250" height="150"></img> </a> </p> </div> </div>jsconst app = new Vue({ el: '#app', // Vueが管理する一番外側のDOM要素 data: { // Vue内部で使いたい変数は全てこの中に定義する Year: '2020', // 年 Cours: '冬', // クール Animes: '', // APIで取得したアニメ情報JSON AniData: [], // 表示するアニメ情報配列 }, methods: { // 関数はココに記述 Search: async function() { // 検索時の関数 try { // クール値を取得 let CoursNo=4; switch (this.Cours){ case '春': CoursNo=1; break; case '夏': CoursNo=2; break; case '秋': CoursNo=3; break; case '冬': CoursNo=4; break; } // ShangriLa Anime API Server let response; response = await axios.get(`https://api.moemoe.tokyo/anime/v1/master/${this.Year}/${CoursNo}`); this.Animes = response.data; // アニメ情報表示用配列作成 this.AniData = []; for (var item in this.Animes) { var data = {Title: this.Animes[item].title, WebUrl: this.Animes[item].public_url, ImgUrl: ""}; this.AniData.push(data); } /* // OGP取得 上手くいかないからあきらめ。 const CORS_PROXY = 'https://cors-anywhere.herokuapp.com/' for await (var item of this.Animes) { await fetch(CORS_PROXY + this.Animes[item].public_url) .then((res) => res.text()) .then((text) => { const el = new DOMParser().parseFromString(text, 'text/html') const headEls = el.head.children Array.from(headEls).map((v) => { //console.log(v) const prop = v.getAttribute('property') if (!prop) return if (prop == "og:image") { //console.log(prop, v.getAttribute('content')) var ogimg = v.getAttribute('content') } }) var data = {Title: this.Animes[item].title, WebUrl: this.Animes[item].public_url, ImgUrl: ogimg}; this.AniData.push(data); }) }*/ } catch (error) { console.error(error); } }, clearAll: function() { // 表示クリア時の関数 this.AniData = []; console.log('クリア'); }, }, });css.flex-container { display: -webkit-flex; display: flex; -webkit-justify-content: center; justify-content: center; -webkit-align-items: center; align-items: center; flex-wrap: wrap; }感想
Web開発は初でしたが、色々勉強にはなったかな。。
経験積んで、技術力を上げたいと思う。(Web開発は非常に興味あるので。)
- 投稿日:2020-10-26T23:20:19+09:00
vueのクリックイベントで、子のイベントを親に渡す方法。
Vueのクリックイベントで子のイベントを親に引き継ぐ方法。
(子テンプレートのボタンをクリックしたときに、親テンプレートで定義しているメソッドを実行する方法)
設定手順
子:①クリックイベントを設定 @click=$emit('イベント名') ↓ 親:②子のイベントを受け取る (子のタグ内で設定したイベント名) ↓ 親:③methodsに処理を記述・子から親にイベントを引き継ぐ。
・$emit
子から親へのデータ/イベント受け渡し
・@click
v-on:click の省略形
記述例
・用意するファイルは2つ
親:parent.vue
子:TmpBtn.vue・実現したいこと
親で呼び出した小テンプレートのボタンをクリックしたときに、親テンプレートで設定してあるメソッドを実行する。(子)TmpBtn.vue<template> <v-btn elevation="2" rounded width="20%" //①クリックイベントを設定 @click="$emit('child-click')" > ボタン </v-btn> </template>子テンプレートのボタンがクリックされると、child-clickというイベントを親に渡す。
(親)parent.vue<template> <v-app> <TmpBtn <!--②子のイベントを受け取る--> @child-click="BtnClicked(hello)" /> </v-app> </template> <script> import TmpBtn from "./TmpBtn" export default { components:{ TmpBtn }, data(){ return{ hello:"親テンプレートのメソッドを実行" } }, methods:{ //③methodsに処理を記述 BtnClicked(a){ console.log(a) } } } </script>子テンプレートのボタンがクリックされたら、BtnClickedメソッドを実行する。
子テンプレートのボタンクリックで、親テンプレートで設定したイベントの発火に成功。
$emitしないとどうなるか
$emitしない場合、イベント設定は2種類考えられる。
- 子テンプレートにclickイベントとメソッドを記述 → OK
- 親テンプレートにclickイベントとメソッドを記述 → NG
上記の場合、正常に作動するのは1のみ。
2の親テンプレートで呼び出した子テンプレートタグにclickイベントを設定しても動作しない。
子テンプレートに記述する場合
<template> <v-btn elevation="2" rounded width="20%" @click="ChildClicked(child)" > ボタン </v-btn> </template> <script> export default { methods:{ ChildClicked(a){ console.log(a) } }, data(){ return{ child:"子テンプレートのボタンがクリックされました" } } } </script>正常に作動。
親テンプレートに記述する場合
(親)parent.vue<template> <v-app> <TmpBtn :bind="text" <!--子テンプレートを呼び出し、クリックイベントを設定--> @click="BtnClicked(hello)" /> </v-app> </template> <script> import TmpBtn from "./TmpBtn" export default { components:{ TmpBtn }, data(){ return{ hello:"親テンプレートのメソッドを実行" } }, methods:{ BtnClicked(a){ console.log(a) } } } </script>反応なし。。
親テンプレートに記述したメソッドを実行する場合は下記2パターンで検討する必要がある。
①子からイベントを引き継ぐ
②親テンプレート内にボタンを作成したクリックイベントを設定する
イベント情報を渡す ($event)
変数$eventを使うことで、クリックした画面上の位置情報などを渡すことができる。
(子)TmpBtn.vue<template> <v-btn elevation="2" rounded width="20%" <!--clickイベントの引数で$eventを渡す--> @click="$emit('chiled-clicked', $event)" > ボタン </v-btn> </template>(親)parent.vue<template> <v-app> <TmpBtn <!--子テンプレのイベントを受け取り、メソッドを実行--> @chiled-clicked="BtnClicked(hello, $event)" /> <br> </v-app> </template> <script> import TmpBtn from "./TmpBtn" export default { name: "Parent", components:{ TmpBtn }, data(){ return{ hello:"親テンプレートのメソッドを実行" } }, methods:{ //第2引数に$eventを渡す BtnClicked(a, b){ console.log(a) console.log(b) } } }$eventの中身MouseEvent {isTrusted: true, screenX: 77, screenY: 151, clientX: 77, clientY: 17, …} isTrusted: true screenX: 77 screenY: 151 clientX: 77 clientY: 17 ctrlKey: false shiftKey: false altKey: false metaKey: false button: 0 buttons: 0 relatedTarget: null pageX: 77 pageY: 17 x: 77 y: 17 offsetX: 61 offsetY: 10 movementX: 0 movementY: 0 fromElement: null toElement: span.v-btn__content layerX: 61 layerY: 9 view: Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …} detail: 1 sourceCapabilities: InputDeviceCapabilities {firesTouchEvents: false} which: 1 type: "click" target: span.v-btn__content currentTarget: null eventPhase: 0 bubbles: true cancelable: true defaultPrevented: false composed: true timeStamp: 132300.8249999839 srcElement: span.v-btn__content returnValue: true cancelBubble: false path: (8) [span.v-btn__content, button.v-btn.v-btn--rounded.theme--light.elevation-2.v-size--default, div.v-application--wrap, div#app.v-application.v-application--is-ltr.theme--light, body, html, document, Window] __proto__: MouseEvent
$event内の特定のデータが欲しい場合はプロパティを指定する。methods:{ BtnClicked(a, b){ console.log(b.type) } } //出力 //click
- 投稿日:2020-10-26T22:04:37+09:00
娘とあと何日お風呂に入れるか計算するWebアプリ作ったら泣けてきた
娘とあと何回一緒にお風呂入れるだろう?
「もう一緒に入りたくない」と言われる前に自ら娘とのお風呂は卒業したいもの。とはいえ、可能な限り一緒に入りたいというのが、父親の心情。あと何回一緒に入れるだろうかと考えたら、今日の1回も特別に思えるはず。そんな思いから、娘とあと何日一緒にお風呂に入れるか計算するWebアプリを作ってみました。
作ったWebアプリはこちらあと何日一緒にお風呂に入れる?
あとこれしかない。。泣けてきた。。
娘の「パパ風呂」卒業のタイミングは、小学校3~4年生頃が多いようです。
娘の9歳の誕生日を卒業する日と定めると...
2020年10月26日時点で、あと、2608日
2日に一回だとしたら1304日
一週間に一回だとしたら372日...
泣けてきた環境
コーディング
CodePen:Webブラウザ上でHTML/CSS/JavaScriptなど、主にフロントエンド言語のコーディングができるサービス
使用したライブラリ
vue.js
splitting.js:テキストアニメーションが実装できるライブラリソース
index.html<!-- 全体をVue.js有効にする --> <div class="container text-center text-white" id="app"> <!-- タイトル行 --> <div class="row my-3"> <div class="col-sm-15 mx-auto"><div data-splitting><h1>あと何日一緒にお風呂入れる?</h1></div> </div> </div> <img src="https://4.bp.blogspot.com/-YUk507i8b7s/UfIJFj6AMQI/AAAAAAAAWc0/KzQO-AiH6Jg/s800/dakko_papa_girl.png" width="30%"/> <div class="form-group"> <label for="lastday">卒業する日</label><input class="form-control col-sm-6 mx-auto" type="date" id="lastday" onChange="calc();"/> <label for="today">今日の日付</label><input class="form-control col-sm-6 mx-auto" type="date" id="today" onChange="calc();"/> </div> <div class="form-group"> <label for="days">一緒にお風呂に入れる日数</label><input class="form-control col-sm-6 mx-auto" type="text" readonly id="days"/> </div> <div data-splitting><label for="days">卒業のその日まで、有意義に過ごしてほしい。小さい娘を持つ全てのパパへ。</label></div> <br> </div> <!-- 全体ここまで -->style.css@import url(https://fonts.googleapis.com/css?family=Kanit:600); html { height: 100%; display: flex; } body { margin: auto; } html, body { background: #00043C; color: #FFF; font: normal 600 1.5vw/1.5 Kanit, sans-serif; } .splitting .char { animation: slide-in 1s cubic-bezier(.5, 0, .5, 1) both; animation-delay: calc(60ms * var(--char-index)); } @keyframes slide-in { from { transform: translateY(-1em) rotate(-.5turn) scale(0.5); opacity: 0; } }script.jsSplitting(); function calc() { lastday = new Date($('#lastday').val()); today = new Date($('#today').val()); console.log(lastday.getTime()); console.log(today.getTime()); var days = Math.floor((lastday.getTime() - today.getTime()) / (1000 * 60 * 60 *24)); $('#days').val(days); } function today() { $('#today').val(formatdate()); } function formatdate(date) { var toTwoDigits = function (num, digit) { num += '' if (num.length < digit) { num = '0' + num } return num }; date = date ? date : new Date() var year = date.getFullYear() var month = date.getMonth() + 1 var day = date.getDate() var yyyy = toTwoDigits(year, 4) var mm = toTwoDigits(month, 2) var dd = toTwoDigits(day, 2) var ymd = yyyy + "-" + mm + "-" + dd; return ymd; } $(function() { $('#lastday').val(formatdate()); $('#today').val(formatdate()); }());はじめてのWebアプリケーション
プログラミング初心者の私でもCodepenと便利なライブラリを駆使して、割と簡単に実装することができました。今後は音を入れるとか、もう少しリッチな見た目とか、スマホ用に便利な画面とか、作れるようになったらより楽しいだろう。時間を見つけて、色々と試作してみたいです。
おまけ
あまりに早く成長してしまうことが悲しいと思うこともありますが、我が子の成長はやはり嬉しいものです。一緒にお風呂に入れなくなっても、いつか娘と一緒にプレモルが飲めることを楽しみに頑張ります
- 投稿日:2020-10-26T21:07:31+09:00
vueのcss-scopedとcss-moduleの違い
この記事の対象者
vueの基礎を理解している
css-scopedのcss-moduleの共通点
両方ともコンポーネントのスタイルとして定義される
css-scoped
<template lang="html"> <button class="button">ボタン</button> </template><style lang="scss" scoped> .button { // スタイル定義 } </style>ビルドの後のHTMLではクラス名の変化は特になし
<button data-v-015de462="" data-v-fae5bece="" type="button" class="button"> OK </button>開発ツールでのCSSの見た目
クラス名の後ろに[data-v-015de462]が付与されている。.button[data-v-015de462] {}css-module
<template lang="html"> <button :class="$style.button">ボタン</button> // $style.button という指定の仕方になる </template><style lang="scss" module> .button { // スタイル定義 } </style>ビルド後のHTMLを見るとクラス名が変化しています!!
<button data-v-fae5bece="" type="button" class="Button_button_Mvymr"> OK </button>開発ツールでのCSSの見た目
scopedとは違い[data-XXX]は付かずビルド後に変化したクラス名が使われています!!.Button_button_Mvymr {}css-scopedとcss-moduleの異なる点とは
- css-moduleの
$style.button
はscript
内でも使用することが可能。公式ドキュメントから抜粋
<script> export default { created () { console.log(this.$style.red) // -> "_1VyoJ-uZOjlOxP7jWUy19_0" // filename と className に基づいて出力された識別子。 } } </script>
- css-moduleは
::v-deep
では決して上書きが決してできない本来では下記の指定をすると子コンポーネントで
scoped
を指定していても::v-deep
を使用するとスタイルを上書きすることが可能になりますが、css-moduleの場合は決して上書きすることができません
。<style lang="scss" scoped> ::v-deep .button { background-color: green; } </style>最後に
普段からクセで
scoped
を使いがちですが、今回はVSCodeの予測変換からmodule
が表示されていたので気になって調べて見ました。
css-moduleであればtemplate, script, styleから参照し合える
のでscopedより使いやすそうです。
- 投稿日:2020-10-26T19:22:50+09:00
VueRouter 404エラー対策
VueRouterを使ったデモアプリを作成
フロントはVue.js、バック(RESTAPI)はSpringBootのデモアプリを作りました
アプリケーションはjar実行しています(SpringBoot組み込みTomcatを利用)トップページのBLOGボタン押下でブログページを表示します
ブログページはSpringBootで作成したRESTAPIのデータを表示しています
ブログページに表示している記事はRESTAPIで取得しています
ページをリロードしようとすると404エラーが発生
ブログページでリロードしようとすると404エラーで白い画面になってしまいます
この事象はVueRouterのリファレンスでも以下のように説明されています
VueRouterガイド-HTML5 History モードしかしながら一点問題があります。シングルページのクライアントサイドアプリケーションなので、適切なサーバーの設定をしないと、ユーザーがブラウザで直接 http://oursite.com/user/id にアクセスした場合に 404 エラーが発生します。
router.jsでワイルドカードを指定するだけではダメだった
404エラー時に表示したいエラーページを作成して、router.jsにルーティング設定をしただけでは解消されませんでした
ErrorPage.vue
demo\web\src\pages\ErrorPage.vue<template> <v-app> <v-app-bar absolute color="red lighten-1" dark > <v-toolbar-title>エラーが発生しました</v-toolbar-title> </v-app-bar> <v-container class="ml-0 mt-16"> <v-row> <v-col cols="12"> トップページに戻ってください </v-col> </v-row> <v-row> <v-col cols="4"> <v-btn color="blue-grey lighten-4" width="100" v-on:click="clickTopPageBtn" > top page </v-btn> </v-col> </v-row> </v-container> </v-app> </template> <script> export default { name: "ErrorPage", methods: { clickTopPageBtn: function () { this.$router.push("/") } } } </script> <style scoped> </style>
demo\web\src\router.jsimport Vue from "vue" import Router from "vue-router" import TopPage from "@/pages/TopPage"; import BlogPage from "@/pages/BlogPage"; import ErrorPage from "@/pages/ErrorPage" Vue.use(Router) export default new Router({ mode: "history", routes: [ { path: "/", name: "トップページ", component: TopPage }, { path: "/blogPage", name: "ブログ", component: BlogPage }, { path: "*", name: "エラーページ", component: ErrorPage } ] })Apache .htaccessで解決
Windows用のApacheを導入しました(VueRouter公式ガイドにもWebServerを使えと書いてありましたね…)
Apache+SpringBootJar構成でアプリが動くように設定していきますAJP通信の設定
今回のアプリはブログページでVue.jsの画面からSpringBootのAPIを呼び出すので
AJP通信を利用してフロントとバックを繋ぎますApacheのAJP通信設定
Apacheにはビルド済みVue.jsを配置します
8009ポートでAJP通信できるようにhttpd-proxy-ajp.confを作成します\httpd-2.4.46-o111h-x86-vc15\Apache24\conf\extra\httpd-proxy-ajp.conf<Location /blog> ProxyPass ajp://127.0.0.1:8009/blog </Location>httpd.confでもAJP通信ができるよう修正します
\httpd-2.4.46-o111h-x86-vc15\Apache24\conf\httpd.conf# 以下のモジュール読み込み箇所のコメントを解除 LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_ajp_module modules/mod_proxy_ajp.so # 末尾に先ほど作成したhttpd-proxy-ajp.confを読み込む設定 Include conf/extra/httpd-proxy-ajp.confSpringBootのAJP通信設定
SpringBootの組み込みTomcatでApacheとAJP通信する設定クラスを作成します
demo\src\main\java\com\example\demo\config\AppConfig.javapackage com.example.demo.config; import org.apache.catalina.connector.Connector; import org.apache.coyote.ajp.AbstractAjpProtocol; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig { @Bean public TomcatServletWebServerFactory servletContainer() { TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); Connector ajpConnector = new Connector("AJP/1.3"); ajpConnector.setPort(8009); ajpConnector.setSecure(false); ajpConnector.setAllowTrace(false); ajpConnector.setScheme("http"); ((AbstractAjpProtocol) ajpConnector.getProtocolHandler()).setSecretRequired(false); tomcat.addAdditionalTomcatConnectors(ajpConnector); return tomcat; } }.htaccess対応
.htaccessにはリダイレクトの設定を記述します
Apacheのドキュメントルートに配置しただけだと.htaccessが適用されませんhttpd.confの.htaccess適用設定
httpd.confを以下のように変更します
\httpd-2.4.46-o111h-x86-vc15\Apache24\conf\httpd.conf# 以下のモジュール読み込み箇所のコメントを解除 LoadModule rewrite_module modules/mod_rewrite.so # ドキュメントルートのAllowOverride NoneをAllに変更 DocumentRoot "${SRVROOT}/htdocs" <Directory "${SRVROOT}/htdocs"> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory>また、Apacheを配置しているディレクトリにmod_rewrite.soファイルが存在することを確認します
私の環境ではC:\httpd-2.4.46-o111h-x86-vc15\Apache24\modules\mod_rewrite.so
でした.htaccessを作成
VueRouter公式ガイドに記載されている設定内容をそのまま使っています
\httpd-2.4.46-o111h-x86-vc15\Apache24\htdocs\.htaccess<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] </IfModule>このファイルをApacheのドキュメントルートに配置します
私はC:\httpd-2.4.46-o111h-x86-vc15\Apache24\htdocs\
に配置しましたこれでApacheとアプリケーションの設定は完了です
ApacheにVue.jsリソースを配置
いつも通り
npm run build
を実行すればOKですcd C:\Users\risab\git\demo\web npm run buildpackage.jsonで特に指定していなければビルド済みリソースは\dist配下に出力されます
出力されたビルド済みリソースをApacheドキュメントルートに配置します
リソースを配置したら、WindowsのサービスからApacheサービスを起動しておきます
Apacheサービス登録は以下のコマンドでできますcd C:\httpd-2.4.46-o111h-x86-vc15\Apache24\bin httpd -k installSpringBootJarを生成
詳細な手順はこちら gradleでjarを生成します
cd C:\Users\risab\git\demo gradlew bootJarjarが生成されたら以下のコマンドでアプリケーションを起動します
cd C:\Users\risab\git\demo\build\libs java -jar demo-0.0.1-SNAPSHOTこれで対策完了!
Apacheにフロントリソースを配置しているので
http://localhost/
にアクセスします
VueRouterでページ遷移、AJP通信で画面からバックのAPIも呼び出せています
画面のリロード時も404エラーが発生せず、リロードできるようになっています
また、存在しないURLにアクセスした場合は.htaccessとVueRouterのワイルドカードが適用されて
エラーページが表示されるようになりました
- 投稿日:2020-10-26T19:17:38+09:00
v-bindの使い方。親のデータを子のテンプレートに渡す方法。
Vueのv-bindで、親から子へのデータ受け渡しを整理。
以下のようなvueファイルを見たときに、:keyなどタグの中身(任意で設定した属性)が何を表しているのかを理解できるようになる。
<VrBtn :key="key" :disabled="disabled" :command="command" />export default { props: { value: { type: Boolean }, disabled: { type: Boolean, default: false }, command: { type: Object, required: true }, }, }
データ受け渡しの流れ
データ受け渡しの流れは4steps。
属性に変数を入れて、その属性を親と子で受けわたす。親:①子に渡すデータを定義 ↓ 親:②子のテンプレートタグに属性と変数を記述 ↓ 子:③propsで受け取る属性を定義 ↓ 子:④属性を指定してデータ呼び出し▼注意点
・親から渡すデータは変数。
┗ ダブルクオテーションで囲むがテキストではない。
┗:属性名="変数名"
の形で記述。属性を子に渡す。
┗ : は v-bind: の省略形・子のpropsではデータの型などを指定可能
┗ String:文字列、Boolean:真偽値
┗ required: true データの必要性
┗ default: false/true 真偽値のデフォルト設定
データの受け渡し例
・使うファイルは2つ
親:parent.vue
子:TmpBtn.vue (vuetifyでボタンを表示)・実現したいこと
親で定義したデータを子のテンプレート内で呼び出す。(親)parent.vue<template> <v-app> <TmpBtn <!--②子のテンプレートタグに属性と変数を記述--> :bind="text" /> </v-app> </template> <script> import TmpBtn from "./TmpBtn" export default { name: "Parent", components:{ TmpBtn }, data(){ return{ //①子に渡すデータを定義 text:"Text form Parent" } } } </script>
(子)TmpBtn.vue<template> <v-btn width="20%" elevation="2" rounded > <!--④属性を指定してデータ呼び出し--> {{bind}} </v-btn> </template> <script> export default { props:{ //③propsで受け取る属性を定義 bind: {type:String}, } } </script>子のテンプレートを表示したときに、無事、親のデータを受け渡すことができた。
まとめ
以下のような記述を見たら、親から子にデータを渡していることがわかる。
<VrBtn :key="key" :disabled="disabled" :command="command" />propsがあれば、子のテンプレートで親からデータを受け取っているなということがわかる。
export default { props: { value: { type: Boolean }, disabled: { type: Boolean, default: false }, command: { type: Object, required: true }, }, }
- 投稿日:2020-10-26T16:58:09+09:00
画面で入力された注文数をグラフに反映するWebアプリ(仮)
入力された注文数をグラフに反映してみた
今日の注文数を入力したらすぐ反映されますね!
DBとか使ってなくてすぐ表示させてるだけだから!URLはこちらです!→今日までの売上を可視化しよう
使い方
一日の終わりに、今日注文された数を入力
→こんだけ増えたよって可視化される苦労した点
今回は、裏のロジックというより簡単な情報をうまく表示するところを意識しました。
・グリッドの理解をしないままbootstrapを使い始めて、表が右に行ったり、めちゃくちゃ高さが低くなったり、小さくなったり。
それもあって、ちゃんと理解をするためにこちらのサイトで勉強させていただきました。↓
bootstrapの構造について学ぶ・最初Chart.jsを使おうとしていましたが、new Chart.jsをするところで、
Vue.jsから呼び出したかったが、どう記述すればよいのかわからなかったので今回はgstaticを使いました。今回やりたかったけどできなかったこと
・DBを使ってデータの出し入れ
・月を選ぶ。今回のは10月にしか追加されない。。。
・「数値をクリア。」どこのだよ!と思うのを解消する。 今は10月の数値が根こそぎ消える。
・目標の数値の設定や、そことの差の描画
・描画方法の選定 絶対もっときれいでおしゃれなやつあるやろ!環境
エディタ:codepen
CSS:bootstrap
使用ライブラリ
vue.js
loader.js gstaticソース
HTML部分はこちら↓
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <title>CodePen - chart</title> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css'> </head> <body> <!-- partial:index.partial.html --> <head> <script src="https://www.gstatic.com/charts/loader.js"></script> </head> <!-- 全体をVue.js有効にする --> <div class="container text-center text-white bg-dark" id="app"> <!-- タイトル行 --> <div class="row my-3"> <div class="col-sm-6 mx-auto"><h1>一日の終わりに注文数を入力</h1></div> </div> <!-- タスク入力行 --> <div class="row my-3"> <div class="col-sm-6 mx-auto"> <input v-model:value="task" placeholder="注文数を入力" class="form-control"><br> 直前に追加した注文数:{{ previous_num }} <button v-on:click="addTask" class="btn btn-primary">追加</button> </div> </div> <!-- 全てのタスクをクリアするボタン --> <div class="row my-3"> <div class="col-sm-6 mx-auto"> <button v-on:click="clearAll" class="btn btn-danger">数値をクリア</button> </div> </div> <!-- タスク追加されると表示される部分 --> <div class="row my-3"> <div class="col-sm-6 mx-auto"> </div> </div> <div class="col-lg-10 mx-auto" id="stage"></div> </div><!-- 全体ここまで --> <!-- partial --> <script src='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/js/bootstrap.min.js'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/vue-chartjs/3.5.1/vue-chartjs.min.js'></script><script src="./script.js"></script> </body> </html>JS部分はこちら↓
//必要なパッケージの読み込み google.charts.load('current', {packages: ['corechart']}); // google.charts.setOnLoadCallback(drawChart); const app = new Vue({ el: '#app', // Vueが管理する一番外側のDOM要素 data: { // Vue内部で使いたい変数は全てこの中に定義する task: '', todoList: [['月', '数量'], ['1月', 65], ['2月', 59], ['3月', 80], ['4月', 81], ['5月', 56], ['6月', 55], ['7月', 55], ['8月', 55], ['9月', 55], ['10月', 0], ['11月', 0], ['12月', 0]], // これは配列 chart:"", previous_num:0, }, methods: { // 関数はここ addTask: function() { console.log('次のタスクが追加されました:', this.task); // 配列の先頭に現在のタスク内容を追加する(最後尾の場合はpush) this.todoList[10][1] = Number(this.todoList[10][1]) + Number(this.task); console.log('現在のToDo一覧:', this.todoList); this.previous_num = this.task; this.drawChart(); }, // 以下を追加、関数名はなんでもよい clearAll: function() { this.todoList = [['月', '数量'], ['1月', 65], ['2月', 59], ['3月', 80], ['4月', 81], ['5月', 56], ['6月', 55], ['7月', 55], ['8月', 55], ['9月', 55], ['10月', 0], ['11月', 0], ['12月', 0]]; console.log('全てのToDoが消去されました'); this.drawChart(); }, drawChart: function() { console.log('drawChart'); //オプション設定 var options = { 'title': '注文数', }; //月別データ var array = this.todoList; var data = google.visualization.arrayToDataTable(array); var stage = document.getElementById('stage'); //グラフの種類を設定 var chart = new google.visualization.ColumnChart(stage); //データとオプションを設定 chart.draw(data, options); } }, });
- 投稿日:2020-10-26T16:36:36+09:00
Vue.jsをSpringBootアプリに組み込む
Vue.jsプロジェクトをjarに内包して実行
フロントエンドはVue.js、バックエンドはJava(SpringBoot)でアプリを作りたいけど、
Vue.jsとJavaでプロジェクトを分けたくない…というシーンに遭遇したので書いておきますビルド済みVue.jsリソースをjarファイルに内包してSpringBootの組み込みTomcatで実行します
開発環境
- Windows10Pro
- AdoptOpenJDKJ9 1.8.0_265
- npm 6.14.6
- vue-cli 4.5.4
Vue.js+SpringBootプロジェクトを作成
IntelliJでSpringBootプロジェクトを作成します
以下のコマンドで作成したSpringBootプロジェクトの中にVue.jsプロジェクトを作成します
cd C:\Users\risab\git\demo vue create webこれでVue.js+SpringBootプロジェクトができました 画像のwebディレクトリがVue.jsプロジェクトです
アプリケーションを実装する
今回はVue.jsで作成した簡単な画面にSpringBootで作成したRESTAPIのレスポンスを表示するアプリを作成します
RESTAPI
/blog/entries/latest
にアクセスすると最新のブログタイトルと記事内容が返される
RESTAPIコントローラーを作成しましたdemo\src\main\java\com\example\demo\controller\BlogController.javapackage com.example.demo.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/blog") public class BlogController { /** * 最新ブログ記事を取得 * @return タイトルと内容のMap */ @RequestMapping(value = "/entries/latest", method = RequestMethod.GET) public Entry getLatestEntry() { Entry entryData = new Entry(); // TODO DBから最新の記事データを取得 entryData.setTitle("VueRouterを使ってみた"); entryData.setContent("VueRouterで簡単にページルーティングができました"); return entryData; } /** * ブログ記事クラス */ private class Entry{ private String title; private String content; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } }IntelliJでSpringBootアプリを作成するとアプリ起動に必要な以下のクラスは自動で生成されています
DemoApplication.java
demo\src\main\java\com\example\demo\DemoApplication.javapackage com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
ServletInitializer.java
demo\src\main\java\com\example\demo\ServletInitializer.javapackage com.example.demo; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(DemoApplication.class); } }アプリ画面
Vue.js+Vuetify+VueRouterで簡単な画面を2つ作成しました
トップページは画面タイトルと「BLOG」ボタンだけのシンプルな画面です
demo\web\src\pages\TopPage.vue<template> <v-app> <v-app-bar absolute color="teal lighten-3" dark > <v-toolbar-title>トップページ</v-toolbar-title> </v-app-bar> <v-container class="ml-0 mt-16"> <v-row> <v-col cols="4"> <v-btn color="brown lighten-3" width="100" v-on:click="clickBlogBtn" > blog </v-btn> </v-col> </v-row> </v-container> </v-app> </template> <script> export default { name: "TopPage", methods: { clickBlogBtn: function () { this.$router.push("/blogPage") } } } </script> <style> </style>ブログページは先ほど作成したRESTAPIを呼び出してブログ記事データを表示します
demo\web\src\pages\BlogPage.vue<template> <v-app> <v-app-bar absolute color="teal lighten-3" dark > <v-toolbar-title>tech blog</v-toolbar-title> </v-app-bar> <v-container class="ml-0 mt-16"> <v-row> <v-col cols="4"> <v-btn color="blue-grey lighten-4" width="100" v-on:click="clickTopPageBtn" > top page </v-btn> </v-col> </v-row> <v-row> <v-card class="ml-3" color="yellow lighten-5"> <v-card-title>{{ blogPost.title }}</v-card-title> <v-card-text> {{ blogPost.content }} </v-card-text> </v-card> </v-row> </v-container> </v-app> </template> <script> export default { name: "BlogPage", data: () => ({ // APIから取得した記事データをバインド blogPost: { title: "", content: "" } }), mounted() { // RESTAPI呼び出し const el = this this.axios.get("/blog/entries/latest") .then(response => { el.blogPost = response.data }) }, methods: { clickTopPageBtn: function () { this.$router.push("/") } } } </script> <style> </style>以下のファイルも修正・作成しました main.jsは変更不要です
App.vue
demo\web\src\App.vue<template> <v-app> <router-view/> </v-app> </template> <script> export default { name: 'App' }; </script>
router.js
demo\web\src\router.jsimport Vue from "vue" import Router from "vue-router" import TopPage from "@/pages/TopPage"; import BlogPage from "@/pages/BlogPage"; Vue.use(Router) export default new Router({ mode: "history", routes: [ { path: "/", name: "トップページ", component: TopPage }, { path: "/blogPage", name: "ブログ", component: BlogPage } ] })
これでアプリケーションの作成は完了ですビルド済みVue.jsの出力先を変更
この時点でwebディレクトリで
npm run build
するとweb\dist\配下にビルド済みVue.jsリソースが出力されます
web\package.jsonを修正してJavaから見える場所に出力するようにしますscripts.buildを以下のように修正してSpringBootプロジェクトの静的リソースディレクトリに出力するようにします
demo\web\package.json{ "name": "demo", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build --dest ../src/main/resources/static/", "lint": "vue-cli-service lint" },上記の通りpackage.jsonを修正してビルドすると以下のようになります
cd C:\Users\risab\git\demo\web npm run buildjarを生成する
jarファイルはgradleで生成するので、以下の記述をbuild.gradleの末尾に追加します
Main-Classには@SpringBootApplication
を付けているクラスを指定しますdemo\build.gradlejar { manifest { attributes 'Main-Class': 'DemoApplication' }以下のコマンドを実行するとbuild\libs\配下にjarファイルが生成されます
cd C:\Users\risab\git\demo gradlew bootJarあとは生成されたjarファイルを実行するだけです!
cd C:\Users\risab\git\demo\build\libs java -jar demo-0.0.1-SNAPSHOT
Started DemoApplication in 2.893 seconds (JVM running for 3.378)
のように出力されていればSpringBootアプリケーションが起動できていますSpringBootの組み込みTomcatを利用しているので、ブラウザで
http://localhost:8080/
にアクセスします
- 投稿日:2020-10-26T13:51:43+09:00
英単語から絵文字を検索するWebアプリを作ってみた
まずは動かしてみた
もし、その絵文字がなければ、エラーメッセージが出るようにしてます。
URL
画面は一般公開しています。
https://todo-heihei.tk/
c使用したライブラリ
■Javascript
●Vue.js
https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js
●jQuery.js
https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js
●Bootstrap
https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/js/bootstrap.min.js
●emojione
https://cdnjs.cloudflare.com/ajax/libs/emojione/4.5.0/lib/js/emojione.min.js■CSS
●Bootstrap
https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css
●emojione
https://cdnjs.cloudflare.com/ajax/libs/emoji-awesome/0.0.2/css/emojione.min.cssソース
プログラム自体はCodePenを利用しています。
index.html<!DOCTYPE html> <html lang="ja" > <head> <meta charset="UTF-8"> <title>絵文字検索ツール</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css'> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/emoji-awesome/0.0.2/css/emojione.min.css'> </head> <body> <!-- partial:index.partial.html --> <!-- 全体をVue.js有効にする --> <div class="container text-center text-white bg-dark" id="app"> <!-- タイトル行 --> <div class="row my-3"> <div class="col-sm-6 mx-auto"><h1>絵文字検索ツール</h1></div> </div> <!-- タスク入力行 --> <div class="row my-3"> <div class="col-sm-6 mx-auto"> <input v-model:value="word" placeholder="単語を入力…" class="form-control"><br> <button v-on:click="searchEmoji" class="btn btn-primary">絵文字を調べる</button> </div> </div> <!-- タスク追加されると表示される部分 --> <div class="row my-3"> <div class="col-sm-6 mx-auto"> <p v-if="emoji === ''" class="alert alert-danger">対象の絵文字がないよ</p> <p v-else class="alert alert-success">{{ emoji }}</p> </div> </div> </div><!-- 全体ここまで --> <!-- partial --> <script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/js/bootstrap.min.js'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/emojione/4.5.0/lib/js/emojione.min.js'></script> <script src="./script.js"></script> </body> </html>script.jsconst app = new Vue({ el: '#app', // Vueが管理する一番外側のDOM要素 data: { // Vue内部で使いたい変数は全てこの中に定義する word: '', emoji: '', }, methods: { // 関数はここ searchEmoji: function() { console.log('次の単語が入力されました:', this.word); console.log('絵文字は次の通りです:', emojione.toImage(':' + this.word + ':')); this.emoji = emojione.toImage(':' + this.word + ':'); if (this.emoji.indexOf('img') == -1) { this.emoji = ''; } }, }, });考察
・絵文字だけ返すことができずに、imgタグが出てくるかっこ悪い表示となっている。
→絵文字だけ返却できるようにしたい。・英単語しか検索できない。
→日本語で検索できるようにしたい。
- 投稿日:2020-10-26T10:29:05+09:00
タグクラウド、ワードクラウド laravel と vue.js を添えて
タグクラウド作りてぇ。
ということでライブラリと作り方を紹介海外では タグクラウド ではなく、 ワードクラウドというのだろうか。
インストール
https://github.com/feifang/vue-wordcloud
npm install vue-wordcloudコピペで動く
<style> div.tooltip { position: absolute; width: 210px !important; height: 70px !important; padding: 16px; font: 18px Arial; line-height: 24px; color: white; background: black; border: 0px; border-radius: 2px; pointer-events: none; } </style> <template> <div> <div class="cloud"> <wordcloud :data="defaultWords" nameKey="name" valueKey="value" :showTooltip="true" :wordClick="wordClickHandler"> </wordcloud> </div> </div> </template> <script> import wordcloud from 'vue-wordcloud' export default { components: { wordcloud }, data () { return { myColors: ['#1f77b4', '#629fc9', '#94bedb', '#c9e0ef'], defaultWords: [ { "name": "りんご", "value": 26 }, { "name": "みかん", "value": 19 }, { "name": "おれんじ", "value": 18 }, { "name": "look", "value": 16 }, { "name": "two", "value": 15 }, { "name": "fun", "value": 9 }, { "name": "know", "value": 9 }, { "name": "good", "value": 9 }, { "name": "play", "value": 6 } ], }; }, methods: { wordClickHandler(name, value, vm) { console.log('クリックされたよ', name, value, vm); }, } } </script>以上
- 投稿日:2020-10-26T04:36:15+09:00
Vue.js × django REST framework 学習ロードマップ
はじめに
本記事は「急がば回れ」精神に基づいて、じっくり確実に Vue.js と django を組み合わせ使えるようになるための学習教材を列挙しました。
Vue.js の学習教材
超Vue.js 2 完全パック - もう他の教材は買わなくてOK! (Vue Router, Vuex含む) | Udemy
https://www.udemy.com/course/vue-js-complete-guide/
→ Vue.js の基礎を一通り動画で学ぶ。django の学習教材
【徹底的に解説!】Djangoの基礎をマスターして、3つのアプリを作ろう! | Udemy
https://www.udemy.com/course/django-3app/
→ django の基礎を一通り動画で学ぶ。django REST framework の学習教材
Django REST Framework で API サーバーを実装して得た知見まとめ(OAuthもあるよ)
https://qiita.com/furuuchin/items/c6d6230aa327ad7b337a
→ 下記の英語動画に備えて、こちらの記事のコードを写経して概要を掴む。The Complete Guide to Django REST Framework and Vue JS | Udemy
https://www.udemy.com/course/the-complete-guide-to-django-rest-framework-and-vue-js/
→ django REST framework の基礎と Vue.js との組み合わせ方を一通り動画で学ぶ。英語字幕出せます。[Django REST Framework] View の使い方をまとめてみた - くろのて
https://note.crohaco.net/2018/django-rest-framework-view/
→上記の動画は英語なので View についての補足はコチラで。[Django REST Framework] Serializer の 使い方 をまとめてみた - くろのて
https://note.crohaco.net/2018/django-rest-framework-serializer/
→上記の動画は英語なので Serializer についての補足はコチラで。さいごに
追記してアップデートしていきます。