20210510のvue.jsに関する記事は11件です。

【Vue × Firestore】削除機能の追加

削除機能の追加 list.vue <button class="hide-btn" @click="deletePost">×</button> list.vue deletePost() { firebase .firestore() .collection("posts") .doc(this.$route.params.uid) .delete(); } router.vue { path: "/board/:uid", name: "Board", component: Board, }, clickしたらdeletePost()が発火します。 postsというコレクションを参照して、現在のURLのパラメータを取得。 delete()で対象を削除します。 この記述のみで削除機能を実装可能です。 おまけ 【Vue.js】vue-swalを使ってアラートを実装 vueにはvue-swalという便利なライブラリをがあります。 このライブラリを使用して削除の際にアラートを付けてあげるとより良いでしょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue × Firestore】ページを一つ前に戻る(認証済みユーザー)

ページを一つ前に戻る chat.vue <router-link :to="`/board/${this.uid}`" class="back-btn"> chat.vue created() { const currentUser = firebase.auth().currentUser; this.uid = currentUser.uid; firebase .firestore() .collection("users") .doc(currentUser.uid) .get(); } router.vue { path: "/board/:uid", name: "Board", component: Board, }, 上記では、変数に入れてるが入れずに記述すると下記になります。 このコードでログイン中のuidを取得できます。 chat.vue firebase .firestore() .collection("users") .doc(firebase.auth().currentUser.uid) .get(); そして、routerで記述したようにPathを"/board/:uidとしているので、 router-linkタグに:to="/board/${this.uid}"としてあげると/boardページに戻ることが出来る。 chat.vue <router-link :to="`/board/${this.uid}`" class="back-btn">
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue × Realtime Detabase】LINEのように自身のメッセージとその他のメッセージを区別し、自身のメッセージを右側に表示させる。

