20210608のvue.jsに関する記事は7件です。

Rails API + Vue.js のSPAにCarrierWaveを導入する

はじめに CarrierWaveの導入はRailsアプリケーションでは簡単にできるのですが、SPAでは少し苦労したのでまとめておきます。 本記事では、ローカル(public/uploader以下)にファイルをアップロードするデフォルトの設定で進めます。 また、CarrierWaveの基本的な説明は省略します。 環境 Ruby 3.0.0 Rails 6.1.3 Vue.js 2.6.12 Vue CLI 4.5.1 CarrierWaveのインストール この記事ではJSONデータの作成にjbuilderを使うので、一緒にインストールしておきます。 Gemfile gem 'carrierwave' gem 'jbuilder' $ bundle install Railsの設定 $ rails g uploader Image $ rails g model Post image:string $ rails db:migrate app/models/post.rb mount_uploader :image, ImageUploader config/routes.rb Rails.application.routes.draw do scope format: 'json' do resources :posts end end app/uploaders/image_uploader.rbはデフォルト設定のまま使います。 ただし、このままファイルをアップロードしてVue.js側で取得するとURLは "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"となり表示できません。 なので、config/initializers/carrierwave.rbを作成して、 config/initializers/carrierwave.rb CarrierWave.configure do |config| config.asset_host = 'http://localhost:3000' end これで、"http://localhost:3000/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"となり表示できるようになります。 続いて、Postsコントローラーを作ります。 app/controllers/posts_controller.rb class PostsController < ApplicationController def index posts = Post.all @images = posts.map { |post| post.image.url } end def create @post = Post.new(post_params) if @post.save render json: :created else render json: @post.errors, status: :unprocessable_entity end end private def post_params params.require(:post).permit(:image) end end ポイントはpost.image.urlです。 Rails側でurlまで取得しておかないと、Vue.jsで表示したときにconsoleに警告が出ます。 app/views/posts/index.json.jbuilderを作成してRails側は完了です。 app/views/posts/index.json.jbuilder json.images do json.array! @images end Vue.jsの設定 まずは、投稿フォームを作ります。 <input type="file">ではv-modelは使えないので、@￰changeでファイルを取得します。 PostsNew.vue <template> <div> <input type="file" @change="setImage"> <button @click="postImage"> </div> </template> <script> import axios from '@/axios' export default { data () { return { image: '' }, methods: { setImage (e) { this.image = e.target.files[0] }, postImage () { const formData = new FormData() formData.append('post[image]', this.image) axios.post('/posts', formData) } } } </script> Rails側でStrong Parametersを設定しているので、パラメータを {"post" => {"image"=>"xxxxx/xxxx/xxxx"}} で渡すために、formData.append('post[image]', this.image)としています。 最後に投稿一覧を作成します。 PostsIndex.vue <template> <div> <div v-for="(image, index) in images" :key="index"> <img :src="image"> </div> </div> </template> <script> import axios from '@/axios' export default { data () { return { images: [] }, created () { axios.get('/posts').then(response => { this.images = response.data.images }) } } </script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vueで、データによってcolspan rowspanをbindしたり、不要なら何も挿入させなくする。

まぁv-bindを知っていれば、そんなの当たり前ですよねーって話ではあるんですが、検索してもうまく引っ掛けられなかったので、誰かの助けになれば・・・。 下記のようなデータがあって、urlがあるときは2つのセルを表示させるけれども、urlが空の場合はぶち抜きで表示させたい場合 test.json [ { "title": "ページA", "url": "https://example.com/page-a" }, { "title": "ページB", "url": "" }, { "title": "ページC", "url": "https://example.com/page-c" } ] はじめはこのように書いていたんですが ダメバターン <table> <tr v-for="(row, index) in data" :key="index"> <td :colspan="row.url ? 1 : 2">{{ row.title }}</td> <td v-if="row.url">{{ row.url }}</td> </tr> </table> これだと無駄に colspan="1" がtdの中に入ってイヤンな感じでした。 ダメパターンアウトプット <table> <tr> <td colspan="1">ページA</td> <td>https://example.com/page-a</td> </tr> <tr> <td colspan="2">ページB</td> </tr> <tr> <td colspan="1">ページC</td> <td>https://example.com/page-c</td> </tr> </table> なので、v-bindを使ってキーごと渡すようにすればいい感じに、不要なところにはcolspanがないテーブルが書き出せます。 いい感じバターン <template> <table> <tr v-for="(row, index) in data" :key="index"> <td v-bind="colspan(row.url)>{{ row.title }}</td> <td v-if="row.url">{{ row.url }}</td> </tr> </table> </tempate> .... <script lang="ts"> ... colspan(url: string) { return row.url === "" ? { colspan:2 } : {} } </script> いい感じパターンアウトプット <table> <tr> <td>ページA</td> <td>https://example.com/page-a</td> </tr> <tr> <td colspan="2">ページB</td> </tr> <tr> <td>ページC</td> <td>https://example.com/page-c</td> </tr> </table> もしtdにcolspanもrowspanもセットする必要がある場合は、同じノリで下記にように返せばOKです。 colspanもrowspanもセットしたい colspanRowspan() { return { colspan:2, rowspan: 2 } } まぁただのv-bindの使い方ではあるんですが・・・・。 v-bindなんてアレすぎるんでコレがおすすめです!とかあるとコメントもらえるとうれしいです!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue × Firestore】自動生成されるドキュメントIDをフィールドに保存する

自動生成されるドキュメントIDをフィールドに保存する 実際のコード saveBookmark() { const ref = firebase .firestore() .collection("users") .doc(this.$route.params.uid) .collection("bookmarks") .doc(); const id = ref.id; firebase .firestore() .collection("users") .doc(this.$route.params.uid) .collection("bookmarks") .doc(id) .set({ bookmarkId: id, ...this.list, time: firebase.firestore.FieldValue.serverTimestamp() }) .then(() => { this.$swal("ブックマークに追加しました。", { icon: "success" }); }) .catch(() => { this.$swal("ブックマークを追加出来ません。", { icon: "error" }); }); }, 以下のように変数(ref)に対象ドキュメントまでのパスを記述して、 改めて変数(id)に「ref.id」として代入する。 const ref = firebase .firestore() .collection("users") .doc(this.$route.params.uid) .collection("bookmarks") .doc(); const id = ref.id; あとはdoc()時に先ほど代入した「id」を入れて、set()を使って参照する。 ※ここで注意。set()を使う理由としてadd()の場合は、IDが自動生成されてしまうので 指定できるset()を使わなければならない。 指定したidを使って、以下のように削除を行える。 firebase .firestore() .collection("users") .doc(this.$route.params.uid) .collection("bookmarks") .doc(id) .set({ bookmarkId: id, ...this.list, time: firebase.firestore.FieldValue.serverTimestamp() }) .then(() => { this.$swal("ブックマークに追加しました。", { icon: "success" }); }) .catch(() => { this.$swal("ブックマークを追加出来ません。", { icon: "error" }); }); }, deleteBookmark() { firebase .firestore() .collection("users") .doc(this.$route.params.uid) .collection("bookmarks") .doc(this.list.bookmarkId) .delete() .then(() => { this.$swal("ブックマークを取り消ししました。", { icon: "success" }); this.$router.go({ path: `/bookmark/${this.$route.params.uid}`, force: true }); }) .catch(() => { this.$swal("ブックマークを取り消し出来ません。", { icon: "error" }); }); }, }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【vue.js】イベントを条件付きで発火させる

