20210909のvue.jsに関する記事は5件です。

Vue.js でコンポーネントの slot で入れ子にしたい

Overview この辺見てたら https://qiita.com/y-miine/items/bf06e9465a2b1821bf2c json でネスト構造持たせるのではなく、 <menu> <menu> <menu> </menu> </menu> </menu> みたいに書きたくなったので試してみた。 こんな感じ コード vue 2 系 typescript pug (一部) scss vue-property-decorator です。 App.vue <template> <div> <TreeMenu label="label1" :items="items" @my-input="hoge"> <TreeMenu label="label2" :items="items" @my-input="hoge"> <TreeMenu label="label3" :items="items" @my-input="hoge"> ほげ </TreeMenu> </TreeMenu> </TreeMenu> </div> </template> <script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; import TreeMenu from './components/TreeMenu.vue'; @Component({ components: { TreeMenu, }, }) export default class App extends Vue { items = [ {message: "msg1"}, {message: "msg2"}, {message: "msg3"}, ] hoge(event) : void { console.log("emitted", event); } } </script> <style lang="scss"> </style> components/TreeMenu.vue <template lang="pug"> .menu label(:for="uid") {{ label }} input(type="checkbox", :id="uid", :checked="checked", @input="onInput") ul li(v-for="item in items" :key="item.message") {{ item.message }} li(v-if="$slots.default") slot </template> <script lang="ts"> import { Emit, Component, Prop, Vue } from 'vue-property-decorator' @Component export default class TreeMenu extends Vue { @Prop({ default: true }) checked @Prop() label @Prop() items uid = this._uid @Emit("my-input") onInput(event) { console.log("input component", event) } } </script> <style scoped lang="scss"> .menu { width: 20rem; input { display: none; &:checked + ul { display: block; } } ul { display: none; background: #ffff00; list-style: none; margin: 0; padding-left: 1rem; } label { display: block; margin: 0; padding: 0.5rem; background: #00ffff; cursor: pointer; &:hover { background: #0000ff; } } } </style> ついでに <input type=checkbox> で開閉制御もしてみた。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue3へ移行に合わせてこう動く

以下環境が整い次第、マルっと移行したいところ Vue3 TypeScript Vite (ヴィータ) Nuxt3 Vuetify3 タイミング 後述するが、Vuetify3が最後(2021/3Q)にリリースされそう そのタイミングに合わせてマルっと。 Vue3 元Google社員の[ Evan You ]が作ったjavascript framework https://v3.ja.vuejs.org/ Vueのメジャーバージョンアップ compositionAPIが標準装備される script書くエリアがかなり整頓されるイメージ composition api is なに? TypeScript MicroSoftが開発したJavaScript進化版 https://www.typescriptlang.org/ Googleの社内標準言語として採用されてる TypeScriptはあらかじめ型を定義する静的型付けでバグなくす Vite (ヴィータ) compileの爆速化 https://zenn.dev/konnyaku256/scraps/6e8f78642cde2c ちなみに作者はVue作者の [ Evan You ] compileまじで早いです Nuxt3 2021/2Qにベータ版 https://nishimura.club/nuxtjs-v3-roadmap-and-information-organization VueFrameworkで大人気のやつ ある程度規約があるので、コードが共通認識されやすい Vuetify3 2021/3Qにリリース目標らしい https://vuetifyjs.com/ja/introduction/roadmap/#v3-0-titan compositionAPI対応されたcomponent 従来通り、楽に綺麗なデザインできる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発】あるあるネタを言いたかったのであるあるネタを共有・投票できるアプリを作りました。