LINEのように自身のメッセージとその他のメッセージを区別し、自身のメッセージを右側に表示させる。 chat.vue <transition-group name="chat" tag="div" class="list content"> <!--chatの中の{ key, name, image, message ,userid }をそれぞれ取得--> <section v-for="{ key, name, image, message, userid } in chat" :key="key"> <div v-if="userid === user.uid" class="myitem flex"> <!-- 自身 --> <!--「画像」の指定--> <!--「名前」と「メッセージ」の指定--> <div class="mydetail"> <div class="mytime">time</div> <div class="mymessage"> <nl2br tag="div" :text="message" /> </div> </div> <div class="myimage flex"> <img :src="image" width="40" height="40" /> <div class="myname">name</div> </div> </div> <div v-else class="otheritem flex"> <!-- 自身ではない --> <!--「画像」の指定--> <div class="otherimage flex"> <img :src="image" width="40" height="40" /> <div class="othername">name</div> </div> <!--「名前」と「メッセージ」の指定--> <div class="otherdetail"> <div class="othermessage"> <nl2br tag="div" :text="message" /> </div> <div class="othertime">time</div> </div> </div> </section> </transition-group> chat.vue <script> import firebase from "firebase"; import Nl2br from "vue-nl2br"; // 改行を <br> タグに変換するモジュール export default { components: { Nl2br }, data() { return { user: {}, // ユーザー情報 chat: [], // 取得したメッセージを入れる配列 input: "", // 入力したメッセージ usersData: [], profileDeta: {} }; }, created() { firebase.auth().onAuthStateChanged(user => { this.user = user ? user : {}; //firebase.database()で以下のデータベースの読み書きを行う。 const ref_message = firebase.database().ref(this.$route.params.id); //[router.vue]にて「/ ~ /:id」と指定しルートがマッチした時、 if (user) { this.chat = []; ref_message.limitToLast(10).on("child_added", this.childAdded); } else { // message に変更があったときのハンドラを解除 ref_message.limitToLast(10).off("child_added", this.childAdded); } }); ~ 省略 ~ }, childAdded(snap) { const message = snap.val(); this.chat.push({ key: snap.key, name: message.name, image: message.image, message: message.message, userid: message.userid }); console.log(this.chat); this.scrollBottom(); }, doSend() { if (this.user.uid && this.input.length) { // firebaseに書き込まれたメッセージを追加 firebase .database() .ref(this.$route.params.id) .push( { message: this.input, name: this.user.displayName, image: this.user.photoURL, userid: this.user.uid, }, () => { this.input = ""; // フォームを空にする } ); } } } }; </script> .list { width: 75%; margin-bottom: 100px; } .content { margin: 0 auto; padding: 0 10px; // -- mymessage -- // .myitem { position: relative; margin: 2rem; } .mytime { color: $white-color; margin-right: 0.5rem; display: flex; align-items: flex-end; } .mymessage { display: inline-block; position: relative; margin: 0 20px 0 0; padding: 10px; max-width: 460px; border-radius: 12px; background: #00ec0ccb; z-index: 5; } .mymessage::before { position: absolute; content: " "; display: block; right: -16px; bottom: 12px; border: 4px solid transparent; border-left: 12px solid #00ec0ccb; } .myname { color: $white-color; font-size: 75%; margin-top: 0.5rem; font-weight: bold; } .myimage { flex-direction: column; padding-left: 1.4rem; img { border-radius: 20px; vertical-align: top; display: flex; align-items: flex-end; } } .mydetail { margin: 0 0 0 auto; display: flex; } // -- othermessage -- // .otheritem { position: relative; margin: 2rem; justify-content: flex-start; } .othertime { color: $white-color; margin-left: 0.5rem; display: flex; align-items: flex-end; } .othermessage { display: inline-block; position: relative; margin: 0 0 0 20px; padding: 10px; max-width: 460px; border-radius: 12px; background: #00ec0ccb; z-index: 5; } .othermessage::before { position: absolute; content: " "; display: block; left: -16px; bottom: 12px; border: 4px solid transparent; border-right: 12px solid #00ec0ccb; } .othername { color: $white-color; font-size: 75%; margin-top: 0.5rem; font-weight: bold; } .otherimage { flex-direction: column; padding-right: 1.4rem; img { border-radius: 20px; vertical-align: top; } } .otherdetail { margin: 0 0 0 1.4em; display: flex; .othername { font-size: 75%; } } } chat.vue <section v-for="{ key, name, image, message, userid } in chat" :key="key"> <div v-if="userid === user.uid" class="myitem flex"> <!-- 自身 --> <!--「画像」の指定--> <!--「名前」と「メッセージ」の指定--> <div class="mydetail"> <div class="mytime">time</div> <div class="mymessage"> <nl2br tag="div" :text="message" /> </div> </div> <div class="myimage flex"> <img :src="image" width="40" height="40" /> <div class="myname">name</div> </div> </div> <div v-else class="otheritem flex"> <!-- 自身ではない --> <!--「画像」の指定--> <div class="otherimage flex"> <img :src="image" width="40" height="40" /> <div class="othername">name</div> </div> <!--「名前」と「メッセージ」の指定--> <div class="otherdetail"> <div class="othermessage"> <nl2br tag="div" :text="message" /> </div> <div class="othertime">time</div> </div> </div> </section> 「v-if関数」と「v-else関数」を使用して、「自身」と「その他」の区別をしています。 「自身」「その他」をそれぞれ作成します。 chat.vue childAdded(snap) { const message = snap.val(); this.chat.push({ key: snap.key, name: message.name, image: message.image, message: message.message, userid: message.userid }); console.log(this.chat); this.scrollBottom(); }, doSend() { if (this.user.uid && this.input.length) { // firebaseに書き込まれたメッセージを追加 firebase .database() .ref(this.$route.params.id) .push( { message: this.input, name: this.user.displayName, image: this.user.photoURL, userid: this.user.uid, }, () => { this.input = ""; // フォームを空にする } ); 上記でthis.user.uidをuseridと追加しておき、表示処理の部分でuseridとuser.uidを比較し(v-ifを使用)、 同じなら自身の書き込みとするようにします。 これでロジックとしては完成になります。 あとは自身のメッセージのみ右寄せさせるようにCSSを書いていきます。 chat.vue .myitem { position: relative; margin: 2rem; } .mytime { color: $white-color; margin-right: 0.5rem; display: flex; align-items: flex-end; } .mymessage { display: inline-block; position: relative; margin: 0 20px 0 0; padding: 10px; max-width: 460px; border-radius: 12px; background: #00ec0ccb; z-index: 5; } .mymessage::before { position: absolute; content: " "; display: block; right: -16px; bottom: 12px; border: 4px solid transparent; border-left: 12px solid #00ec0ccb; } .myname { color: $white-color; font-size: 75%; margin-top: 0.5rem; font-weight: bold; } .myimage { flex-direction: column; padding-left: 1.4rem; img { border-radius: 20px; vertical-align: top; display: flex; align-items: flex-end; } } .mydetail { margin: 0 0 0 auto; display: flex; } これでLINEのように自身のメッセージの場合は右寄せに、その他のメッセージでは左寄せになるよう実装することが出来ました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue x Firestore】セレクトボックスで初期表示の文字のみ色を変える