※この記事ではテンプレートについては全てpugで記載します。 コード button(@click='isClickable && clickEvent') 解説 isClickable が truthy なら clickEvent が発火します。 なぜここで論理式(&&)が出てくるのか?truthyとは何か?というのは以下の記事が大変参考になります。 参考: 3歳娘「いつから論理式が真偽値のみを返すと錯覚していた?」 ただ、button の @click の場合は以下のように disabled に条件を指定してやるほうが良いと思います。 button(@click='clickEvent', :disabled='!isClickable') 今回のような条件分岐は @mouseup など、少し特殊な用途で役に立つかもしれません。 参考 Conditional event binding - vuejs
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue v-slot での select が無効化されている問題解決

はじめに Vue.js でのフロントのスプレッドシート画面を開発する際に、cell 内の select が無効化されていることが大きな壁になりました。その課題は Vue.js での親コンポーネントの v-slot 部分に子コンポーネントの scopeId が継承できないという共通の課題であり、本文ではその課題の原因の考察と解決法を共有します。 Vue画面の仕組み <template> <app-layout> <v-spread v-model="dataset" :fields="fields" nameKey="apiName" :cellClass="cellClass" :cellReadonly="cellReadonly" ref="vspread" @mounted="onload" > <template v-slot:input="{field, item}"> <div> <select v-model="item[field.apiName]"> <option disabled value="">選択して下さい</option> <option v-for="option in options" :value="option.id"> {{ option.name }} </option> </select> </div> </template> </v-spread> </app-layout> </template> <script type="text/javascript" defer=true> import AppLayout from '@/Layouts/AppLayout' import VSpread from '@/Shared/VueSpread' export default { components: { AppLayout, VSpread }, } </script> <style scoped> </style> 以上はメイン画面の一部です。メイン画面では VSpread という子コンポーネントをインポートしています、template v-slot を利用し、子コンポーネントの cell 内に select を適用している仕組みになります。以上のコードで、メイン画面でスプレッドシートの cell 内選択ボックスが機能していると期待していますが、実際に選択ボックスが無効化されていることが判明しました。なぜそのようなことが起きましたのか、次は HTML element について考察してみます。 HTML elementの考察 <form id="form" data-v-40046a6d=""> <div data-v-b1b3ea5c=""> <select class="select" data-v-b1b3ea5c=""> <option disabled="" value="" data-v-b1b3ea5c="">選択して下さい</option> <option value="1" data-v-b1b3ea5c="">1</option> <option value="2" data-v-b1b3ea5c="">2</option> <option value="3" data-v-b1b3ea5c="">3</option> </select> </div> </form> 以上は HTML element の一部です。form は子コンポーネントに所属する element、select は親コンポーネントに所属する element 、以上のコードで、select element のscopeId と form element の scopeId が違うことが判明しました。その原因で select element が子コンポーネント VSpread に定義されている CSS style が適用されてなく、メイン画面で操作できなくなることが分かりました。では、なぜ template v-slot の部分に子コンポーネントの scopeId を継承できないだろうか。それを解明するために、 Vue の Scope CSS のメカニズムについて解説します。 vue-loader について 全ての.vue ファイルに対して、最初は vue-loader によって処理されます。スコープ CSS に対して、簡単にまとめると、vue-loader は次の3つのことを行います。 コンポーネントを分析し、template、script、style に対応しているコードを抽出します。 コンポーネントのインスタンスを構築し、インスタンスに scopeId をバインドします。 scopeId を使用してセレクターの属性に設定し、style の CSS コードをコンパイルします。 vue-loader から scopeId の生成する方法が分かったことで、scopeId の継承とどんな関係があるだろうか? scopeId の継承順 みんな分かっていることと思いますが、Vue の Scope CSS に対して、子コンポーネントの root element は必ず親コンポーネントから scopeId を継承するという仕組みがあります。この点から、上記のサンプルに戻ると、.vue ファイルはメイン画面の.vue ファイル -> 子コンポーネントの VueSpread.vue という順番で解析されることが分かりました。 つまり、vue-loader がメイン画面の.vue ファイルをコンパイルする時点では、子コンポーネントはまだ解析されていない、子コンポーネントの scopeId はまだ生成されていない状態なので、メイン画面での template v-slot に子コンポーネントの scopeId を渡すには不可能なことである。 自動的に scopeId を追加できないことが判明したことで、問題解決するためには手動で select に scopeId を追加しなければなりません。ここでは2つの方法を紹介します。 setAttribute で scopeId 属性を追加。 scopeId 属性を追加する前に、先ずは子コンポーネントの scopeId を取得しなければなりません。ここでは子コンポーネントの mounted() に emit を追加する方法で取得します。mounted() と emit について具体的にはここに参照してください。 <template> <app-layout> <v-spread v-model="dataset" :fields="fields" nameKey="apiName" :cellClass="cellClass" :cellReadonly="cellReadonly" ref="vspread" @mounted="onload" > <select ref="myselect" v-model="item[field.apiName]"/> </v-spread> </app-layout> </template> <script type="text/javascript" defer=true> import AppLayout from '@/Layouts/AppLayout' import VSpread from '@/Shared/VueSpread' export default { components: { AppLayout, VSpread }, data: () => ({ scopeID: '', }), methods: { onload() { let vspread = this.$refs.vspread; let app = vspread.$refs.app this.scopeID = app.attributes[1].name let select = this.$refs.myselect select.setAttribute(this.ScopeID,'') }, } } </script> <style scoped> </style> 以上のような子コンポーネントが mounted した時点でトリガーを送信し、onload() メソッドで attributes[1].name で子コンポーネントの scopeID を取得し、 select.setAttribute で select に子コンポーネントの scopeID を追加します。 動的属性で scopeID を追加 Vue のライフサイクルの関係で、子コンポーネントの mounted 完成した時点でも親コンポーネントでは、特定な DOM element はまだ作成されていない可能性があるので、その場合 setAttribute が使えなくなり、その時に対応できる方法としては動的属性で scopeID を追加することです。 <template> <app-layout> <v-spread v-model="dataset" :fields="fields" nameKey="apiName" :cellClass="cellClass" :cellReadonly="cellReadonly" ref="vspread" @mounted="onload" > <template v-slot:input="{field, item}"> <div> <select v-bind:[scopeID]="scopeID" v-model="item[field.apiName]"> <option disabled value="">選択して下さい</option> <option v-for="option in options" :value="option.id"> {{ option.name }} </option> </select> </div> </template> </v-spread> </app-layout> </template> <script type="text/javascript" defer=true> import AppLayout from '@/Layouts/AppLayout' import VSpread from '@/Shared/VueSpread' export default { components: { AppLayout, VSpread }, data: () => ({ scopeID: '', }), methods: { onload() { let vspread = this.$refs.vspread; let app = vspread.$refs.app this.scopeID = app.attributes[1].name }, } } </script> <style scoped> </style> 上と同じような方法で子コンポーネントの scopeID を取得しています、こちらは select に v-bind:[scopeID]="scopeID" で動的属性 scopeID を設定しています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue-chartjsとchart.jsを入れたらエラーが出たのでバージョンを下げた

