20210907のvue.jsに関する記事は4件です。

Vue3でprovide/injectを使用しているときのVue Test Utilsでのテストの書き方

結論 Vue Test Utils 2 ではglobalオプションを使ってprovideするよ v2 の provide/inject に関する変更 変更点 provideオプション → 廃止 globalオプション → 追加 v1ではマウンティングオプションのprovideを使用していた。 しかし、v2ではprovideオプションがなくなり、代わりにglobalオプションが用意された。 新しいprovide方法 Before const wrapper = mount(Component, { provide: { Theme: 'dark' } }) After const wrapper = mount(Component, { global: { provide: { Theme: 'dark' } } }) 詳しいことは ↓ を参照
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

テキストファイルを出力する方法

function downLoadTXT(text, filename) { // TXTをダウンロードする let blob = new Blob([text], {type: "text/plain"}); let link = document.createElement("a"); // aタグのエレメントを作成 link.href = window.URL.createObjectURL(blob); link.download = filename; link.click(); }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IBM Cloud App ID+Vue.js 3+Quarkusで、認証付きSPAを作る

はじめに 当記事では、IBM Cloudにおける認証・認可サービスである「App ID」を利用し、Vue.js 3をフロントエンド、QuarkusによるREST APIをバックエンドとした認証付きSPAを作成します。 凝った認証・認可の仕組みを自前で作成することは非常に難しく、専用のミドルウェアを導入するか、Auth0やFirebase Authenticationといったインターネット上のサービスを利用する方が容易であり、かつ、セキュアに実装することが可能です。 App IDもAuth0などと同様のサービスで、少ないコードでWebアプリやAPIに認証・認可を追加することが可能になっています。App ID上に独自のユーザ(メールアドレス、ユーザ名、パスワード)を準備・利用することもできますし、GoogleやFacebookによる認証を利用することもできます。ログイン画面を自分で作成する必要は無く、App IDが提供してくれます(ログイン画面のカスタマイズも可能)。また、クライアント側、サーバ側それぞれのSDKが提供されており、実装も容易となっています。 App IDでのリソースの保護方式は、認証フロー別に「シングルページ」、「Web」、「バックエンド」などがあります。また、Kubernetes環境であれば、アプリケーションに組み込まずに、「Ingress」や「Istio」でリソースを保護することも可能になってます。 当記事では、Vue.js 3で作成したSPAを「シングルページ」で保護し、SPAの認証・認可で取得したアクセストークンを用いて、「バックエンド」で保護されたQuarkus製のREST APIにアクセスするという構成を取ります(下記の図を参照)。 注意事項 「シングルページ」では、「アプリのために管理しているバックエンドがありますか? その場合、SPA のフローは適していません。 Web アプリのフローを試してください。」と記載されており、上記のようにSPAで取得したアクセストークンを利用してAPIにアクセスすることは推奨されていないと読み取れます。 当初は「Web」方式(OpenID Connect 認可コードフロー)での実装を試みたのですが、Vue + Quarkusでの実現方法が不明で、サーバ側のSDKも提供されていなかったため、上記の構成を取っています。 また、後述しますが、App ID上にアプリケーションを2つ(SPA、通常のWebアプリ)を定義するという変則的な手段を取っているため、ベストな方法ではないかもしれません。 前提環境 Maven 3.8.1 Quarkus 2.2.1 Node.js 12.14.1 npm 6.13.4 Vue CLI 4.5.13 Vue.js 3.x App IDのインスタンス作成、初期設定 App IDのインスタンス作成ページにアクセスします。 ロケーションで「東京(jp-tok)」を、料金プランで「段階的層」を選択し、「作成」ボタンを押下します。 作成されたApp IDインスタンスを選択し、「認証の管理」をクリックします。 どのIDプロバイダーを有効にするかという画面で、Facebook、Google、匿名を「無効」にします(初期状態では有効です)。今回、あらかじめ作成しておいたユーザ・パスワードでのみ認証・認可を可能としたいため、Googleなどは無効にします。 「認証設定」タブをクリックし、認証後にリダイレクトさせる(リダイレクトを認める)URLとして「http://localhost:8888/*」を追加します。 画面左側の「クラウド・ディレクトリー」->「設定」をクリックし、「ユーザーがサインアップとサインインに使用する情報」を「ユーザー名とパスワード」、「ユーザーがアプリケーションにサインアップできるようにする」を「いいえ」に設定します。 次に、認証する対象のユーザを作成します。画面左側の「クラウド・ディレクトリー」->「ユーザー」をクリックし、「ユーザーの作成」ボタンをクリックします。 ユーザーの名前、ユーザー名などを入力する画面が表示されますので、適当に入力して「保存」ボタンをクリックします。 画面左側の「アプリケーション」をクリックし、「アプリケーションの追加」ボタンをクリックします。 「名前」に任意の名前を入力し、「タイプ」を「単一ページ・アプリケーション」とし、「保存」ボタンをクリックします。 同じ流れで、今度は「タイプ」を「通常のWebアプリケーション」とし、「保存」ボタンをクリックします。 「単一ページ・アプリケーション」と「通常のWebアプリケーション」の2つのアプリケーションを作成した後、画面に表示された各アプリケーションを展開し、以下の値をメモしておきます(後ほど使います)。 単一ページ・アプリケーション clientId dicoveryEndpoint 通常のWebアプリケーション clientId tenantId secret oAuthServerUrl バックエンドAPI(Quarkus)の作成 次にQuarkusでバックエンド(サーバ側)のAPIを作成していきます。 まず以下のコマンドを実行し、APIの雛形を作成します。エクステンションとして、OpenID Connectを扱うための"oidc"を含めます。 mvn io.quarkus.platform:quarkus-maven-plugin:2.2.1.Final:create \ -DprojectGroupId=org.appidsample \ -DprojectArtifactId=appid-sample-backend \ -Dextensions="resteasy,oidc,resteasy-jackson" \ -DnoExamples appid-sample-backendというディレクトリが作成され、その下にQuarkusのプロジェクト構造・設定ファイルなどが配置されます。その中のGreetingResourceクラスを編集します。 このクラスは、/helloエンドポイントを提供するAPIになります。APIはApp IDにより保護され、適切なアクセストークンが付与されていなければ401 Unauthorizedを返すようにします(このクラスではその設定は無く、後述の設定ファイルで設定します)。 適切なアクセストークンが付与されてAPIが呼び出された場合、JsonWebToken accessTokenにはそのアクセストークンが自動的に格納されます。また、UserInfo userInfoには、App IDで管理されているユーザ情報(ユーザ名、メールアドレスなど)が自動的に格納されます。 hello()メソッドでは、上記のアクセストークン、ユーザ情報の扱い方のサンプルを記述し、returnとしてそれぞれの情報を組み立てた文字列を返すようにしています。 appid-sample-backend/src/main/java/org/appidsample/GreetingResource.java package org.appidsample; import io.quarkus.oidc.UserInfo; import org.eclipse.microprofile.jwt.JsonWebToken; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import java.util.Date; @Path("/hello") public class GreetingResource { @Inject JsonWebToken accessToken; @Inject UserInfo userInfo; @GET @Produces(MediaType.TEXT_PLAIN) public String hello() { String accessTokenString = this.accessToken.getRawToken(); String name = this.userInfo.getString("name"); String email = this.userInfo.getString("email"); String preferredUsername = this.userInfo.getString("preferred_username"); Date date = new Date(); System.out.println(date + " : " + name + ", " + email + ", " + preferredUsername + ", " + accessTokenString); return "Hello RESTEasy : " + date + ", " + name + ", " + email + ", " + preferredUsername; } } application.propertiesは、Quarkus全体の挙動を設定するための設定ファイルになります。各設定項目の意味・設定内容はコメントに記載の通りです。 appid-sample-backend/src/main/resources/application.properties # OpenID ConnectサーバのURL。後述の環境変数から取得して設定 quarkus.oidc.auth-server-url=${AUTH_SERVER_URL} # アプリケーションのclientId quarkus.oidc.client-id=${CLIENT_ID} # テナントID quarkus.oidc.tenant-id=${TENANT_ID} # アプリケーションのタイプ。web-app, service, hybridのいずれかを設定する。今回はバックエンドAPIのためserviceを設定 quarkus.oidc.application-type=service # 前述の UserInfo userInfo にユーザ情報を自動的に設定するためには、この設定を true にする必要がある。2021/09/06時点のドキュメント(https://ja.quarkus.io/guides/security-openid-connect#user-info) では「quarkus.oidc.user-info-required=true を設定します」と誤ったプロパティ名が記載されているため注意が必要。 quarkus.oidc.authentication.user-info-required=true # リソース保護の対象のパスを指定。値を「/*」とすることで全リソースを対象としている。 quarkus.http.auth.permission.authenticated.paths=/* # リソース保護のポリシー。値を「authenticated」とした場合は保護の対象となり、認可されなければアクセスできない。 quarkus.http.auth.permission.authenticated.policy=authenticated # CORSフィルターの有効・無効。true とすることで有効にしている。 quarkus.http.cors=true # CORSで許可するオリジン。「*」にしているため全て許可(そのため無効状態と同じであるが、将来的に有効にする場合は適切な内容を設定する)。 quarkus.http.cors.origins=* App IDのインスタンスの設定でメモしておいた値を環境変数として設定します。 export AUTH_SERVER_URL=<「通常のWebアプリケーション」のoAuthServerUrl> export CLIENT_ID=<「通常のWebアプリケーション」のclientId> export TENANT_ID=<「通常のWebアプリケーション」のtenantId> export SECRET=<「通常のWebアプリケーション」のsecret> 以下のコマンドで、バックエンドAPIのアプリを起動します。 mvn clean compile quarkus:dev バックエンドAPIへのアクセスを確認するためにアクセストークンが必要となります。以下のコマンドを実行し、App IDからアクセストークンを取得します。アクセストークンの取得は、$AUTH_SERVER_URL'/token'にPOSTリクエストを投げることで取得できます。その際の認証として、「$CLIENT_ID:$SECRET」をBase64エンコードした値を渡す必要があります($CLIENT_IDと$SECRETをそれぞれBase64エンコードするのではなく、コロンで連結した値をBase64エンコードします)。また、username、passwordとして、App IDで作成したユーザのユーザ名、パスワードを渡します。 export access_token=$(curl -w "\n" -X POST $AUTH_SERVER_URL'/token' -H 'Authorization: Basic '`echo -n $CLIENT_ID':'$SECRET | base64` -H 'Accept: application/json' -F 'grant_type=password' -F 'username=<作成したユーザのユーザ名>' -F 'password=<作成したユーザのパスワード>' | jq --raw-output '.access_token') アクセストークンが環境変数に設定されたので、以下のcurlコマンドを実行し、Bearer認証でバックエンドAPIにアクセスします。 curl -w "\n" -v http://localhost:8080/hello -H "Authorization: Bearer "$access_token バックエンドAPIへのアクセスに成功した場合、以下のレスポンスが得られるはずです。 Hello RESTEasy : Mon Sep 06 11:55:21 JST 2021, <ユーザの姓・名>, <メールアドレス>, <ユーザ名> フロントエンド(Vue)の作成 最後にフロントエンドをVueで作成していきます。まずはVue CLIでアプリケーションの雛形を作成します。 vue create appid-sample-frontend Vue CLIの各選択肢では以下の通り選択します。Vueのバージョンは3.xを選択します。また、Vue Router、Vuexを使用します。 Vue CLI v4.5.13 ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, Router, Vuex, Linter ? Choose a version of Vue.js that you want to start the project with 3.x ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes ? Pick a linter / formatter config: Basic ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, ESLint, etc.? In package.json ? Save this as a preset for future projects? (y/N) N 作成されたアプリケーションのディレクトリに移動し、ibmcloud-appid-jsをnpmでインストールします。 cd appid-sample-frontend npm install ibmcloud-appid-js これでフロントエンドアプリ作成の準備ができました。最終的なアプリケーションのファイル構成は以下のようになります。ここからは、キーとなるファイルについてその内容を説明していきます。 フロントエンドアプリのポートはデフォルトでは8080ですが、バックエンド(Quarkus)のポートとかぶるため、vue.config.jsでフロントエンドのポートを8888に変更します。 appid-sample-frontend/vue.config.js module.exports = { devServer: { port: 8888, disableHostCheck: true } }; App ID関連の設定値は環境変数として定義します。.envファイルを作成し、そこに記述します。Vue CLIで環境変数を扱う場合、項目名をVUE_APP_で始める必要があるため、以下のような内容で定義します。 appid-sample-frontend/.env VUE_APP_APPID_CLIENT_ID=<「単一ページ・アプリケーション」のclientId> VUE_APP_APPID_DISCOVERY_ENDPOINT=<「単一ページ・アプリケーション」のdicoveryEndpoint> また、ローカル環境とクラウドなどのサーバ環境で、バックエンドAPIのURLが異なってきます。そのため、.env.developmentにはローカル環境での設定を、.env.productionにはサーバ環境での設定を記述します。.env.developmentを適用する場合、yarn serve --mode developmentのように、モードを指定して起動します。 appid-sample-frontend/.env.development NODE_ENV=development VUE_APP_API_SERVER_URL=http://localhost:8080 appid-sample-frontend/.env.production NODE_ENV=production VUE_APP_API_SERVER_URL=<サーバデプロイ時のURL> フロントエンドでログインした後、リロードしてもログイン状態を維持したいため、Vuex、および、vuex-persistedstate を使用します。npmでvuex-persistedstateをインストールします。 npm install vuex-persistedstate Vuexを使用してユーザ情報を定義します。ログイン済みかどうか、名前、メールアドレス、アクセストークンを保持します。pluginsの定義でvuex-persistedstateを使用し、SessionStorageにデータを格納するようにします。なお、アクセストークンをSessionStorageに格納することはリスクがあるため、本番のアプリケーションでは検討が必要です。 appid-sample-frontend/src/store/index.js import { createStore } from 'vuex' import createPersistedState from 'vuex-persistedstate' export default createStore({ state: { user: { isLogin: false, name: "", email: "", accessToken: "" } }, mutations: { login(state, user) { state.user = user; }, logout(state) { state.user.isLogin = false; state.user.name = ""; state.user.email = ""; state.user.accessToken = ""; } }, actions: { }, modules: { }, plugins: [ createPersistedState({ key: 'app1', storage: window.sessionStorage }) ] }) 次に、各ページのコンポーネントを整備していきます。App.vueはVue CLI作成時に作成されるファイルで、各ページへのナビゲーションリンク、コンポーネント表示を定義しています。デフォルトではHome、Aboutの2ページですが、ここにログインが必要となるUserページへのリンクを新たに追加します。 appid-sample-frontend/src/App.vue <template> <div id="nav"> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link> | <router-link to="/user">User(要ログイン)</router-link> </div> <router-view/> </template> (後略) Homeページに、フロントエンド側でのログインボタンを追加します。未ログイン状態ではログインボタンを表示し、ログイン済み状態ではログアウトボタン、ユーザ名、メールアドレスを表示します。 ログイン処理は、ibmcloud-appid-jsのApp ID用SDKでほぼ全て処理してくれるため、App IDのシングルページ・アプリのドキュメントのコードを参照し、appId.signin()関数を用いて記述します。 また、appId.init()関数に渡す設定値は、上記で定義した環境変数から取得します。 ※以下のコードでは確認のためアクセストークンなどをconsoleに出力していますが、本来は不要ですので適宜削除してください。 appid-sample-frontend/src/views/Home.vue <template> <div id="welcome"> <p>ようこそ。</p> <button id="login" v-if="!this.$store.state.user.isLogin" v-on:click="onLoginButtonClick">Login</button> <button id="logout" v-if="this.$store.state.user.isLogin" v-on:click="onLogoutButtonClick">Logout</button> <div v-if="this.$store.state.user.isLogin"> <p>{{ this.$store.state.user.name }}</p> <p>{{ this.$store.state.user.email }}</p> </div> </div> </template> <script> import AppID from 'ibmcloud-appid-js'; const appID = new AppID(); const config = { clientId: process.env.VUE_APP_APPID_CLIENT_ID, discoveryEndpoint: process.env.VUE_APP_APPID_DISCOVERY_ENDPOINT }; (async () => { try { await appID.init(config); } catch (e) { console.log(e); } })(); export default { name: 'Home', data: () => ({ } ), methods: { async onLoginButtonClick() { const tokens = await appID.signin(); let userInfo = await appID.getUserInfo(tokens.accessToken); let name = userInfo.name; let email = userInfo.email; let accessToken = tokens.accessToken; let accessTokenPayload = tokens.accessTokenPayload; let idToken = tokens.idToken; let idTokenPayload = tokens.idTokenPayload; const user = { isLogin: true, name: name, email: email, accessToken: accessToken } this.$store.commit("login", user) console.log("accessToken=" + accessToken); console.log("accessTokenPayload=" + accessTokenPayload); console.log("idToken=" + idToken); console.log("idTokenPayload=" + idTokenPayload); console.log("userInfo=" + JSON.stringify(userInfo)); }, onLogoutButtonClick() { this.$store.commit("logout"); } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> div#welcome { margin-right: auto; margin-left: auto; text-align: center; } </style> ページごとにログイン状態を判断して表示・非表示を切り替えるだけでなく、未ログイン状態で保護されたページにアクセスした場合にログインモーダルを表示させる処理をVue Routerのファイルに記述します。/userのリンクをクリックした場合に、requireAuth()関数を呼び出し、ログイン状態をチェックするようにします。未ログインの場合、先ほどと同様にibmcloud-appid-jsのApp ID用SDKを用いてログインモーダルを表示してログインを強制します。 appid-sample-frontend/src/router/index.js import { createRouter, createWebHistory } from 'vue-router' import Home from '../views/Home.vue' import User from '../views/User.vue' import store from '../store' import AppID from 'ibmcloud-appid-js'; const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') }, { path: '/user', name: 'user', beforeEnter: requireAuth, component: User }, ] const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes }) export default router const appID = new AppID(); const config = { clientId: process.env.VUE_APP_APPID_CLIENT_ID, discoveryEndpoint: process.env.VUE_APP_APPID_DISCOVERY_ENDPOINT }; (async () => { try { await appID.init(config); } catch (e) { console.log(e); } })(); async function requireAuth(to, from, next) { if (store.state.user.isLogin) { next() } else { const tokens = await appID.signin(); let userInfo = await appID.getUserInfo(tokens.accessToken); let name = userInfo.name; let email = userInfo.email; let accessToken = tokens.accessToken; const user = { isLogin: true, name: name, email: email, accessToken: accessToken } store.commit("login", user); next() } } 最後にUserページのコンポーネントです。このページはログイン済みの状態で遷移することを前提としているため、ユーザ名・メールアドレスを(場合分けなしで)表示します。また、バックエンドAPIを呼び出す「hello」ボタンを設置し、callHelloAPI()関数を呼び出すようにします。この関数では、保持しているユーザのアクセストークンをヘッダに指定し、axiosでバックエンドAPIをコールし、レスポンスを画面に表示します。 appid-sample-frontend/src/views/User.vue <template> <div class="user"> <h1>This is a login user page</h1> <h2>name: {{ this.$store.state.user.name }}</h2> <h2>email: {{ this.$store.state.user.email }}</h2> <p><button v-on:click="callHelloAPI">hello</button></p> <p>{{ message }}</p> </div> </template> <script> import axios from 'axios' export default { name: 'User', data: () => ({ message: "" }), methods: { callHelloAPI() { axios.get(process.env.VUE_APP_API_SERVER_URL + '/hello', { headers: { Authorization: "Bearer " + this.$store.state.user.accessToken }}) .then(response => ( this.message = response.data )) } } } </script> ここまでできたらフロントエンドアプリを起動します。developmentモードで起動することで、ローカル環境のバックエンドAPIを呼び出すようになります。 yarn serve --mode development バックエンドが起動していない場合、前述のmvnコマンドで起動させます。フロントエンド・バックエンドともに起動した状態で、ブラウザで「http://localhost:8888/ 」にアクセスします。「Login」ボタンをクリックしてログインします。 「Login」ボタンをクリックすることで以下の画面が表示されます。この画面は、App IDで提供されているログイン画面になります。Username、Passwordを入力してSign inします。 ログイン状態になったため、ユーザ名・メールアドレスが表示されます(メールアドレスは黒塗りにしています。また、スクリーンショットの取得の都合上、ユーザの姓・名はここまでのスクリーンショットのものと異なります)。ここで、「User(要ログイン)」リンクをクリックします。 既にログイン状態のため、Userページに何事もなく遷移できます。「hello」ボタンをクリックし、バックエンドAPI(/hello)を呼び出します。 バックエンドAPI(/hello)から返ってきたレスポンスデータが正常に表示されています。 以上のように、App IDを用いることで、認証・認可の処理を少ないコードで実現することができました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#1 VueRouterを用いて部分SPA化させる方法

app.vueに投稿一覧があり、その投稿の中にあるユーザーの名前をクリックしたらユーザーのページに遷移させるようにしたい。 以前静的なページをVueRouterを用いてそんな感じのことをやったので、その動的なページ版。 router.js import Vue from 'vue/dist/vue.esm.js' import VueRouter from 'vue-router' import Contact from '../components/contact.vue' import About from '../components/about_coffee_passport.vue' Vue.use(VueRouter) export default new VueRouter({ mode: 'history', routes: [ { path: '/contact', component: Contact }, { path: '/about_coffee_passport', component: About} ], }) before router.js import Vue from 'vue/dist/vue.esm.js' import VueRouter from 'vue-router' import Contact from '../components/contact.vue' import About from '../components/about_coffee_passport.vue' import User from '../components/users/user.vue'; Vue.use(VueRouter) export default new VueRouter({ mode: 'history', routes: [ { path: '/contact', component: Contact }, { path: '/about_coffee_passport', component: About}, { path: 'user', component: User} ], }) after こんな感じで記述。 app.vue <router-link to="/user"> <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> こんな感じで記述。 hello_vue.js import VueRouter from 'vue-router' import router from './router/router' Vue.use(VueRouter) Vue.use(router) と書いたら、 vue.runtime.esm.js:638 [Vue warn]: Error in render: "TypeError: Cannot read properties of undefined (reading 'matched')" このようにコンソールに出力された。。。 hello_vue.js document.addEventListener('DOMContentLoaded', () => { const app = new Vue({ render: h => h(App), router: router }).$mount() document.body.appendChild(app.$el) console.log(app) }) router: router この一文を追加すればok. このように表示された。 下準備完了したので実装。 VueRouterで違うコンポーネントを表示させているが、その際にデータの受け渡しとかの記述は必要なのか。。 こーゆー記述あったので、多分必要。 router-linkでparamsを渡すことできるっぽい。 これらを参考に、 router.js { path: '/user/:userId', name: 'user',component: User, app.vue <router-link to="{name: 'user', params: {userId: drink.user.id}}"> と書いてみた。 $attrs is readonly. とエラーがコンソールに表示された。。。 この方の記事の通りで、 hello_vue.jsでは import Vue from 'vue' こうやってなってるのに、、 router.js側では import Vue from 'vue/dist/vue.esm.js' となってる。 複数のvueインスタンスがつくられてるらしい。 environment.js environment.config.merge({ resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' } } }) にこうやって記述したら、 vue.esm.js:648 [Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option. 次はこのようなエラーになった。 at app/javascript/packs/components/footer.vue footer.vueでエラーが起こり始めた。 一旦これはとばすとして、 hello_vue.js +import Vue from 'vue/dist/vue.esm.js' -import Vue from 'vue' とした。 一旦、router-viewでSPA化できたからokとして、 問題点 このように一つ押しただけなのに、全部の投稿のrouter-viewが反応してしまう。。。 app.vue <li v-for="drink in drinks" :key="drink.id" class="list" > このように繰り返してるからねぇ。。 app.vue <router-view></router-view> router-viewの部分に表示されるので、それをv-forの外に置けば済む。 さらなる問題点。。。 問題はユーザーの投稿だけを表示させたいにも関わらず、 ```user.vue export default { data: function(){ return { drinks: "drinks" } }, created(){ this.setDrink(); }, methods: { setDrink: function(){ axios.get('/api/drinks') .then(response =>( this.drinks = response.data )) } } } ``` このように書いているので、全投稿を取得してしまう。 なぜ全投稿を取得してしまうのかは割愛。 自分が今考えたロジックとしては 全投稿を取得してしまうのはしょうがないとして、 v-on@clickとかなんかで、クリックした要素のuser_idを取得して、 それを子コンポーネントで渡して、 そのidと一致する投稿をthis.drinks に代入するとか、なんとか。 ってことでクリックした要素を取得するコードを書きます。 router-linkをクリックしたら呼び出したいが、 ここにあるとおり、 このようにしないといけないらしい。 引数にdrinkを渡すことで,drink.user_idで投稿に紐付いてるユーザーのidを取得することができる。 app.vue <li v-for="drink in drinks" :key="drink.id" class="list" > <router-link to="/user" @click.native="getUserId(drink)"> methods{ getUserId(drink){ console.log(drink) } 雑だけど、こんな感じで書いたらいけた!!やったね!! これをuser.vueの方に渡したい。 app.vue <router-view :user_id="user_id"></router-view> user.vue props: { user_id: { type: Number } }, と書いたら取得できた。。やったぜ user.vue <template> <div> <li v-for="drink in drinks" :key="drink.id" class="list" > {{drink.user_id}} </li> </div> </template> <script> import axios from 'axios'; // import likeButton from './like/likeButton.vue'; // import drinkShow from './drinks/show.vue'; export default { props: { user_id: { type: Number } }, data: function(){ return { drinks: "drinks" } }, mounted(){ this.setDrink(); }, methods: { setDrink: function(){ axios.get('/api/drinks') .then(response =>( this.drinks = response.data.filter(drink => drink.user_id = this.user_id ) )) } } } </script> こんな感じで書いたら、 router-link押したときに  特定の投稿のユーザーだけのidを表示できた。 app.vue mounted(){ this.setDrink(); }, methods: { setDrink: function(){ axios.get('/api/drinks') .then(response =>( this.drinks = response.data.filter(drink => drink.user_id = this.user_id ) )) } } createdからmounted()に変更 createdだと処理をするタイミングが早すぎて、this.user_idにアクセスしたときにエラーが起こってしまった。 response.dataに全投稿のデータが入ってるが、 その配列にfilter()メソッドをしてあげることで 条件に一致した要素だけで作られた配列を改めて返してくれる嬉しいメソッド。 drink.user_id(response.data、全投稿のそれぞれのuser_id)と router-linkクリックしたときにuser.vueに渡されるuser_idが一致した要素のみを返してくれる。 つまり、クリックした投稿に紐付いてるユーザーの投稿しか表示されない!!! やったぜ!! って思ったけど、なぜかuser_id 5じゃない投稿もuser_idが5になっていて表示されてしまってる。 user.vue setDrink: function(){ axios.get('/api/drinks') .then(response => { const drinks = response.data this.filteredDrinks = drinks.filter(drink => drink.user_id === this.user_id) }) }, こんな感じで書くんだよ!!! drink.user_id === this.user_id にしないといけない。。。 ずっと代入式書いていた。。。 評価式にしないといけなかった。。。。 そっかぁ。。。 やっぱ基礎って大事だと思いました。 した準備は完了したので、明日はデザイン部分をやっていきたいと思いますl
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む