はじめに 「これあるあるだわ〜しかも結構面白いからみんなに共有したいなー」 って思ったことありませんか? ありますよね? あると思います。 ということで今回、あるあるネタを共有・投票できるアプリをリリースしました! サービス概要 日常で起きたあるあるネタを投稿し、一人何回でも投票できるアプリケーションです。 一人で何回でも投票できるため、自分の投稿を意図的に上の方に持ってくることも可能です。 複数回投票できることで承認欲求も満たせるかと思います。 Twitter共有もできるので、ぜひオススメのあるあるネタをたくさん共有してみてください! 使用技術 バックエンド Ruby 2.7.2 Rails 6.0.4.1 RSpec 5.0.2 RuboCop 1.20.0 JWT 2.2.3 フロントエンド Vue 2.6.14 VueRouter 3.5.2 Vuex 3.6.2 Vuetify 2.5.8 VeeValidate 3.4.12 axios 0.21.1 ESLint 0.4.3 インフラ Nginx Puma AWS VPC EC2 Route53 RDS ALB ACM Capistrano インフラ構成図 最後に Vue.jsを使ってアプリを作ったのは初めてだったので、いろいろとわからないこともありましたが楽しく作れました。 やはり初学者にとって日本語のドキュメントがあるのは助かりますね笑 最後まで読んでいただきありがとうございました! 前回作ったアプリについての記事も見てくれたら嬉しいです↓ 【個人開発】家の中にときどき出る不快な虫を検索するサービス「むしめがね」をリリースしました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue-chartjsで export 'default' xxx was not found in 'chart.js' エラーが出るとき

vue-chartjs公式を参考に導入してみたら、以下のエラーが出てしまった。 export 'default' xxx was not found in 'chart.js' 原因 chart.jsの最新バージョン(3.x系)にvue-chartjsが対応していなかった。 以下のように素直にインストールするとchart.jsは3.x系がインストールされるので、ダウングレードする必要がある。 yarn add vue-chartjs chart.js 対応 2.x系の最終版はv2.9.4らしいので、そちらを導入 yarn remove chart.js yarn add chart.js@2.9.4 エラー解消し、無事チャートが描画された 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

router-linkで表示した投稿にいいねを押して、別のページに遷移したら反映されてない

