20210509のvue.jsに関する記事は17件です。

【Vue.js】Vue.jsを使ったアプリケーションのデータフロー

概要 今回は表題の通り、Vue.jsを使ったアプリケーションにおける データの流れについて書いていきます。 Vue.jsにおけるデータフロー Vue.jsを使用したアプリケーションを「データの流れ」という観点捉えると、 View,Action,Stateという3つの要素に分けることができます。 (この3要素はビュー、更新処理、状態と呼ばれたりする場合もあります) ・View…Stateの内容に応じてブラウザ表示 ・Action…Stateの内容を更新 ・State…ブラウザ表示に必要なデータの保持 では、実際のコードを元に3要素の流れを確認していきます。 script.js new Vue({ el: '#app', data() { return { count: 0 }; }, methods: { increment() { this.count++; } } }); index.html <body> <div id= "app"> <button @click="increment">+1</button> <p>{{ count }}</p> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="./script/script.js"></script> </body> こちらはボタン押下すると+1されるカウンターアプリです。 (そのままコピペしてJSFIddleで簡単に動かすことができます!) このアプリでは以下の①〜③の流れを確認できると思います。 ①countプロパティの初期値がブラウザに表示される(State→View) ②ボタン押下により、  countプロパティの内容が書き換えられる(Action→State) ③変更されたcountプロパティの内容が、  ブラウザに即時反映される(State→View) このあと再度ボタンを押下した場合も同様の流れです。 ViewはStateによって規定され、 StateはActionによって規定される、 つまり、全体の処理の中には「State→View→Action」という流れがあり、 この流れの繰り返しがアプリケーションの動きを実現しています。 この流れは「単方向のデータフロー」とも呼ばれ、 データフロー設計において意識すべきポイントの一つになります。 参考資料 ・書籍「Vue.js入門 基礎から実践アプリケーション開発まで」 ・書籍「たった1日で基本が身に付く! Vue.js 超入門」
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】コンポーネント超入門

はじめに 最近、Vue.jsを使って自学用にWEBアプリを作成しているのですが、 そこでコンポーネントを使った実装をする機会がありました。 今回はその中で得たコンポーネントの概念的な基礎知識や コンポーネント間のデータのやりとりについて書いていきます。 コンポーネントとは コンポーネントとは、ある特定の機能を保有した再利用可能な「部品」を指し、 Vue.jsでは「再利用可能なVueインスタンス」と定義できます。 また、コンポーネントを組み合わせてWEBアプリケーションを構築する開発を 「コンポーネント指向開発」と呼びます。 最近のフロントエンドの開発ではこの手法が主流になってきています。 コンポーネントを使うメリット 先ほど「コンポーネント指向開発がモダンフロントエンド開発手法のスタンダード」 と書きましたが、そこにはやはり理由があります。 コンポーネント指向が採用される最大の理由は、 「再利用性が高い」という点です。 従来の開発手法では、同じUIパーツを複数ページに実装する場合、 同一コードを何度も書く必要がありました。 しかし、コンポーネントを使用すると、 機能ごとにコンポーネントとして分割した部品を再利用できます。 そのため開発スピードの向上が期待できるのです。 また、機能が分かれていることからメンテナンス性も高まり、 これらのメリットはより大規模な開発を行う際に実感できます。 親コンポーネントと子コンポーネント コンポーネントには大きく分けて2つの種類があります。 それは「取り込む側のコンポーネント」と「取り込まれる側のコンポーネント」です。 そして、Vue.jsでは取り込む側を「親コンポーネント」、 取り込まれる側を「子コンポーネント」と呼びます。 親子間のデータのやりとり Vue.jsでは親子間のデータのやりとりをpropsとemitで実現します。 props:親から子に渡されるデータ emit:子から親に渡されるデータ propsとemitの具体的な書き方については後日別記事で整理します。 参考資料 ・書籍「たった1日で基本が身に付く! Vue.js 超入門」
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[メモ] sticky & Intersection Observer & opacityの併用はchromeでバグるっぽい

