20210503のvue.jsに関する記事は8件です。

[Nuxt]slotの中でcomponentを呼び出す[vuetify]

結論:divで囲む 一応Vuetifyを使っていますが別に普通のslotでもいいと思います。 sample.vue <template> //コンポーネントは表示されない <v-radio value="ce" class="mb-2"> <template v-slot:label> <MyComponent /> //←コンポーネントを使いたい </template> </v-radio> //コンポーネントが表示された <v-radio value="ce" class="mb-2"> <template v-slot:label> <div><MyComponent /></div> //←<div>などで囲む </template> </v-radio>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[解決]Access to XMLHttpRequest at from origin has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

結論 RailsAPIとVue間でデータを共有しようとしたところエラー。API側のCORS設定がうまく行っていなかったことが原因だった。 詳しく エラーのコード Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins '*' resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] end end CORSが正常に機能したコード Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins 'http://localhost:8080', 'http://localhost:3002/v1/blogs/' resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] end end originを全て受け入れる(*)にすると、CORSのセキュリティ的にエラーが出るようになっているらしい。だから、クライアント側のoriginを明記すると、CORSが機能する。 参考 https://web.dev/cross-origin-resource-sharing/ https://qiita.com/att55/items/2154a8aad8bf1409db2b https://qiita.com/residenti/items/3a03e5e0268b354284b7
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue Routerで固定のパスの一部をparamsにしたい

動機 下記のようなroutesがあったとして、"/xxx/yyy"にマッチしたときにも$route.params.aaaに"yyy"が入ってきてほしい。 routes: [ {path: '/xxx/yyy', ...}, {path: '/xxx/:aaa', ...} ] 解決方法 routes: [ {path: '/xxx/:aaa(yyy)', ...}, {path: '/xxx/:aaa', ...} ] ":aaa"のようなパラメータを設定すると、その部分を"[^/]+"という正規表現に置き換えてマッチングするんですが、パラメータの後ろにカッコ付きで正規表現を指定してやると、その正規表現が"[^/]+"の代わりに使用されます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

さくっとNuxt.jsとApolloでGraphQLの雰囲気を掴むハンズオン