投稿のユーザーの名前をクリックすると、そのユーザーの全投稿が表示される機能を VueRouterを用いて作ったのだが、タイムラインに戻ったときにいいねがリアルタイムで反映されていない。。。 http://coffee-passport.net/ こちらで実際の挙動を確認していただければと思います。 (投稿一覧) app.vue <template> <div class='main' > <div class="playing-button"> <button class="playing-button-on" @click="pauseVideo" v-if="playing">BGMをOFF</button> <button class="playing-button-off" @click="playVideo" v-else>BGMをON</button> </div> <div class='item-contents'> <router-view :user_id="user_id"></router-view> <h2 class='title' id="title">タイムライン</h2> <youtube :video-id="videoId" ref="youtube" @playerVars="playerVars" hidden/> <ul class='item-lists' id="timeline"> <li v-for="drink in drinks" :key="drink.id" class="list" > <router-link to="/user" @click.native="getUserId(drink.user_id)"> <div class="user-info-timeline" > <div v-if="drink.user_img"> <img class="user-img-timeline" v-bind:src="drink.user_img" > </div> <div v-else> <img class="user-img-timeline" src ="https://images.unsplash.com/photo-1469334031218-e382a71b716b?ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8YnJlYXV0aWZ1bCUyMGdpcmx8ZW58MHx8MHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=700&q=60"> </div> <div class="username-timeline"> {{drink.nickname}} </div> </div> </router-link> <div class='item-img-content'> <img class= "item-img" v-bind:src="drink.image" > </div> <div class='item-info'> <h3 class="item-name"> {{drink.name }} </h3> <div class='item-price'> <span>{{ drink.price }}円<br>(税込み)</span> </div> <div class='item-explain'> {{drink.explain}} </div> <div class='item-tag'> </div> <div class="like-and-comment"> <likeButton :drinkId=drink.id></likeButton> <i class="far fa-comment fa-lg"></i> <button v-on:click="show(drink)">詳細を表示</button> </div> </div> <modal v-bind:name="'display-drink-'+drink.id" height="1000px" styles="background-color: bisque"> <drinkShow :drink=drink></drinkShow> </modal> <div v-if="drink.body_id === 2" class="body-light"></div> <div v-else-if="drink.body_id === 3" class="body-medium"></div> <div v-else-if="drink.body_id === 4" class="body-full"></div> <div v-else class="body-nothing"></div> <div v-if="drink.acidity_id === 2" class="acidity-low"></div> <div v-else-if="drink.acidity_id === 3" class="acidity-medium"></div> <div v-else-if="drink.acidity_id === 4" class="acidity-high"></div> <div v-else class="acidity-nothing"></div> </li> </ul> </div> </div> </template> <script> import axios from 'axios'; import likeButton from './packs/components/like/likeButton.vue'; import drinkShow from './packs/components/drinks/show.vue'; export default { name: 'player', props: { src: "https://www.youtube.com/watch?v=02azSAMtZWU" }, components: { likeButton, drinkShow, }, data: function(){ return { drinks: "drinks", user_id: 0, videoId: "QN1uygzp56s", playing: false, playerVars: { autoplay: 1 } } }, created(){ }, mounted: function(){ // this.playVideo(); this.setDrink(); }, methods: { setDrink: function(){ axios.get('/api/drinks') .then(response =>( this.drinks = response.data )) }, show : function(drink) { this.$modal.show(`display-drink-${drink.id}`); }, hide : function () { this.$modal.hide('display-drink-show'); }, playVideo(){ // 再生処理 this.$refs.youtube.player.playVideo() this.playing = true }, pauseVideo(){ // 停止処理 this.$refs.youtube.player.pauseVideo() this.playing = false }, getUserId(user_id){ this.user_id = user_id document.getElementById("timeline").style.visibility ="hidden"; document.getElementById("title").style.visibility ="hidden"; scrollTo(0, 0); } } } </script> (ユーザーの投稿) user.vue <template> <div class='main'> <div class='item-contents'> <h2 class='title'></h2> <router-link to="/" @click.native="showTimeline()" class="back-to-timeline">タイムラインに戻る</router-link> <ul class='item-lists'> <li v-for="drink in filtredDrinks" :key="drink.id" class="list" > <div class="user-info-timeline" > <div v-if="drink.user_img"> <img class="user-img-timeline" v-bind:src="drink.user_img" > </div> <div v-else> <img class="user-img-timeline" src ="https://images.unsplash.com/photo-1469334031218-e382a71b716b?ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8YnJlYXV0aWZ1bCUyMGdpcmx8ZW58MHx8MHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=700&q=60"> </div> <div class="username-timeline"> {{drink.nickname}} </div> </div> <div class='item-img-content'> <img class= "item-img" v-bind:src="drink.image" > </div> <div class='item-info'> <h3 class="item-name"> {{drink.name }} </h3> <div class='item-price'> <span>{{ drink.price }}円<br>(税込み)</span> </div> <div class='item-explain'> {{drink.explain}} </div> <div class='item-tag'> </div> <div class="like-and-comment"> <likeButton :drinkId=drink.id></likeButton> <i class="far fa-comment fa-lg"></i> <button v-on:click="show(drink)">詳細を表示</button> </div> </div> <modal v-bind:name="'display-drink-'+drink.id" height="1000px" styles="background-color: bisque"> <drinkShow :drink=drink></drinkShow> </modal> <div v-if="drink.body_id === 2" class="body-light"></div> <div v-else-if="drink.body_id === 3" class="body-medium"></div> <div v-else-if="drink.body_id === 4" class="body-full"></div> <div v-else class="body-nothing"></div> <div v-if="drink.acidity_id === 2" class="acidity-low"></div> <div v-else-if="drink.acidity_id === 3" class="acidity-medium"></div> <div v-else-if="drink.acidity_id === 4" class="acidity-high"></div> <div v-else class="acidity-nothing"></div> </li> </ul> </div> </div> </template> <script> import Vue from 'vue/dist/vue.esm.js' import VModal from 'vue-js-modal' import axios from 'axios'; import likeButton from '../like/likeButton.vue'; import drinkShow from '../drinks/show.vue'; Vue.use(VModal) export default { props: { user_id: { type: Number } }, components: { likeButton, drinkShow }, data: function(){ return { filtredDrinks: [], drinks: "drinks", userName: "" } }, created(){ this.setDrink(); }, methods: { setDrink: function(){ axios.get('/api/drinks') .then(response => { const drinks = response.data this.filtredDrinks = drinks.filter(drink => drink.user_id === this.user_id) }) }, show: function(drink) { this.$modal.show(`display-drink-${drink.id}`); }, hide: function () { this.$modal.hide(`display-drink-${drink.id}`); }, showTimeline: function(){ document.getElementById("timeline").style.visibility ="visible"; document.getElementById("title").style.visibility ="visible"; } }, } </script> <style scoped> .back-to-timeline{ color: white; background-color:#3c1900 ; padding: 5px; border-bottom: solid 4px black; border-radius: 3px; transition: background-color 5s ease-out; } .back-to-timeline:active{ -webkit-transform: translateY(4px); transform: translateY(4px);/*下に動く*/ box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.2);/*影を小さく*/ border-bottom: none; } .back-to-timeline:hover{ background: linear-gradient(270deg, black 0%, maroon 50%, #3c1900 100%); } .back-to-timeline a:visited{ color: white; background-color:#3c1900 ; padding: 5px; } </style> likeButton.vue <template> <div> <div v-if="isLiked" @click="deleteLike()"> <div class="iine__button"> <i class="fas fa-heart"></i> {{ count }} </div> </div> <div v-else @click="registerLike()"> <div class="iine__button"> <i class="far fa-heart"></i> {{ count }} </div> </div> </div> </template> <script> import axios from 'axios' // import { csrfToken } from 'rails-ujs' // // CSRFトークンの取得とリクエストヘッダへの設定をしてくれます // axios.defaults.headers.common['X-CSRF-TOKEN'] = csrfToken() export default { props: ["drinkId"], // user.idはdrinks/index.html.erbに定義 data(){ return{ likeList: [] // いいね一覧を格納するための変数 } }, computed: { // データが変更されるたび動く // ここではlikeListが変更される度に、count,isLikedが再構築される count(){ return this.likeList.length // いいね数を返す }, isLiked(){ // ログインユーザーが既にいいねしてるかを判定する if (this.likeList.length === 0){ return false} return Boolean(this.findLikeId()) } }, created: function(){ // vueインスタンスの作成、初期化直後に実行される this.fetchLikeByDrinkId().then(result =>{ console.log(result) this.likeList = result }) }, methods: { fetchLikeByDrinkId: async function(){ // async function() // jsの非同期処理 const response = await axios.get('/api/likes',{params: {drink_id:this.drinkId,user_id: user.id}}) // await // その投稿のいいね一覧を取得したい // もし処理が失敗したらプロセスから抜ける(処理をやめる?) return response.data }, registerLike: async function(){ // rails側のcreateアクションにリクエストするメソッド const response = await axios.post('/api/likes',{drink_id: this.drinkId,user_id: user.id}) this.fetchLikeByDrinkId().then(result => { this.likeList = result }) }, deleteLike: async function(){ // rails側のdestroyアクションにリクエストするメソッド const likeId = this.findLikeId() const response = await axios.delete(`/api/likes/${likeId}`,{params: {drink_id: this.drinkId,user_id: user.id}}) this.likeList = this.likeList.filter(n => n.id !== likeId) }, // ログインユーザーがいいねしているLikeモデルのidを返す findLikeId: function(){ const like = this.likeList.find((like) => { return (like.user_id == user.id) }) if (like) { return like.id } } } } </script> app.vueのコンポーネントのいいねボタンと、user.vueの子コンポーネントのいいねを同期させたい。 VueRouterでuser.vueを表示させてるから分からんけど、 app.vueが親、app.vueの中にあるrouter-linkで表示させたuser.vueが子、user.vue内でインポートしてるlikeButton.vueが孫って一旦仮定していいのかな。。。 そうね、app.vueが親、app.vueの中にあるrouter-linkで表示させたuser.vueが子っていう概念でいいっぽい。 もう一回整理すると、 app.vueの子コンポーネントであるlikeButton.vueのイベントと、 app.vueの子コンポーネントであるuser.vueの子コンポーネントであるlikeButton.vueの イベントを同期させたい。。。 なんて検索したらいいんだろう。。。 $emitはそのまま親コンポーネントのイベントを発火させるが、 親コンポーネントのなかの子コンポーネントのイベントを発火させたい。。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む