position: sticky (by css) -> スクロールして画面上部で固定。弟要素が上にかぶさる Intersection Observer (by js) -> 画面表示領域内への出入りでアニメーション用のクラス追加・削除 opacity (by css) -> Intersection Observerに伴うクラスによって表示/非表示アニメーション の3点を組み合わせると、 上へのスクロールはできるけど、下へのスクロールができなくなる。 (上へのスクロールは通常だけど、下へスクロールは弟要素が画面下でバウンスして戻ってくる) なんでだろう……。 どれか2個までの組み合わせなら動くんだけどな~。 この3つになると動かない… ↓書いたもの(一部省略) parent.vue <template> <div class="l-page"> <section class="list"> <Widgets-component v-for = "item in items" :item = "item" class = "item" :key = "item.id" :ref = "'item'" /> </section> </div> </template> <script> export default { data() { return { items: [//**省略**//] } }, updated() { let targets = []; this.$refs.item.forEach(el => targets.push(el.$el)); const observer = new IntersectionObserver((entries) => { let className_active = 'is-show'; entries.forEach((entry) => { if (entry.intersectionRatio >= 0.8) { entry.target.classList.add(className_active); } else { entry.target.classList.remove(className_active); } }); }, { root: null, rootMargin: '0px', threshold: 1 }); targets.forEach((target) => { observer.observe(target); }); } } </script> <style lang="scss" scoped> .list { height: 100%; overflow-y: auto; scroll-snap-type: y mandatory; } .item { position: sticky; top: 0; height: 82vh; scroll-snap-align: start; scroll-snap-stop: always; } .disc-item::v-deep { .item-body, .item-footer { opacity: 0; transition: opacity 600ms ease; } &.is-show { .item-body, .item-footer{ opacity: 1; } } } </style> child.vue <template> <div> <div class="item-thumb"> <img :src="item.images.url" /> </div> <div class="item-body"> some body element </div> <div class="item-footer"> some footer element </div> </div> </template> <script> export default { props: { item: { type: Object } } } </script> ちなみにopacityじゃなくて、backgroundだと普通に動くんだよな~。 なんか別の理由かな~。 parent.vue <style lang="scss" scoped> .disc-item::v-deep { .item-body, .item-footer { background: red; } &.is-show { .item-body, .item-footer{ background: blue; } } } </style>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

それだけじゃない! Storybook の活用方法

はじめに なぜフロントエンド開発にStorybookを導入するのか というエントリーで、コンポーネントの凝集度という切り口から Storybook の有用性について述べており、素晴らしいと思いました。 Storybook に関しては他にも様々なエントリーがあり、主にデザイナーとのコミュニケーションツールや、カタログツールとして紹介されていることが多いように感じます。 一方で、Storybook には、他にも知っておくべき魅力があると思うので、ここにまとめていきます。 Storybook とは Storybook は React や Vue.js 向けの開発サポートツールです。先程紹介したエントリーでは、Storybook の活用方法を、以下のように紹介していました。 エンジニアとデザイナーが協働する環境として使う エンジニアがサンドボックス環境として使う 再利用できるComponentのカタログとして使う はい、完全に同意です。特に、3 について詳しく解説されていました。 このエントリーでは、2 のサンドボックス環境としての活用方法に加え、コンポーネントのドキュメントとしての活用方法にフォーカスしてみたいと思います。 サンドボックス環境およびドキュメントとしての Storybook 今回は一つのコンポーネントを題材にして、Storybook を使った開発方法を解説します。最後に、もし Storybook が無ければどれほど困るか、ということを考えてみたいと思います。 開発環境 今回は Vue.js + TypeScript を使用してコンポーネントを書いていきます。 コンポーネントの仕様 次のようなコンポーネントを作ってみます。 UserInfoCard ユーザの ID と名前を表示するカード 管理者として閲覧するときはメールアドレスを見ることができる メールアドレスが無いときは[-]を表示する メールアドレスが長過ぎるときは[...]のように省略表示される STEP 1 : コンポーネントとストーリーのスケルトンを作成する UserInfoCard.vue <template> <div> </div> </template> <script lang="ts"> import { Component, Prop, Vue } from "vue-property-decorator" @Component export default class UserInfoCard extends Vue { } </script> UserInfoCard.story.ts import UserInfoCard from "./UserInfoCard.vue" const ARG_TYPES = { } export default { title: "UserInfoCard", component: UserInfoCard, argTypes: ARG_TYPES, } export const basic = () => ({ components: { UserInfoCard, }, props: Object.keys(ARG_TYPES), template: ` <UserInfoCard /> `, }) はい、これで開発の準備完了です! 今回は主に docs タブの画面を使用します。 ちなみに、上記ファイルを VSCode のスニペットとして登録しておくと非常に便利です。 STEP 2 : story ファイルを書く 私の場合、コンポーネントを書くときは最初に story ファイルの方を書いてしまいます。感覚としてはテスト駆動開発に近いです。ストーリー駆動開発と称して提唱していきたいです。 もちろん、テスト駆動開発と同じように、最初に全てのストーリーを完全に書き切る必要はありません。ある程度コンポーネントを書いてから、逐次書き足していっても OK です。 ここでのポイントは、すべての仕様を確認できるようにコントローラを定義し、適切な形に変換してコンポーネントに渡すことです。 今回のコンポーネントは、 閲覧者の権限 メールアドレスの長さ(無し/通常/長い) というパラメータで表示が変化するので、コードは下記のようになります。 UserInfoCard.story.ts import { User } from "../models/user" import UserInfoCard from "./UserInfoCard.vue" const ARG_TYPES = { email: { control: { type: "select", options: { none: "", nomal: "hogehoge@example.com", long: "hogeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee@email.com", }, }, defaultValue: "hogehoge@example.com", }, permission: { control: { type: "select", options: ["admin", "general", "guest"], }, }, } export default { title: "UserInfoCard", component: UserInfoCard, argTypes: ARG_TYPES, } export const basic = () => ({ components: { UserInfoCard, }, data: (vue: Vue) => { const { email } = vue.$props const user = new User(123, "user name", email) return { user, } }, props: Object.keys(ARG_TYPES), template: ` <UserInfoCard :user="user" :permission="permission" /> `, }) email から none normal long を選ぶと、それに対応する値 hogehoge~ hogeeeeeeee~ で User インスタンスが作成されます。 permission は選んだ値を直接 prop として渡しています。 今回は select コントローラを使いましたが、公式ドキュメントで他のコントローラの使い方について詳しく解説されているので、是非参考にしてください。 コンポーネントの prop について UserInfoCard の prop user と permission はまだ存在していませんが、このコンポーネントの描画に必要と思われる情報をこういうインターフェースだったら良いなという思いを込めて書いたものです。 ちなみに User はプロジェクトに既存のモデルクラスとして、次のようなものを想定しています。 user.ts export class User { constructor( readonly id: number, readonly name: string, readonly email: string ) {} } STEP 3 : コンポーネントを書く ここまで書けば、後は、Storybook の画面を見ながらコンポーネントを作っていくだけです! コントローラで prop の値を変えながら実装することで、仕様を満たしているかどうかをすぐに確認することができます。 UserInfoCard.vue <template> <div class="flex flex-col border border-solid border-gray-600 rounded-lg p-3" :style="{ width: '300px' }" > <div class="flex flex-row space-x-3"> <div>ID:</div> <div>{{ user.id }}</div> </div> <div class="flex flex-row space-x-3"> <div>name:</div> <div>{{ user.name }}</div> </div> <div v-if="permission === 'admin'" class="flex flex-row space-x-3" > <div>email:</div> <div class="truncate w-full"> {{ user.email ? user.email : '-' }} </div> </div> </div> </template> <script lang="ts"> import { User } from "../models/user" import { Component, Prop, Vue } from "vue-property-decorator" @Component export default class UserInfoCard extends Vue { /** * ログインしているユーザの権限 * * `MyInformation` クラスの `permission` を使う */ @Prop({ required: true }) permission!: "admin" | "general" | "guest" /** * このカードに表示したいユーザ */ @Prop({ required: true }) user!: User } </script> ※ tailwind という CSS ライブラリを使用して記述しています。 コントローラの値を選択することで、表示の切り替わりを容易に確認することができます permission が general または guest permission が admin で email が none permission が admin で email が long ドキュメントとしての側面 description Storybook の画面上に Description が表示されていたのに気づきましたか? これは、UserInfoCard.vue で各 prop の上の行のコメントがそのまま反映されています。 他にも、event や control にも description を表示することができます。詳細は公式ドキュメントをご参照ください。 mark down 記法もサポートされていて、非常に便利です。 Show code コンポーネントが表示されているエリアの右下の、Show code をクリックすると、story ファイルでどのようにコンポーネントを使っているかを見ることができます。 これらの機能を活用することで、開発者はコンポーネントファイルを見に行かなくても使い方や仕様を知ることができます。 ここまでサンドボックス環境として使ってきた story ファイルは、次の仕様追加や修正をする開発者がすぐに使える開発環境であり、コンポーネントのドキュメントにもなります。 story ファイルを書く、という手間は発生しますが、それ以上の大きなリターンを得ることができるのです。 Storybook がない世界 ここまで、Storybook をサンドボックス環境、そしてドキュメントとして利用する方法についてお伝えしてきました。 もし Storybook が無ければ、どのようになるでしょうか? プロダクトに新しい機能を追加する時 画面単位の大きめの新機能を実装することになったとします。開発初期段階で、小さなコンポーネント単位で実装を確認したい時、表示する画面がありません。確認するためには、表示のための画面を一時的に作るか、ページ全体の完成を待つしか選択肢はありません。 バグを修正する時 先程の UserInfoCard.vue で、「admin として閲覧している時、長いメールアドレスのユーザの表示が崩れる」というバグが発生したとします。再現するには、 ローカルでアプリケーションを立ち上げて、 admin としてログインし、 メールアドレスの長いユーザを作成し、表示を確認する 必要が有ります。 この状態からスタイルの修正をしていきますが、ビルドに時間のかかるアプリケーションであれば、修正→確認 のサイクルに非常に時間がかかってしまいます。 仕様を確認したい時 バグ修正や機能追加をするときは、もちろん現在の仕様を知る必要があります。仕様書があれば仕様書を見に行きますが、メンテナンスされていない場合は実装を深く読む必要が有ります。そもそも仕様書が存在していない場合はもっと悲惨です。 まとめ 安心してください。Storybook はあります。 開発フェーズのどの段階でもコンポーネントの実装を確認でき、 ビルドも早く、 そのまま仕様書として残すことができる 素晴らしいツールです。使い始めて半年程度経ちましたが、もう手放すことはできません。 みなさんも、ストーリー駆動開発で、良いフロントエンドライフを送りましょう!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js 勉強メモ】v-forを使う時の注意点 key属性

はじめに 仕事で使う事になったので1からVue.jsについて学んだ(元々Angularでプロダクト開発をやっていた事はあるがAngularはだめか・・・)。 ちゃんと覚えておかないとまずそうな事を備忘録として1つ1つ残しておく。 v-forを使う時の注意点 key属性 v-forディレクティブを使う際には、key属性を付与する事で予期せぬバグが発生しないように注する必要がある。 予期せぬバグが発生してしまったパターンとして、v-forでリストレンダリングをした際に以下のような挙動になってしまった原因をみていく。 動画のソースコードは以下。 sample.html <body> <div id="app"> <ul> <div v-for="fruit in fruits"> <p>{{ fruit }}</p> <input type="text"> </div> </ul> <button @click="remove">先頭を削除</button> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script> <script> new Vue({ el: '#app', data: { fruits: ['りんご', 'バナナ', 'ぶどう'] }, methods: { remove: function () { this.fruits.shift(); } } }) </script> </body> ここで意図していた動きとしては、それぞれのdivタグが消えるような動き方で、1度先頭を削除ボタンをクリックすると、inputタグには上から順に「Banana、Grape」と記載されている状態になる事・・・。 これの原因は、v-forディレクティブには、 要素の移動を最小限に抑えるアルゴリズムが採用されており、可能な限りその場の近い同じ要素を再利用しようとする という性質があるため。今回だと、配列の要素を消す前後の差分を考えて効率よくレンダリングしようとした結果、inputタグはDOM上は同じなので上記の動画のような挙動になってしまったという事。 これを解決するには、 key属性を付与し、要素とデータとを関連付ける(紐づける) ようにする。今回で言えばdivタグという要素に対してfruits配列の1つ1つのデータとを関連付けるようにする。そうすると、以下のように期待通りの動きになる。 動画のソースコード(抜粋)は以下。 sample.html <body> ・・・ <div v-for="fruit in fruits" :key="fruit"> <p>{{ fruit }}</p> <input type="text"> </div> ・・・ </body> これはVueに対し、あるkeyを持つ要素という情報を与える事で各divタグが一意の要素として扱われるようになり、同じ要素として判定されず再利用されなかったため。 ※key属性を使用する時の注意点 templateタグでは使えない理由:templateタグには属性は存在しないので v-forのindexをkey属性に指定しない理由:リストレンダリングする時のリストの中身が変化しても、リストの中身が変化するとそのインデックスも変化し、同じindexになってしまう場合があるから1 Vue.jsの勉強メモ一覧記事へのリンク Vue.jsについて勉強した際に書いた勉強メモ記事のリンクを集約した記事。 https://qiita.com/yuta-katayama-23/items/dabefb59d16a83f1a1d4 例えば今回のfruits配列で言えば、先頭の「りんご」を削除すると、「バナナ」のインデックスが0になってしまい、削除の前後でv-forのindexは変化しない事になる。すると、再利用されてしまい意図した動きにならない。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】computedについての理解を整理してみた

はじめに この投稿ではcomputed(算出プロパティ)について自分の理解を整理してみました。 そもそもプロパティとは computedの説明に入る前にプロパティについて簡単に整理します。 プロパティとはひとことで言えば「オブジェクトを定義するもの」です。 オブジェクトは下のsample.jsでいうところの{ }(波カッコ)で定義されます。 { }の中にはプロパティ(sample.jsではel)と値(sample.jsでは'#app')を記述します。 sample.js new Vue({ el: '#app' }); computedとは では、本題に入っていきます、 computedとはVue.jsに用意されているプロパティの一つで、 「算出プロパティ」とも呼ばれます。 computedはdataプロパティと同様にデータの変化を画面に即時反映しますが、 computedはデータそのものに何らかの処理を与えたいものをプロパティにしたいときに使用します。 使用例は以下の通りです。 こちらは簡単なカウンターアプリで 10を超えると「10以上です」という文字が表示されます。 index1.html <body> <div id="app"> <button @click="increment">+1</button> <p>{{ count }}</p> <div v-if="isOver">10以上です</div> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="./script/script.js"></script> </body> script1.js new Vue({ el: '#app', data() { return { count: 0 }; }, methods: { increment() { this.count++; } }, computed: { isOver() { return this.count >= 10; } } }); (JSFiddleにそのままコピペして挙動を手元で確認できます!) HTMLに条件式を書き込めば良くないか? 先ほどのcomputedを使ったカウンターアプリについて、 以下の実装でも良いのではないか?と思った方もいらっしゃるのではないでしょうか。 index2.html <body> <div id="app"> <button @click="increment">+1</button> <p>{{ count }}</p> <div v-if="count >= 10">10以上です</div> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="./script/script.js"></script> </body> script2.js new Vue({ el: '#app', data() { return { count: 0 }; }, methods: { increment() { this.count++; } } }); そうです、computedなんて使わず、 HTML側のコードに直接 "count>=10" と書く込む方法です。 実際にこの実装でもアプリは想定通りの挙動をしてくれます。 しかし、このような実装は「良くない」とされています。 理由は「HTMLの可読性が下がるため」です。 今回例として示しているのは単純な条件式ですが、 これが更に複雑になり条件式の記述が長くなると、HTMLの可読性を保つことは難しくなります。 以上の観点からv-ifの条件をHTMLのタグには記述せず、 そのようなロジックは冒頭で示した通り算出プロパティを使用して JavaScript側に分離して実装すべきでしょう。 参考資料 ・Vue.js公式ドキュメント  https://jp.vuejs.org/v2/guide/computed.html ・書籍「Vue.js入門 基礎から実践アプリケーション開発まで」 ・書籍「たった1日で基本が身に付く! Vue.js 超入門」
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【SPA】Nuxt.js,TypeScript,AWS,graphQLで作るインスタクローンアプリ

目次 作品リンク 作成目的 使用技術 アプリの使い方 1.作品リンク amplifyにてホスティングしました。 https://master.d2qrnepigha0wa.amplifyapp.com/ 2.作成目的 学習のアウトプット ポートフォリオとしての使用 3.使用技術 フロントエンド HTML/CSS TypeScript Nuxt.js(SPAモード) Vuetify(UIフレームワーク) ESLint/Prettier(コード解析ツール) バックエンド・インフラ Amplify(インフラ構築) GraphQL(API呼び出し) AppSync(API管理) DynamoDB(データベース) S3(画像ストレージ) Cognito(ユーザー認証) GitHub(バージョン管理) インフラ構成図 ER図 4.アプリの使い方 機能名 説明 ユーザー機能 新規登録・メールアドレスによる確認・登録内容変更・アイコン登録・ログイン・ログアウト・ゲストユーザー 投稿機能 投稿、編集、削除、画像複数枚登録 コメント機能 投稿に対してコメント投稿 いいね機能 投稿をいいねできる ①ユーザー機能 登録・編集 ユーザー名・メールアドレス・パスワードでアカウントを作成します。 同時にアイコンの登録も可能です。 パスワードとメールアドレスはcognitoのユーザープールにて管理され、データベースに保存されません。 登録の際に入力いただいたメールアドレスに、確認コードを送信し、 確認が完了すると機能をお使いいただけます。 (ここで確認しなかった場合、ログインの際に確認機能を挟むようになっています。) 会員登録 ログイン ゲストユーザーでのログインも用意しました。 ゲストユーザー ログイン画面・アカウントの新規登録画面に、ゲストログインのボタンを配置し、気楽に使用できるようにしました。 ②.投稿機能 投稿の作成、削除は即時に反映されます。 画像は最低1枚は登録必須になります。 登録枚数の制限はありません。 ③.いいね機能 投稿に対し、いいねできます。自分がいいねしている場合は、いいねを外すことももちろん可能です。 ④.コメント機能 投稿詳細ダイアログから、投稿に対してコメントできます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フォールバックコンテンツ

の中身で表示されるものがない場合には以下のように表示されます <slot>デフォルトのコンテンツ</slot>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

親コンポーネントから子コンポーネントへhtmlごと送る

App.vue <template> <div> <LikeHeader> <h1>トータルのいいね数</h1> <h2>{{ number }}</h2> </LikeHeader> <LikeNumber v-bind:total-number="number" @my-click = "incrementNumber">></LikeNumber> <LikeNumber v-bind:total-number="number"></LikeNumber> </div> </template> <script> import LikeHeader from "./components/LikeHeader.vue"; export default { data() { return { number: 14 }; }, components: { LikeHeader }, methods: { incrementNumber(value) { this.number = value } } }; </script> <style scoped> div { border: 1px solid blue; } </style> LikeHeader.vue <template> <div> <slot></slot> </div> </template> (親コンポーネント)子コンポーネントタグの中にHTMLごと入れてしまう (子コンポーネント)タグで受ける
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

propsでオブジェクトと配列で渡す場合

参照渡しになります。 参照渡しとは同じメモリを見に行っていることになります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

子コンポーネントから親コンポーネントにデータを引き渡す。

コンポーネントの親子関係 ある任意のコンポーネントを取り込み利用した場合に、そのコンポーネント自体を子、利用した先は親という関係になります。 https://noumenon-th.net/programming/2019/02/22/component/ 今回の子コンポーネントの親への受け渡しという事柄についてはその本質は子コンポーネントのタイミングで自由に親コンポーネントのメソッドを発火できるという方がフィットします。そうなってくるとデータを送るというのは付随したものでイベントのみを行うということがメインのテーマとなる。つまり親コンポーネントに仕込んである式を実行するということになる。[親のイベントを発火させるトリガーになる]別の言い方をすれば$emitはカスタムイベントを自由に作れるということになる。 $emit 第1引数 第2引数 $emit 渡すデータの名前(emit名) データの値(自身) 引き受け側の親の方へ属性を書いて「$event」を記述するとこの$event自身がデータになる。 <template> <div> <LikeHeader></LikeHeader> <h2>{{ number }}</h2> <LikeNumber v-bind:total-number="number" v-on:my-click = "number = $event">></LikeNumber> <LikeNumber v-bind:total-number="number"></LikeNumber> </div> </template> <script> import LikeHeader from "./components/LikeHeader.vue"; export default { data() { return { number: 14 }; }, components: { LikeHeader: LikeHeader } }; </script> <style scoped> div { border: 1px solid blue; } </style> <template> <div> <p>いいね({{ halfNumber }})</p> <button @click="increment">+1</button> </div> </template> <script> export default { props: ["totalNumber"], computed: { halfNumber() { return this.totalNumber / 2; } }, methods: { increment(){ this.$emit("my-click", this.totalNumber + 1) } } }; </script> <style scoped> div{ border:1px solid red; } </style> メソッドで引き受ける App.vue(親コンポーネント) <template> <div> <LikeHeader></LikeHeader> <h2>{{ number }}</h2> <LikeNumber v-bind:total-number="number" @my-click = "incrementNumber">></LikeNumber> <LikeNumber v-bind:total-number="number"></LikeNumber> </div> </template> <script> import LikeHeader from "./components/LikeHeader.vue"; export default { data() { return { number: 14 }; }, components: { LikeHeader }, methods: { incrementNumber(value) { this.number = value } } }; </script> <style scoped> div { border: 1px solid blue; } </style> LikeNumber.vue(子コンポーネント) <template> <div> <p>いいね({{ halfNumber }})</p> <button @click="increment">+1</button> </div> </template> <script> export default { props: ["totalNumber"], computed: { halfNumber() { return this.totalNumber / 2; } }, methods: { increment(){ this.$emit("my-click", this.totalNumber + 1) } } }; </script> <style scoped> div{ border:1px solid red; } </style> App.vue(L5) <LikeNumber v-bind:total-number="number" @my-click = "incrementNumber"> App.vue(L20) methods: { incrementNumber(value) { this.number = value } incrementNumberの引数をvalueとすることによって、LikeNumber(子コンポーネント)の第2引数を指定していることになる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

propsをオブジェクトで指定する

<template> <div> <LikeHeader></LikeHeader> <h2>{{ number }}</h2> <LikeNumber></LikeNumber> <LikeNumber :total-number="number"></LikeNumber> </div> </template> <script> import LikeHeader from "./components/LikeHeader.vue"; export default { data() { return { number: 14 }; }, components: { LikeHeader: LikeHeader } }; </script> <style scoped> div { border: 1px solid blue; } </style> <template> <div> <p>いいね({{ halfNumber }})</p> <button @click="increment">+1</button> </div> </template> <script> export default { props: { totalNumber: { type: Number, default: 10 } }, computed: { halfNumber() { return this.totalNumber / 2; } }, methods: { increment(){ this.number += 1; } } }; </script> <style scoped> div{ border:1px solid red; } </style> props: { totalNumber: { type: Number, /型の指定/ default: 10 /うまくいいかない場合に取る値を設定/ } 今回はにtotalNumberの属性指定がないのでdefault値10が使用されている。 あとこれ以外に required: trueもよく使われてこれについては親要素に属性を絶対指定しないといけなくなる。 *defailtとは共存できない。 *typeというのはバリデーションと呼ばれ意図しないエラーが怒らないように抑制をかけている
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】v-ifとv-showを使いこなそう

はじめに v-ifとv-showの違いと使い分けについて学習したので、 その内容を自分の備忘録もかねて整理して記事にします。 v-ifとv-showについて v-if,v-showについてそれぞれ簡単に説明します。 どちらも特定の要素を条件に応じて描画したい場合に使用され、 ディレクティブの式が真を返す場合のみに描画されます。 ※ディレクティブ  Vue.jsでは、標準のHTMLに対して独自の属性を追加することで、  属性値の式に変化に応じてDOM操作を行います。  この特別な属性のことをディレクティブと呼び、  属性名にはデフォルトで「v-」という接頭辞がつきます。 v-ifとv-showの違い さて、ここから本題です。 これらの違いは大きく分けて2つあります。 v-ifは要素の描画コントロールをJavaScriptで行うが、 v-showはJavaScriptでなく、cssのdisplayプロパティで行う v-ifはv-elseやv-else-ifのような分岐処理を使用することができるが、 v-showはできない v-ifとv-showの使い分け では、前述の違いを踏まえてこれらをどう使い分けていくか。 違い②で説明したようにv-showは複数の分岐処理は使えないので、 そのような実装をしたい場合はv-if一択でしょう。 ここでは違い①に焦点を当てます。 要素の描画コントロールについて、 v-ifはJavaScriptで式の結果に応じてDOM要素を追加または削除するのに対して、 v-showはスタイルの操作、つまりdisplayプロパティの値を変更することで実現します。 一般的にスタイルの操作よりもDOM操作の方がレンダリングコストが高く、 この観点から「切り替えの頻度が高い場合」はv-showの使用が推奨されます。 参考資料 ・Vue.js公式ドキュメント  https://jp.vuejs.org/v2/guide/conditional.html ・書籍「Vue.js入門 基礎から実践アプリケーション開発まで」 ・書籍「たった1日で基本が身に付く! Vue.js 超入門」
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

propsをscript内で扱う

App.vue(親コンポーネント) <template> <div> <LikeHeader></LikeHeader> <h2>{{ number }}</h2> <LikeNumber :total-number="number"></LikeNumber> <LikeNumber :total-number="number"></LikeNumber> </div> </template> <script> import LikeHeader from "./components/LikeHeader.vue"; export default { data() { return { number: 10 }; }, components: { LikeHeader: LikeHeader } }; </script> <style scoped> div { border: 1px solid blue; } </style> LikeNumber.vue(子コンポーネント) <template> <div> <p>いいね({{ halfNumber }})</p> <button @click="increment">+1</button> </div> </template> <script> export default { props: ["totalNumber"], computed: { halfNumber() { return this.totalNumber / 2; } }, methods: { increment(){ this.number += 1; } } }; </script> <style scoped> div{ border:1px solid red; } </style>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

親子コンポーネント間でのデータの受け渡し

親コンポーネントから子コンポーネントまで main.js import Vue from 'vue'; import App from './App.vue'; import LikeNumber from './components/LikeNumber.vue'; Vue.config.productionTip = false; Vue.component("LikeNumber", LikeNumber); new Vue({ render: h => h(App) }).$mount("#app"); App.vue <template> <div> <LikeHeader></LikeHeader> <h2>{{ number }}</h2> <LikeNumber number="6"></LikeNumber> <LikeNumber number="6"></LikeNumber> </div> </template> <script> import LikeHeader from "./components/LikeHeader.vue"; export default { components: { LikeHeader: LikeHeader } }; </script> <style scoped> div { border: 1px solid blue; } </style> LikeNumber.vue <template> <div> <p>いいね({{ number / 2 }})</p> <button @click="increment">+1</button> </div> </template> <script> export default { props: ['number'], methods: { increment(){ this.number += 1; } } }; </script> <style scoped> div{ border:1px solid red; } </style> LikeHeader.vue <template> <p>いいね数合計</p> </template> propsはpropertyの略 <子> (1)numberという値を使いたいところが「子」コンポーネントになります。今回はLikeNumber.vue (2)propsは配列で使いたい値の名前を書きます。[子コンポーネントで定義します] <親> (1)属性名で定義する[]*なるべくケバブケースで定義する propsの命名規則 キャメルケース ケバブケース totalNumber total-number *ブラウザに直接渡すものはHTMLの慣例としてケバブケースで書くようにする。 理由)ブラウザは大文字を認識できずに全て小文字で解釈するため。 (参考)App.vueのnumberを動的に表現するためv-bindを使用 <template> <div> <LikeHeader></LikeHeader> <h2>{{ number }}</h2> <LikeNumber v-bind:number="number"></LikeNumber> <LikeNumber :number="number"></LikeNumber> </div> </template> <script> import LikeHeader from "./components/LikeHeader.vue"; export default { data() { return { number: 10 }; }, components: { LikeHeader: LikeHeader } }; </script> <style scoped> div { border: 1px solid blue; } </style>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

toString(備忘録)

toString 数値を文字列に変換する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Azure Active Directory B2C のフロントエンドとAPIの実装

この記事は 前の記事からの続きになります。 ここでは、Azure側残りの設定を実施した後、実装まで進めたいと思います。 hogehoge-auth アプリの設定 API の公開 hogehoge-auth アプリの設定を表示します。 API の公開 をクリックして、 Scope の追加 をクリックします。 テキストフィールドに uuid が入力されていて、変更可能になっていますが、ここは何でも良いので、そのまま 保存してから続ける をクリックします。 スコープ名 を piyopiyo にします。 管理者の同意の表示名 と 管理者の同意の説明 は適当に入力しておきます。 状態 は 有効 に設定します。 スコープの追加 をクリックします。 API のアクセス許可 hogehoge-auth アプリの設定を表示します。 API のアクセス許可 をクリックして、 アクセス許可の追加 をクリックします。 自分の API をクリックします。 hogehoge-auth をクリックします。 アクセス許可 に piyopiyo がいるので、チェックを入れます。 アクセス許可の追加 をクリックします。 hogehoge campaign に管理者の同意を与えます をクリックします。 ダイアログが表示されるので はい をクリックします。 これで、状態がグリーンになりました。 認証後のリダイレクト URI を設定 hogehoge-auth アプリの設定を表示します。 認証 をクリックして、 プラットフォームを追加 をクリックします。 シングルページアプリケーション をクリックします。 今回 Vue の serve で確認するので、 http://localhost:8080/ と入力します。 一番下のチェックボックスは2つともオフにします。 構成 をクリックします。 あとで、Storage などにデプロイしたら、この画面でリダイレクト URI を追加することができます。 さぁ、情報を集めましょう! 設定はこれで終わりです。 これから、フロントエンドや API で必要な情報を収集します。 アプリの情報 hogehoge-auth アプリの設定を表示します。 アプリケーション (クライアント) ID をメモします。 ディレクトリ (テナント) ID をメモします。 エンドポイント をクリックします。 赤で囲った箇所を issuer domain としてメモします。 緑で囲った箇所を b2c domain としてメモします。 APIの公開 をクリックして、スコープの右のボタンをクリックして スコープ URI をメモします。 スコープ名 (ここでは piyopiyo )をメモします。 ユーザーフローの情報 hogehoge campaign の Azure AD B2C を表示します。 ユーザーフロー をクリックします。 ユーザーフロー名 (ここでは B2C_1_SIGNUP_SININ )をメモします。 収集したもの一覧 名称 プログラムでの定義名 今回設定した値 アプリケーション (クライアント) ID clientId e3870b94-11e9-4d78-bdc3-9e822dc10a84 ディレクトリ (テナント) ID tenantId 14c34ae7-a677-4c96-8475-5398ce5ef230  issuer domain issuerDomain hogehogecampaign.b2clogin.com  b2c domain b2cDomain hogehogecampaign.onmicrosoft.com  スコープ URI apiScopeUri https://hogehogecampaign.onmicrosoft.com/e3870b94-11e9-4d78-bdc3-9e822dc10a84/piyopiyo スコープ名 apiScopeName piyopiyo ユーザーフロー名 flowName B2C_1_SIGNUP_SIGNIN  実装例 実際に動作するサンプルアプリを github で公開しました。 クローンすれば、そのまま動きます。 なお、認証は今回用意した hogehoge campaign になっています。 ユーザー登録していただいても構いませんが、気まぐれで インスタンスを落とすかも知れません。 その際は下記の記事でテナント作成からお試しくださいませ。 API passport を使用していますが、今回は Azure Functions を想定しているので、 express は使わずに実装をしています。 認証に失敗するとサーバエラー500を返却します。 フロントエンド サインイン、サインアウトを行い、どの状態でも API を呼び出すことができるので、トークン有無での API の挙動を確認できます。 まとめ 手数が多いですが、覚えてしまえば大した量ではないと思います。 実際に運用するとなると ユーザに入力させる情報の精査 ユーザ情報の編集のための flow 作成 scope の精査 日本語化(設定でできます) ユーザ権限 くらいは面倒みないとダメですが、まずは認証が動くのを実感していただけたらなぁ~ということで記事にしてみました。 まぁ、偉そうに言ってますが、投稿者本人が忘れちゃうから記事にまとめただけですけどね。 認証は OAuth や OpenID が登場し、その後ユーザの利便性と安全性を向上させるために日々進化しているので、あっという間に浦島太郎状態になりまする。。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む