本記事の目的 Nuxt.jsとApollo Clientを用いたハンズオンを通してGraphQLの雰囲気を掴むことを目的としています。 バックエンドにはpokemon APIを活用します。 最終的には以下のようなポケモン図鑑が完成します。 (なおDeploy作業までは扱っておりません。) https://nuxt-graphql-demo.netlify.app/ 開発環境 macOS node v14.15.5 GraphQLとは API向けのクエリ言語 特徴として以下の点があります。 RESTでは複数のendipointが存在し、用途に応じてrequestを送り不必要なデータも含むresponseがあった反面、GraphQLでは単一のendpointに対して、欲しい情報を指定してrequest、responseを得ることができる。 request時に必要なresponseを指定することでフロントエンドでの開発が行いやすくなる 開発開始 プロジェクト作成  npx create nuxt-appコマンドにてプロジェクトを作成します。 npx create-nuxt-app nuxt-graphql-example 対話形式でNuxt.jsのアプリを立ち上げることができます。 今回は以下のようにオプションを選択します。 Programming languageはTypescript(以下TSと略)を選択していますが、当アプリで型を用いるメリットはほぼないため、Javascriptで開発を進めていきます。 また、UI構築を簡単にするためVuetifyを用います。結果的に工数をかなり減らすことができました。 ? Programming language: TypeScript ? Package manager: Yarn ? UI framework: Vuetify.js ? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert selection) ? Linting tools: (Press <space> to select, <a> to toggle all, <i> to invert selection) ? Testing framework: Jest ? Rendering mode: Universal (SSR / SSG) ? Deployment target: Server (Node.js hosting) ? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection) ? Continuous integration: None ? Version control system: Git 現時点でyarn devにてローカルサーバの立ち上げを確認できます。 Apollo Clientセットアップ GraphQLをもちいたrequestを送るためにApollo Clientを用います。 ライブラリをインストール yarn add @nuxtjs/apollo graphql-tag clientを読み込む設定をnuxt.config.jsに行っていきます。 // nuxt.config.js modules: [ '@nuxtjs/apollo', ], // Apollo module configuration apollo: { clientConfigs: { default: { httpEndpoint: 'http://localhost:4000', } } }, TS対応のためgql.d.tsを新規作成し、記述していきます。 // gql.d.ts declare module '*.gql' { import { DocumentNode } from 'graphql' const content: DocumentNode export default content } declare module '*.graphql' { import { DocumentNode } from 'graphql' const content: DocumentNode export default content } 実行するクエリを用意 プロジェクト配下にapollo/queriesディレクトリを新規作成し、クエリを用意します。 ├── apollo │   └── queries │   ├── pokemon.gql │   └── pokemons.gql pokemon.gql idを引数に該当するpokemonの情報を取得 $id:String!でString型で必ず引数を受け取る(Non-nullable) # pokemon.gql query pokemon($id: String!) { pokemon(id: $id) { name classification types resistant weaknesses evolutions { name id image } evolutionRequirements { name amount } image } } pokemons.gql $amount:Int!で取得するpokemonの種別数を必ずInt型で受け取る(Non-nullable) # pokemons.gql query pokemons($amount: Int!) { pokemons(first: $amount) { id name image } } UI構築(pages) pages配下にページネーション用のファイルを用意します。 ├── pages │   ├── index.vue │   ├── pokemon │   │   └── _id.vue │   └── pokemons.vue index.vue トップページ pokemons.vue ポケモン一覧ページ pokemon/_id.vue ポケモン詳細ページ index.vue サイトの説明ページです。 好みに合わせてカスタマイズしましょう。 <template> <v-container fill-height> <v-row justify="center" align-content="center" class=""> <v-col cols="12" align-self="auto"> <h1 class="text-center">This is the demosite by using Nuxt.js + GraphQL(Apollo)</h1> </v-col> <v-col cols="12" align-self="auto"> <h2 class="text-center">You can play the pokemomn picture book from sidebar menu</h2> </v-col> </v-row> </v-container> </template> pokemons.gql ポケモン一覧ページ <script>内でapolloobjectを用いることでgraphQLのrequestを行うことができます。 respponseはapolloObject内で宣言したpokemonsを値として使うことができます。 <template> <v-container fluid> <v-row dense> <v-col v-for="pokemon in pokemons" :key="pokemon.id" :cols="12"> <v-card> <NuxtLink :to="`pokemon/${pokemon.id}`"> <v-img :src="pokemon.image" class="white--text align-end" gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)" contain=true height="800" > <v-card-title v-text="pokemon.name"></v-card-title> </v-img> </NuxtLink> <!-- <v-card-actions> <v-spacer></v-spacer> <v-btn icon> <v-icon>mdi-heart</v-icon> </v-btn> <v-btn icon> <v-icon>mdi-bookmark</v-icon> </v-btn> <v-btn icon> <v-icon>mdi-share-variant</v-icon> </v-btn> </v-card-actions> --> </v-card> </v-col> </v-row> <br> <v-expansion-panels accordion> <v-expansion-panel> <v-expansion-panel-header>Show Query Result</v-expansion-panel-header> <v-expansion-panel-content> {{ pokemons }} </v-expansion-panel-content> </v-expansion-panel> </v-expansion-panels> </v-container> </template> <script> import "vue-apollo"; import pokemons from "~/apollo/queries/pokemons.gql"; // pokemonは最大151匹 let numGetPokemons = 151; export default { data() { return { pokemons }; }, // pokemon一覧を取得 apollo: { pokemons: { prefetch: "loading", query: pokemons, variables: { amount: numGetPokemons } } }, }; </script> pokemon/_id.vue ポケモン一覧ページのNuxtLinkを用いて動的にポケモン詳細ページを生成します。 レンダリング時にresponseのpokemonが取得しきれずエラーになるため、v-if="pokemon"にて取得が完了でき次第ページを表示するようにします。 prefetch: ({ route }) => ({ id: route.params.id })とvariables() {return { id: this.$route.params.id };}を用いることでpath内のポケモンidを取得して、pokemonクエリの変数として用います。 ※ 使用するコンポーネントsingleExplanation,multiExplanation,evolutionExplanationについては次章にて解説していきます。 <template> <div v-if="pokemon"> <v-container fluid> <v-img :src="pokemon.image" class="white--text align-end" gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)" contain=true height="800" > <v-card-title v-text="pokemon.name"></v-card-title> </v-img> <v-expansion-panels focusable> <!-- classification --> <single-explanation referKey="Classification" :referValue="pokemon.classification" /> <!-- types --> <multi-explanation referKey="Types" :referValue="pokemon.types" /> <!-- resistant --> <multi-explanation referKey="Resistant" :referValue="pokemon.resistant" /> <!-- weaknesses --> <multi-explanation referKey="Weaknesses" :referValue="pokemon.weaknesses" /> <!-- evolutions --> <evolution-explanation v-if="pokemon.evolutions" referKey="Evolutions" :referValue="pokemon.evolutions" /> <!-- evolutionRequirements --> <multi-explanation v-if="pokemon.evolutionRequirements" referKey="EvolutionRequirements" :referValue="pokemon.evolutionRequirements" /> <!-- レスポンス --> <v-expansion-panel> <v-expansion-panel-header>Show Query Result</v-expansion-panel-header> <v-expansion-panel-content class="justify-center"> {{ pokemon }} </v-expansion-panel-content> </v-expansion-panel> </v-expansion-panels> </v-container> </div> </template> <script> import pokemon from "~/apollo/queries/pokemon.gql"; import multiExplanation from "~/components/multiExplanation.vue"; import singleExplanation from "~/components/singleExplanation.vue"; import evolutionExplanation from "~/components/evolutionExplanation.vue"; export default { apollo: { pokemon: { query: pokemon, prefetch: ({ route }) => ({ id: route.params.id }), variables() { return { id: this.$route.params.id }; } } }, components: { multiExplanation, singleExplanation, evolutionExplanation } }; </script> UI構築(components) responseに対応してUIを表示するcomponentsを作成します。 singleExplanation,multiExplanation,evolutionExplanationコンポーネントを作成していきます。 ├── components │   ├── evolutionExplanation.vue │   ├── multiExplanation.vue │   └── singleExplanation.vue singleExplanation <template> <v-expansion-panel> <v-expansion-panel-header>{{ referKey }}</v-expansion-panel-header> <v-expansion-panel-content class="text-center"> {{ referValue }} </v-expansion-panel-content> </v-expansion-panel> </template> <script> export default { props: ["referKey", "referValue"] }; </script> multiExplanation <template> <v-expansion-panel> <v-expansion-panel-header>{{ referKey }}</v-expansion-panel-header> <v-expansion-panel-content v-for="(value, key) in referValue" :key="key" class="text-center"> {{ value }} </v-expansion-panel-content> </v-expansion-panel> </template> <script> export default { props: ["referKey", "referValue"] }; </script> evolutionExplanation NuxtLinkを用いて進化するポケモンへのリンクを貼ります。 <template> <v-expansion-panel> <v-expansion-panel-header>{{referKey}}</v-expansion-panel-header> <v-expansion-panel-content v-for="(value, key) in referValue" :key="key" class="text-center"> <NuxtLink :to="`${value.id}`"> {{value.name}} </NuxtLink> </v-expansion-panel-content> </v-expansion-panel> </template> <script> export default { props:[ 'referKey', 'referValue' ] }; </script> UI構築(その他) お好みでdefault.vueを修正してSidebarの項目やlayoutを整えます ├── layouts │   ├── default.vue │   └── error.vue <template> <v-app dark> <v-navigation-drawer v-model="drawer" :mini-variant="miniVariant" :clipped="clipped" fixed app > <v-list> <v-list-item v-for="(item, i) in items" :key="i" :to="item.to" :href="item.href" router exact > <v-list-item-action> <v-icon>{{ item.icon }}</v-icon> </v-list-item-action> <v-list-item-content> <v-list-item-title v-text="item.title" /> </v-list-item-content> </v-list-item> </v-list> </v-navigation-drawer> <v-app-bar :clipped-left="clipped" fixed app > <v-app-bar-nav-icon @click.stop="drawer = !drawer" /> <v-toolbar-title v-text="title" /> <v-spacer /> </v-app-bar> <v-main> <v-container> <nuxt /> </v-container> </v-main> <v-footer :absolute="!fixed" app > <span>&copy; {{ new Date().getFullYear() }}</span> </v-footer> </v-app> </template> <script> export default { data () { return { clipped: false, drawer: false, fixed: false, items: [ { icon: 'mdi-apps', title: 'Welcome', to: '/' }, { icon: 'mdi-format-list-bulleted', title: 'Pokemons', to: '/pokemons' }, { icon: 'mdi-code-tags', title: ' Github', href: 'https://github.com/kimkiyong0612/Nuxt-GraphQL-Demo' } ], miniVariant: false, right: true, rightDrawer: false, title: 'Nuxt.js GraphQL(Pockemon API) Demo' } } } </script> 結び 以上をもってポケモン図鑑を作成することができました。 あとはherokuやNetlifyにデプロイすることで0円でサイトを公開することができます。 (VercelではSSRのDeployがうまくいかなかったため、成功した方がいたら教えてもらえるとうれしいです。) おつかれさまでした。 Reference
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt.jsが流行っているらしいので使用してみた件