セレクトボックスで初期表示文字のみ色を変える 自分自身ポートフォリオ作成時に沼ってしまい時間を要してしまった為、備忘録として置いておきます。 結論を先に言うと「undefind」となってしまう場合は、「空の文字列」を入れるようにする。 mypage.vue <modal class="modal-inner" v-scroll-lock="open" name="edit" :width="1100" :height="740"> <div data-modal="edit" aria-expanded="true" class="vm--overlay"> <div class="vm--top-right-slot"></div> </div> <div class="modal-header flex"> <h2 class="profile-tll flex">プロフィールを編集する</h2> <hr class="separate" /> </div> <div class="modal-body"> <div class="profile-inner flex"> <div class="profile-contens flex"> <div class="profile-img-inner flex"> <img src="../assets/アイコン.jpg" width="200" height="200" class="profile-img" alt="プロフィール画像" /> <button class="profile-txt profile-update">プロフィール画像を編集する</button> </div> <div class="line"></div> <div class="profile-items flex"> <div class="profile-contens flex"> <input type="text" class="profile-item" placeholder="名前" v-model="name" /> </div> <div class="profile-contens flex"> <select class="profile-select" v-model="sex" :style="{ color: sex == '' ? 'gray' : 'white' }" > <option class="profile-item" value hidden>性別</option> <option v-for="sex in sexs" :value="sex.name" :key="sex.id" class="profile-item" style="color: white;" >{{ sex.name }}</option> </select> </div> ~ 省略 ~ <button class="hide-btn flex" @click=" hide(); closeModal(); " >×</button> </div> <button @click="updateBtn" class="update-btn flex">更新</button> </div> </div> </modal> mypage.vue export default { data() { return { name: "", sex: "", sexs: [{ name: "男性" }, { name: "女性" }, { name: "その他" }], age: "", ages: [ { name: "10際未満" }, { name: "10 ~ 19歳" }, { name: "20 ~ 29歳" }, ~ 省略 ~ ], access: "", accesses: [ { name: "北海道" }, { name: "青森県" }, { name: "岩手県" }, ~ 省略 ~ ], profession: "", professions: [ { name: "公務員" }, { name: "会社員" }, { name: "自営業" }, ~ 省略 ~ ], selfpr: "", genre: "", genres: [ { id: 0, name: "ジャンル" }, { id: 1, name: "アクション" }, { id: 2, name: "ドラマ" }, { id: 3, name: "恋愛" }, ~ 省略 ~ ], favMovie: "", uploadedImage: "", profileData: {}, listData:[], open: false, }; }, components: { Header, }, methods: { // updateBtn()が押下されたら、dbインスタンスを初期化して"posts"という名前のコレクションへの参照 updateBtn() { firebase .firestore() .collection("users") .doc(this.$route.params.uid) //現在のURLのパラメーターを取得。 .set( { name: this.name, sex: this.sex, age: this.age, access: this.access, selfpr: this.selfpr, profession: this.profession, uploadedImage: this.uploadedImage, genre: this.genre, favMovie: this.favMovie, time: firebase.firestore.FieldValue.serverTimestamp() //サーバ側で値設定 }, { merge: true } //set()でmergeをtrueにすると、上書き。updetaと同様。 ); this.$swal({ title: "内容確認", text: "この内容で投稿しますか?", icon: "info", buttons: true, dangerMode: true }).then(willDelete => { if (willDelete) { this.$swal("投稿しました。", { icon: "success" }); this.$router.go({ path: `/mypage/${this.$route.params.uid}`, force: true }); //プロフィール編集されたらページをリロード } else { this.$swal("キャンセルしました。"); } }); }, ~ 省略 ~ }, created() { const currentUser = firebase.auth().currentUser; if (currentUser) { firebase .firestore() .collection("users") .doc(this.$route.params.uid) .get() .then(snapshot => {      this.name = this.profileData.name || ""; this.sex = this.profileData.sex || ""; this.age = this.profileData.age || ""; this.access = this.profileData.access || ""; this.selfpr = this.profileData.selfpr || ""; this.profession = this.profileData.profession || ""; this.uploadedImage = this.profileData.uploadedImage || ""; this.genre = this.profileData.genre || ""; this.favMovie = this.profileData.favMovie || ""; }); } mypage.vue <select class="profile-select" v-model="sex" :style="{ color: sex == '' ? 'gray' : 'white' }"> <option class="profile-item" value hidden>性別</option> <option v-for="sex in sexs" :value="sex.name" :key="sex.id" class="profile-item" style="color: white;">{{ sex.name }}</option> </select> :style="{ color: sex == '' ? 'gray' : 'white' }"と三項演算子を使用して未選択であればグレー、選択済であれば白色としています。 mypage.vue created() { const currentUser = firebase.auth().currentUser; if (currentUser) { firebase .firestore() .collection("users") .doc(this.$route.params.uid) .get() .then(snapshot => { this.name = this.profileData.name || ""; this.sex = this.profileData.sex || ""; this.age = this.profileData.age || ""; this.access = this.profileData.access || ""; this.selfpr = this.profileData.selfpr || ""; this.profession = this.profileData.profession || ""; this.uploadedImage = this.profileData.uploadedImage || ""; this.genre = this.profileData.genre || ""; this.favMovie = this.profileData.favMovie || ""; }); } 上記のように「this.name = this.profileData.name || ""」とせず「this.name = this.profileData.name」としてしまうとundefindとなってしまう。 その時は「this.name = this.profileData.name || ""」を加えて空の文字列を入れるようにします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js 勉強メモ】コンポーネントはLocal登録で使う

はじめに 仕事で使う事になったので1からVue.jsについて学んだ(元々Angularでプロダクト開発をやっていた事はあるがAngularはだめか・・・)。 ちゃんと覚えておかないとまずそうな事を備忘録として1つ1つ残しておく。 コンポーネントはLocal登録で使うべし 前提として、Vue.jsでコンポーネントを定義する方法には以下の2つがある。 # 方法 1 Local登録 2 Global登録 この内、基本的にはLocal登録を用いるようにするべき。 Gobal登録を一般的に用いない理由は、 あまり使用されないコンポーネントなのにファイルに残ってしまいファイルサイズが増加する などの弊害があるため。 ※Local登録・Gobal登録いずれも以下の画像のように描画される。 Local登録の例 sample.html <body> <div id="app"> <my-component></my-component> <my-component></my-component> <my-component></my-component> </div> <script src=" https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <script> Vue.component('my-component', { data: function () { return { number: 12 } }, template: '<p>いいね({{number}})<button @click="increment">+1</button></p>', methods: { increment: function () { this.number += 1; } } }) new Vue({ el: '#app' }) </script> </body> Global登録の例 sample.html <body> <div id="app"> <my-component></my-component> <my-component></my-component> <my-component></my-component> </div> <script src=" https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <script> const component = { data: function () { return { number: 12 } }, template: '<p>いいね({{number}})<button @click="increment">+1</button></p>', methods: { increment: function () { this.number += 1; } } } new Vue({ el: '#app', components: { 'my-component': component } }) </script> </body> Vue.jsの勉強メモ一覧記事へのリンク Vue.jsについて勉強した際に書いた勉強メモ記事のリンクを集約した記事。 https://qiita.com/yuta-katayama-23/items/dabefb59d16a83f1a1d4
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js 勉強メモ】コンポーネントはLocal登録で使うのが基本

はじめに 仕事で使う事になったので1からVue.jsについて学んだ(元々Angularでプロダクト開発をやっていた事はあるがAngularはだめか・・・)。 ちゃんと覚えておかないとまずそうな事を備忘録として1つ1つ残しておく。 コンポーネントはLocal登録で使うのが基本 前提として、Vue.jsでコンポーネントを定義する方法には以下の2つがある。 # 方法 1 Local登録 2 Global登録 この内、基本的にはLocal登録を用いるようにするべき。 Gobal登録を一般的に用いない理由は、 あまり使用されないコンポーネントなのにファイルに残ってしまいファイルサイズが増加する などの弊害があるため。 ※Local登録・Gobal登録いずれも以下の画像のように描画される。 Global登録の例 sample.html <body> <div id="app"> <my-component></my-component> <my-component></my-component> <my-component></my-component> </div> <script src=" https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <script> Vue.component('my-component', { data: function () { return { number: 12 } }, template: '<p>いいね({{number}})<button @click="increment">+1</button></p>', methods: { increment: function () { this.number += 1; } } }) new Vue({ el: '#app' }) </script> </body> Local登録の例 sample.html <body> <div id="app"> <my-component></my-component> <my-component></my-component> <my-component></my-component> </div> <script src=" https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <script> const component = { data: function () { return { number: 12 } }, template: '<p>いいね({{number}})<button @click="increment">+1</button></p>', methods: { increment: function () { this.number += 1; } } } new Vue({ el: '#app', components: { 'my-component': component } }) </script> </body> Vue.jsの勉強メモ一覧記事へのリンク Vue.jsについて勉強した際に書いた勉強メモ記事のリンクを集約した記事。 https://qiita.com/yuta-katayama-23/items/dabefb59d16a83f1a1d4
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Webアプリケーション開発におけるフロントエンド、バックエンド分離環境

目的 PoC業務で、数年ぶりにWebアプリケーション開発を行うことになって、開発環境、特にフロントエンド開発環境の進展、変貌に驚いた。 動的コンテンツはJSP, Ajaxで止まっており、テンプレートにjavascript, JSPタグを必至に埋め込んでいた自分にとって、これは纏めないと速攻忘れそうだ。 開発環境の構成 フロントエンドとバックエンドはREST APIで分離されている。 フロントエンドはローカル環境(Windows10, Mac)で開発可能 フロントエンド開発においてバックエンドはスタブで代用可能 この開発環境の利点 フロントエンド、バックエンドの開発環境、開発体制を分離できる。(REST APIのみ取り決め) フロントエンド開発はjavascriptダミーデータによりREST API取り決め以前でも開始可能。 開発環境での充分なテスト後に、フロントエンド実行環境をサーバにデプロイすることができる。 フロントエンド開発環境 前提 フロントエンド開発環境 (Windows10) javascript実行環境 node.js javascript実行環境 PS C:\work\git\post19\acp_portal> node -v v14.15.1 yarn npm互換のパッケージマネージャ。npmでもいいが、nuxtの自動セットアップと依存バージョンの固定(yarn.lock)を期待。 PS C:\work\git\post19\acp_portal\sample_cloud> yarn -v 1.22.10 javascriptフレームワーク、コンポーネントライブラリ vue ユーザーインターフェイスを構築するためのjavascriptフレームワーク yarn create nuxt-app で自動的にインストールされる。 nuxt vueフレームワーク。vueファイルの配置がそのままルーティングになるためかなり楽。 yarn create nuxt-app で自動的にインストールされる。 vuetify VueのUIコンポーネントライブラリ。多くのコンポーネント、レイアウトツールが含まれていて、vuetifyコンポーネントを組み合わせていくだけで多くの画面を構成できる。これを利用するためにVueを使うといっても過言ではない。 yarn create nuxt-app で自動的にインストールされる。 フロントエンドアプリケーション作成例 yarn create nuxt-appによりnuxtフレームワークを利用したアプリケーション(sample-app)を作成する。 選択の要点 Programming language: JavaScript/TypeScriptのどちらかの選択。上位互換のTypeScriptを選択。 Package manager: Yarn UI framework: Vuetity.jsを選択すると自動的にインストールされる。 Nuxt.js modules: AxiosモジュールによりREST APIアクセスを行う。 Rendering mode: Singe Page Appを選択 PS C:\work\git\frontend> yarn create nuxt-app sample-app yarn create v1.22.10 [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh packages... success Installed "create-nuxt-app@3.6.0" with binaries: - create-nuxt-app create-nuxt-app v3.6.0 ✨ Generating Nuxt.js project in sample-app ? Project name: sample-app ? Programming language: TypeScript ? Package manager: Yarn ? UI framework: Vuetify.js ? Nuxt.js modules: Axios - Promise based HTTP client ? Linting tools: ESLint ? Testing framework: None ? Rendering mode: Single Page App ? Deployment target: Server (Node.js hosting) ? Development tools: jsconfig.json (Recommended for VS Code if you're not using typescript) ? Continuous integration: None ? Version control system: Git yarn run v1.22.10 $ eslint --ext ".js,.vue" --ignore-path .gitignore . --fix Done in 21.63s. To get started: cd sample-app yarn dev To build & start for production: cd sample-app yarn build yarn start For TypeScript users. See : https://typescript.nuxtjs.org/cookbook/components/ Done in 258.47s. yarn.lockにアプリケーション依存ライブラリのバージョンが固定される。 Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2021/05/10 15:24 402210 yarn.lock ・・・ vue "^2.6.12" ・・・ vuetify "^2.4.2" ・・・ nuxt@^2.15.3: version "2.15.5" フロントエンドアプリケーションの起動 yarm devで起動 PS C:\work\git\frontend\sample-app> yarn dev yarn run v1.22.10 $ nuxt ╭───────────────────────────────────────╮ │ │ │ Nuxt @ v2.15.5 │ │ │ │ ▸ Environment: development │ │ ▸ Rendering: client-side │ │ ▸ Target: server │ │ │ │ Listening: http://localhost:3000/ │ │ │ ╰───────────────────────────────────────╯ i Preparing project for development 15:45:47 i Initial build may take a while 15:45:47 i Discovered Components: .nuxt/components/readme.md 15:45:47 √ Builder initialized 15:45:47 √ Nuxt files generated 15:45:48 √ Client Compiled successfully in 18.58s i Waiting for file changes 15:46:15 i Memory usage: 237 MB (RSS: 428 MB) 15:46:15 i Listening on: http://localhost:3000/ 15:46:15 No issues found. 15:46:15 ブラウザからlocalhost:3000にアクセス フロントエンドアプリケーションの開発例 この状態からバックエンド環境から独立してフロントエンドアプリケーション開発が可能です。 試しにトップページ(pages/index.vue)にダッシュボードぽいグラフコンポーネントを配置します。 VuetifyのDashboard cardを開き、ソースを表示。(<>を押下) TEMPLATEの<v-card>~</v-card>をトップページ(pages/index.vue)に貼り付け SCRIPTの内容をトップページ(pages/index.vue)の<script>~</script>にマージ ブラウザからlocalhost:3000にアクセス ダッシュボードぽいグラフが追加されました。 バックエンドとのREST APIによるデータ通信 Javascript・AxiosモジュールによってREST APIクライアントを実装します。 例(GET)だけ示します。 <script> import axios from "axios" export default { data() { let today = new Date(); let tomorrow = new Date(); tomorrow.setDate(today.getDate() + 1); return { from_ts: today.toISOString().substr(0, 10), to_ts: tomorrow.toISOString().substr(0, 10), devices:[], }; }, created: function() { let sample_api = "https://backend/sample_app/api/deviceinfo/" + "?from_ts=" + this.from_ts + "&to_ts=" + this.to_ts axios.get(sample_api).then( function(response) { this.devices = response.data }.bind(this) ) }, バックエンド開発環境 前提 バックエンド開発環境 (ubunutu@AWS) バックエンドは基本的な構成はデータベース、Webアプリケーションフレームワークで構成され、フロントエンドとのREST APIを提供する。 django djangoのREST API Framework HTTPサーバ (Apache 2.4) djangoとの連携をmod_wsgiで行う。 データベース (mariaDB) RESTful環境 djangorestframework, django-filter, django-cors-headersをpipによりinstallする。 /home/ubuntu/acp_cloud/acp_cloud/acp_cloud/settings.py INSTALLED_APPS = [ ... 'rest_framework', 'django_filters', 'corsheaders', ] MIDDLEWARE = [ ... 'corsheaders.middleware.CorsMiddleware', ] REST_FRAMEWORK = { 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) } ... CORS_ORIGIN_ALLOW_ALL = True バックエンド実装については省略します。 本番環境へのデプロイ フロントエンド開発環境でnuxt generateを実行し、ビルドとエクスポートを行う。 本番用の webpack を使用してアプリケーションをビルドおよび最適化します。 すべてのルートを HTML ファイルとして生成し、dist/ ディレクトリに静的にエクスポートします(静的ホスティングに使用されます)。 PS C:\work\git\frontend\sample-app> yarn generate yarn run v1.22.10 $ nuxt generate WARN When using nuxt generate, you should set target: 'static' in your nuxt.config 16:33:29 ? Learn more about it on https://go.nuxtjs.dev/static-target i Production build 16:33:46 i Bundling only for client side 16:33:46 i Target: static 16:33:46 i Using components loader to optimize imports 16:33:46 i Discovered Components: .nuxt/components/readme.md 16:33:46 √ Builder initialized 16:33:46 √ Nuxt files generated 16:33:46 √ Client Compiled successfully in 45.81s Hash: fee4ce8f4922b5867c93 Version: webpack 4.46.0 Time: 45807ms Built at: 2021/05/10 16:34:44 Asset Size Chunks Chunk Names ../server/client.manifest.json 16.5 KiB [emitted] 3714763.js 644 bytes 6 [emitted] [immutable] pages/inspire 40b57dd.js 51.2 KiB 1 [emitted] [immutable] app 5425da6.js 2.14 KiB 3 [emitted] [immutable] components/logo 5f0440d.js 27 KiB 9 [emitted] [immutable] vendors/pages/index 7b1fd44.js 12.7 KiB 0 [emitted] [immutable] vendors/pages/index/pages/inspire 81b55ec.js 214 KiB 2 [emitted] [immutable] commons/app 9f5a49a.js 2.35 KiB 7 [emitted] [immutable] runtime LICENSES 390 bytes [emitted] a373906.js 946 bytes 4 [emitted] [immutable] components/vuetify-logo d98c730.js 482 KiB 8 [emitted] [immutable] [big] vendors/app e4a0c6b.js 6.52 KiB 5, 3, 4 [emitted] [immutable] pages/index + 1 hidden asset Entrypoint app = 9f5a49a.js 81b55ec.js d98c730.js 40b57dd.js WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB). This can impact web performance. Assets: d98c730.js (482 KiB) i Generating output directory: dist/ 16:34:44 i Generating pages 16:34:44 √ Generated route "/inspire" 16:34:44 √ Generated route "/" 16:34:44 √ Client-side fallback created: 200.html 16:34:44 Done in 77.61s. 1.で作成されたdistフォルダをWebサーバのドキュメントルート(ex. /var/www/html)に配置する。 Webサーバを再起動し、上記ドキュメントルートにアクセスする。(ex. https://backend:443/dist) Webサーバにデプロイされた環境は以下のようになる。 フロントエンド開発環境でgenerateされたdistフォルダをWebサーバにデプロイする。 アクセスURLはWebサーバのデプロイ先となる。 フロントエンド/バックエンド間のREST APIはそのままブラウザ/Webサーバ間となるため、CORS設定には注意が必要。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VueCLIでpace.jsを使用する

Pace.jsを使ってページローダーを実装する https://codebyzach.github.io/pace/ PACEはファイルを読み込むだけで簡単にプログレスバーが実装できる定番のプラグインです。また、デザインのテーマが豊富に用意されているので様々なローディング画面を演出できます。 publicフォルダ内にPace.jsを配置した後、 public/index.html <head> <!-- 省略 --> <script src="./pace.js"></script> </head> App.vue <style> @import "./assets/styles/loader.css"; </style> loader.css .pace-done #app { opacity: 1; transition: opacity 2s; } .pace-running #app { opacity: 0; } /* 以下略 */ 上記のようにすることで、ページローダーをVueCLIに配置することができます。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WEBアプリ『ひとこと日記』概要

アプリの概要 アプリ名:ひとこと日記ver2 日々の記録をテキスト形式で保存する、シンプルな日記アプリです。 コードはGithubにてご覧いただけます。(フロントエンドのコード・バックエンドのコード) 作成環境 ・インフラ:AWS ・バックエンド:node.js + Express ・フロントエンド:Vue.js ・データベース:MySQL ●各バージョン node.js v12.18.2, Express 4.17.1, Vue.js 2.6.12, Veu-CLI 4.4.6, MySQL 8.0 アプリで行う主な処理 ユーザー登録・認証処理 日記のCRUD処理 アプリの構成 ・インフラは、AWSのEC2上にExpressサーバーを立て、RDS上にMySQLデータベースを立てています。 ・フロントエンドは、Veu-CLIを使って構築し、SPAにしています。(Nuxt.jsは使っていません) ・Expressサーバーにて、SPAのホスティングと、データベース処理用のエンドポイントの提供を行っています。 ・Vue.js-Express間の通信は、axiosでデータをやり取りしています。 サーバーとクライアントの構成図 ユーザー登録・認証機能の動作 ・UIからユーザー登録、ログインを行います。 ・Vue.jsからaxiosで通信し、Expressサーバーのエンドポイントへアクセスすることで、ユーザー登録、ログインの処理を実現します。 ・ログインするとトークンが発行され、以降の処理はトークンによってユーザーを一意に特定します。 日記機能の動作 ・UIから日記の表示・作成・更新・削除を行います。 ・Vue.jsからaxiosで通信し、Expressサーバーのエンドポイントへアクセスすることで、日記のCRUD処理を実現します。 ・axios通信時にサーバーにトークンを送信することで、ユーザーを一意に特定して、日記データを処理します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

node.js+Vue.jsアプリのWindowsサービス登録方法

winserを使用してnode.js+Vue.jsアプリをWindowsサービスに登録する node.jsアプリのWindowsサービス登録にはwinserモジュールが使えますが、vue-cliを使ったアプリをWindowsサービスに登録する場合にはwinserコマンドがそのまま使えませんでした。 winserがラッピングしているNSSMを使ってWindowsサービス登録ができたので、その方法を紹介します。 手順 1. winserを導入 $ npm install winser 導入されると/node_modules/winserディレクトリができる 2. NSSMを起動 <アプリ名>にはWindowsサービスとして表示したいアプリケーション名称を入れる $ C:\<アプリディレクトリ>\node_modules\winser\bin\nssm.exe install <アプリ名> 以下のような画面が表示される 3. 必要な値を入力 Path: <アプリディレクトリ>\node_modules.bin\vue-cli-service.cmd を絶対パスで指定 Startup: <アプリディレクトリ>を絶対パスで指定 Arguments: アプリ起動コマンドを指定 Expressを使用していたため、ArgumentsにExpressのコマンドを指定した 4. "Install service"ボタンを押下 以下のような画面が表示されてWindowsサービスが登録される これでWindowsサービスにアプリが登録され、起動・停止が可能になりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue CliにTailWind CSSを読み込む方法

TailWind CSS ってなに? CSSフレームワークの一つで、いくつものclassを組み合わせることでコンポーネントが作成できます。 最大の特徴として、1つのクラスに対して1つプロパティのみが付与されていることです。 FLOCSSにおけるユーティリティクラスと同じ発想であり、インラインCSSを書くようにclassを付与していき、コンポーネントを作成していきます。 <!-- border: 1px solid #000; border-radius: 0.25rem; padding: 12px 24px; color: #fff; background: #3b82f6; みたいボタンを作りたいときは以下のように書く --> <button class="border border-solid border-black py-3 px-6 text-white bg-indigo-600">ボタン</button> まずはVue Cliをインストールしてプロジェクトフォルダを作る vue3・vue-router(historyモード)を使用するので、マニュアルでプロジェクトを作成。 Vue Cliのインストールからプロジェクト作成までがわからない方はこの記事を参照にするといいかも。 https://qiita.com/ITmanbow/items/9ea7d3f5a9219d760d0c TailWind CSSのインストール Vue Cliではpluguinでインストールができます。 最新バージョンはvueでサポートがされてないので、公式の記事を参照にして旧バージョンをインストールします。 npm install -D tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9 次にconfigファイルを生成します。 npx tailwindcss init -p 完了すると、tailwind.config.jsが作成されるので、以下の内容を記述します。 tailwind.config.js module.exports = { purge: [], darkMode: false, // or 'media' or 'class' theme: { extend: {}, }, variants: { extend: {}, }, plugins: [], } 最後にpostcss.config.jsを手動で作成し、以下の内容を記述します。 postcss.config.js module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, }, } これでVue CliにおけるTailwind CSSのインストールは完了です。 TailWind CSSを読み込む インストールが完了したら、使えるように読み込んでいきます。 まずは、任意の場所にCSSファイルを作成し、以下の内容を記述します。 EX-/src/assets/css/tailwind.css @tailwind base; @tailwind components; @tailwind utilities; 次にmain.jsにTailwind CSSを読み込む記述を追加します。 main.js import { createApp } from "vue"; import App from "./App.vue"; import router from "./router"; // tailewind css import '@/assets/css/tailwind.css'; これで準備完了です。Vue CliでTailwind CSSを使えるようになりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む