(The sky is the limitさんの記事で解消できました。大感謝です。) ①何をしたらエラー解消したか 結論:バージョンを下げた。 グラフを実装してもコンポーネントは表示されず、検証ツールでエラーになってしまう。 どうやらchart.jsのバージョンが問題らしい。 以下の手順で無事にエラー解消して表示されました。 1 . vue-chartjsとchart.jsをアンインストール yarn remove chart.js yarn remove vue-chartjs 2 . バージョンを指定して、再インストール yarn add chart.js@2.8 yarn add vue-chartjs@3.4.2 これでエラーが消えて正常に表示された。 ②もう少し調べると・・・ chart.js @2.9.4だとダメで、@2.9.3だと動いたってこともあるみたい。 バージョンって難しい。 ③参考にしたサイト The sky is the limit isoroot(開発者ブログ)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js入門

始めに Vue.jsの基礎をまとめました。 初学者の方でも分かるように書いていますので、よければ参考にしてください! 参考記事 [URL]https://qiita.com/kiyokiyo_kzsby/items/ce9fe8b72953584fecee 環境 JSFiddle(HTML・CSS・JavaScriptを簡単に試せるオンラインIDE) Viuインスタンスの生成 ①  html側にVueを適応したい箇所に<div id="id名"></div>を記載する。(jsffidleでは、headやbodyタグがあらかじめ読み込まれているので、ここでは記載していない。) ② divタグ外に<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>と記載する。 これによりVue.jsを使用できるようになる。 詳細は公式ホームページ参照 https://jp.vuejs.org/v2/guide/installation.html#CDN ③new VueでVueインスタンスを生成し el でhtml側のどの部分でVueを適用するのか指定できます。 html <div id='app'> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> js new Vue ({ el: '#app' }) 変数 data jsに以下のように記述することで、data要素に変数を定義することができます。 html側に{{変数名}}とすることで、変数を表示できます。※{{}}はマスタッシュ構文 html <div id='app'> {{ message }} </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> js new Vue ({ el: '#app', data: [ message: '初めてのVue' ] }) ディレクティブ v-on  任意のイベントによってメソッドを実行させる Vueインスタンスにmethodsを記載することで、メソッドを使用できます。 ①html側にbuttonタグを配置し、v-on:アクション="アクション名"のようにv-onディレクティブを指定する。 これによりボタンをクリックした際に、②で作成するmethodsのbuttonメソッドを実行させることができる。 ②methods: {~~~}以下を記述することで、buttonメソッドを作成する。 ここではメソッドが実行された際、alertを表示させるようにしている。 html <div id='app'> {{ message }} <button v-on:click="button"> ここをクリック </button> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> js new Vue ({ el: '#app', data: [ message: '初めてのVue' ], methods: { button: function { alert(); } } }) v-vind タグ属性の値をVueインスタンス内で定義した変数で表現する際に使います。 ①html側に、aタグを配置しv-bindディレクティブを指定する。(v-bind:属性="属性名") ②js側で、data内に変数url(属性名)を記述する。 ③リンクをクリックすると、任意のページを表示する。 html <div id='app'> <p><div id='app'> {{ message }} <button v-on:click="button"> ここをクリック </button> <a v-bind:href="url">Vue公式</a> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> js new Vue ({ el: '#app', data: [ message: '初めてのVue', url: 'https://jp.vuejs.org/v2/guide/installation.html#CDN' ], methods: { button: function { alert(); } } }) v-model 双方向データバインディング。 双方向データバインディングとはvue.jsの機能で、data変数とフォームの値を連動させることが出来ます。 v-bindでは変数を表示させるだけでしたが、v-modelではフォームの値を変更すると変数の値も変更されます。 ① html側で、<input>タグを記述しv-modelディレクティブを指定する。(v-model="変数名") v-modelの動作を確認する為、hello変数を{{ hello }}で表示させる。 ②js側で、hello変数を記述する。 ③フォームの値を変更すると、hello変数の値が変化していることが確認できる。 html <div id='app'> <input v-model="hello"> {{ hello }} </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> js new Vue ({ el: '#app', data: [ hello: 'こんにちは' ], }) v-for 配列を要素にマッピングする。 v-forはdata内で、記述した配列よhtml側で表示させることができます。 ①js側で、languages配列を作成する。 ②ulタグ内にliタグを作成する。 liタグにv-forディレクティブを指定する。(v-for="要素 in 配列名") ③<li v-for="la in languages"></li>内に{{ la }}と記述して配列の要素の表示させる。 html <div id='app'>  <ul> <li v-for="la in languages">{{ la }}</li> </ul> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> js new Vue ({ el: '#app', data: [ languages: ['Ruby', 'PHP', 'Python'], ], methods: { button: function { alert(); } } }) 終わりに Vue.jsの基礎をまとめました。 次回は一歩進んだ内容をまとめるので良ければ見てください!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む