Nuxt.jsが流行っているらしいので使用してみた件 最近でもないのかな?JS由来のフレームワークやらライブラリやらがフロントエンドの主流になってきてるらしいですね。 なので今回、自分自身学習のアウトプットも兼ねてQiitaで情報発信していこうと思います。 不慣れなところもあるとは思いますが、何卒よろしくお願いします。 そもそもNuxt.jsってなんすか。 vue.jsの機能をふんだんに盛り込んだフレームワークという認識でいいと思います。 (SEO対策に強いとかSSRとかいろいろあるけど詳しくは公式ドキュメントを見てね) Nuxt.js公式サイト 実際に動かしてみよう。 Nuxt.jsで開発を進めていくにあたってNode.js環境が必須なので、インストールがまだの方はこちらの記事を参考にされてみてください。 本当の初心者のためのNode.js超入門 ~環境構築編~ インストールが終わったら、下記のコマンドをお手元のCLI(Mac:コンソール, Win:コマンドプロンプト)にて実行してみましょう。 Iterm2 npx create-nuxt-app <namespace> //namespaceは任意の文字 対話形式でいろいろ聞かれます。 プロジェクト名 使用する言語(TSは言語認識か微妙なところ)JS or TS Yarn or npm(高速なyarnが推奨!どうしてもnpmがいい人はそれでも問題はないです。) UIフレームワーク(個人的にはnoneでおk。後々でも追加はできます。) Axios(http通信)かPWA(モバイルapp)かCMS(WordPressなどをいじりたい人)か。Webappを作るならaxios推奨 Lint tool(コードを綺麗に整形してくれる便利なやつ。ESlintで問題ない。) Testing frameworkの選択 無難にjestにしましょう。 SSRかSPAか。個人的にはSSRがオヌヌメです。 Serverかstaticか。これはserverで問題ないです。staticを作りたくなったらそれ用のコマンドがあります。 あとの項目はEnter連打で大丈夫です。 Server Side Rendering(SSR)のメリット SEO対策に効果的! ページの表示が爆速! OGの設定が容易! Server Side Rendering(SSR)のデメリット サーバーへのデプロイがやや大変。(人によっては致命的) Single Page Application(SPA)のメリット 実装が容易 サーバーの準備が比較的簡単 ページ遷移が爆速 Single Page Application(SPA)のデメリット 初期表示が遅い。。。 OGの設定が詳細にできない。 などがありますので、これらを踏まえた上で選択してみてください。 ここまでくればやや時間を要しながら、appの作成が完了したと思います。 iTerm2 cd <namespace> //自分のappの名前 でディレクトリに入り、 iTerm2 yarn dev //npmの人は npm run dev で実行します。(正式には開発モードと言ってローカルで変更した値を即時にビルドして実行してくれる便利なやつ。) デフォルトではポート3000番が開かれてますのでwebブラウザからlocalhost:3000にアクセスしてみましょう。 しっかり表示されてますね。ひとまず安心。 作成したディレクトリに入ると様々なフォルダが作成されていると思います。 何が何だかわからなくて挫折してしまいそうになりますが、グッと堪えてざっくりと概要を理解していきましょう!! ディレクトリの紹介 .nuxt 自分たちが書いたコードをえっちらおっちら実行可能なコードに直してくれているところ。 assets 今後cssなどを入れるためのところ components vue.jsは基本的にコンポーネント(部品)で管理していきます。その部品を記述するところです。コンポーネント指向がいまいちわからないよ。という方向けに下に参考文献貼っておきます。 layouts ページ全体の共通部分をここに記述します。(eg. header footer) middleware レンダリングされる前に実行したい関数の置き場所(認証されてないユーザーへのlogin画面へのリダイレクトなど。) node_modules 先人たちのありがたい知恵が詰まった場所。yarn add xxxとかするとここに入るよ。 pages これが本当に便利。この中に新しくファイルを作ると、そこに勝手にパスを通してくれる。 plugins 全体を通して使用したい関数などを記述する。 static faviconや画像などをここに置いておく。 store アプリケーションを通してのデータの保持。login情報などに主に使用される。 test テストに関する記述がある。 参考文献: Vuejsのちょっと便利なコンポーネント機能 【Nuxt.js】 middleware is 何? Nuxt.jsのpluginsにaxiosの共通部品を実装する とまあこんな感じでゆるっと理解しておけば、いずれ役に立つかと思います。 こんな感じでどしどし開発を進めてみてください。 これから機能ごとに記事をいくつかアップしていこうと考えてます。 もしよければそちらも参考になさってください。 ご視聴ありがとうございました(_ _)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue-slick-carouselでautoplayをリセットする

vue-slick-carouselとは? Vueのカルーセルライブラリです。 autoplayの設定をすると、一定のタイミングで自動で送ることができます。 ※ 現在このライブラリーの開発は止まっていそうです。 やりたいこと autoplayを設定すると、一定のタイミングで自動で送ることができるようになります。また、矢印をクリックすることで手動でスライドを進めたり戻したりもできます。この自動送りと、手動送りは独立して動いているため、手動で戻してもすぐ次に送られてしまう問題が発生します。 これを防ぐのが今回の目的。 完成形 完成形 Components/Carousel.vueが今回のことを実装しているファイル。 Vue-slick-carouselでどうやって自動送り(autoplay)にするの? vue-slick-carouselのタグには設定を引数として渡せて、そこにautoplayを追加してあげれば自動送りになります。 <template> ... <VueSlickCarousel v-bind="setting"> ... </template> <script> ... export default { ... data() { return { setting: { autoplay: true, // <--ここ autoplaySpeed: 1000, ... }, }; }, ... </script> 今回の対応 スライドにフォーカスを合わせると(スライドを選択すると)、autoplayのタイマーをリセットするオプションがあり、今回はこれを利用します。下記手順で進めます。 1. フォーカスするとリセットするオプションをTrue 2. クリックしたらスライドにフォーカスしたような処理をする 1. フォーカスするとリセットするオプションをTrue setting: { autoplay: true, autoplaySpeed: 1000, pauseOnFocus: true, // <--ここをTrue ... }, settingに pauseOnFocusにtrueで設定するとスライドにフォーカスされるとautoplayのタイマーがリセットされます。正確には、タイマーのリセットだけでなく、一時的にautoplayが停止されます。 2. クリックしたらスライドにフォーカスしたような処理をする マウントしたタイミングでクリックイベントを登録します。 elementsのfocus()でスライドにフォーカスさせます。 mounted() { const elements = document.getElementsByClassName("slick-arrow"); Array.from(elements).forEach((element) => { element.addEventListener("click", function (e) { const sliderElement = document.getElementsByClassName("slick-slide"); if (sliderElement.length > 0) { sliderElement[0].focus(); sliderElement[0].blur(); } }); }); }, 今回、下記コードで複数枚あるスライド全ての情報が取得されます(1、2、3と表示されているスライド情報)。 const sliderElement = document.getElementsByClassName("slick-slide"); vue-slick-carouselの pauseOnFocusオプションでは いずれかのスライドにフォーカスされるとリセット処理が発動するので、今回は矢印をクリックされるとスライド[0]にフォーカスされるようにしました。 おわりに 実際には、完成形を見て確認してみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

$router.push()でqueryを変更したときに反映されない場合

ちょっとハマったのでメモ。 VueのRouterは変更を完全一致でチェックしているそうです。 例えば以下のようにソート条件を加えて新しいクエリストリングに変更してURLを変える場合。 sort() { const query = this.$route.query query.sort = this.sort this.$router.push({ path: '/search', query }) }, これだと反映されません。 なぜかとしうとシャローコピーで this.$route.query と query が同じ値になるからです。 これを回避するには sort() { const query = Object.assign({}, this.$route.query) query.sort = this.sort this.$router.push({ path: '/search', query }) }, してやると動きます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue × Firestore】ユーザーが投稿した記事のみをマイページに表示させる

ユーザーが投稿した記事のみをマイページに表示させる mypage.vue <h3 class="post-list flex">{{ profileData.name }} さんの投稿一覧</h3> <div class="profile-posts"> <List v-for="(list, index) in listData" :index="index" :list="list :key="list.id"/> </div> mypage.vue created() { firebase .firestore() .collection("posts") .where("uid", "==", this.$route.params.uid) //uidをフィルタリングして現在のURLと合致するもののみを参照 .get() .then(snapshot => { //"posts"(参照先)のスナップショットを得る snapshot.forEach(doc => { //上記で得たデータをforEachでドキュメントの数だけ"doc"データに格納 this.listData.push(doc.data()); console.log(doc.data()); //更にlistDataの空箱に格納した"doc"データを格納 }); }); }, キーワードは.where()を使うということです。 公式サイト:クエリ演算子 mypage.vue .where("uid", "==", this.$route.params.uid) .where()を使わずに.doc()取得をしようとしても.doc()の結果は単一のドキュメントになります。 その為、表示すらされません。 mypage.vue .doc(this.$route.params.uid) where()メソッドは、フィルタリングするフィールド("uid")、比較演算子("==")、値("this.$route.params.uid")の 3 つのパラメータを受け入れます。 フィルタリングをして決まったものだけを取得する時は.where()メソッドを使用しましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む