- 投稿日:2020-11-15T23:16:21+09:00
Vue.Jsのv-forディレクティブはforEachと考えればしっくりくる!
この記事について
JavaScriptもろくに書けないにも関わらずいきなり、Vueをバリバリ使った案件にぶち込まれて、はや半年。最初に理解に苦しんだのが"v-for"。
今ではv-forにはかなりお世話になっているが、最初のうちはもの凄く苦しんだ。
何せfor文すら書けなかったのだから無理もない。そもそもv-for とは??
まずVue.jsを触りはじめたときは、ディレクティブすら意味が分からなかった。
よくもまあそんな状態で初めたものである。簡単にいうとディレティブとは、Html側でJavaScriptっぽいことができるように、
Vue.Jsで用意されている便利な部品のこと だと私は思っている。
Vueにはいろんなディレクティブが用意されている。それらを駆使することによって、
ScriptからHtmlへのアクセス(逆もしかり)が非常に簡単になるというのがVue.Jsの
本質かと思う。それではv-for は何かというと
公式サイトによるとv-for で配列に要素をマッピングする
配列に基づいて、アイテムのリストを描画するために、v-for ディレクティブを使用することができます。v-for ディレクティブは item in items の形式で特別な構文を要求し、items はソースデータの配列で、item は配列要素がその上で反復されているエイリアスです:半年前のど素人の私なら、完結に書かれすぎてさっぱり分からなかった。
今見返すと実に完結に書かれてある。(笑)
当然、初学者にはわかるはずもない(笑)私だけだったりして私なりの見解だが、ある配列の値を表示するために、v-forというVueが用意した
便利な部品を使って、item in itemsみたいな書き方でその値を取得して、itemで反復して配列を呼び出すことができるというのがv-forだ。
自分で書いていてもよくわからなくなってきたので、JavaScriptのforEachを考えればしっくりくるのではないか?試しにforEachを使って配列の値をconsole.logで表示させるコードを下記に示したい。
items という配列をitemという引数を使って、一つずつ描画していることがわかる。
Vue.jsではv-forを使うことで、いちいちforEachなんて書かなくて済むのだ。JavaScriptconst items = ['item1', 'item2', 'item3'] const copyItemsArray = [] items.forEach((item) => { copyItemsArray.push(item) console.log(item) }) //結果は //item1 //item2 //item3 //と表示される。もう少しforEachにお付き合いいただきたい。
今度は連想配列で考えてみる。JavaScriptconst objectItems = [ {name:"太郎",age:22}, {name:"治郎",age:20}, {name:"三郎",age:18} ] const objectArray = [] objectItems.forEach((item)=>{ objectArray.push(item) console.log(`名前:${item.name}/年齢:${item.age}`) }) //名前:太郎/年齢:22 //名前:治郎/年齢:20 //名前:三郎/年齢:18forEachを知って、かなりしっくりきた。
というかv-forもforEachもかわらんやん(笑)さて、ここで、v-for を使用して描画とやらをやってみる。
CSSは苦手なので、Vuetifyを使用している。
それと普段のプロジェクトで、TypeScriptを触っている都合上
私が一番慣れている書き方で書かせていただいた。Vue.js<template> <div> <ul> <li v-for="(brother,i) in brothers" :key="i">名前:{{brother.name}}/年齢:{{brother.age}}</li> </ul> </div> </template> <script lang="ts"> import { Component, Vue } from 'nuxt-property-decorator' interface Person { name:string age:number } @Component({}) export default class extends Vue{ brothers:Person[] = [ {name:"太郎",age:22}, {name:"治郎",age:20}, {name:"三郎",age:18} ] } </script>↑こんな感じで描画される。
v-forを使用するときは、注意点としてkey要素というものをつけなければならない。
大抵の場合はオブジェクトの中にidなんかがあってそれをkey要素として返してあげるのだが、とくにid指定がないときなんかはindexで返してあげるのがベター。
その場合は、v-forのitemの2番目の引数としてindexを取ることができるので、
(item,i)のように書いてあげる。
このv-for のkeyの動作については下記のサイトに非常に詳しく載っているのでぜひ参考にしてみてほしい。
参考: 【徹底解説】これを見ればわかるvue.jsのv-forのkeyの動作さて、せっかくUI frameworkとしてVuetify.jsを使用しているのだから、
何かコンポーネントを使用して描画してみよう。
今回はv-simple-tableというコンポーネントを使用。
その名の通り、めっちゃシンプルなコンポーネント。Vue.js<template> <div> <ul> <li v-for="(brother,i) in brothers" :key="i">名前:{{brother.name}}/年齢:{{brother.age}}</li> </ul> <v-simple-table> <template v-slot:default> <thead> <tr> <th>名前</th> <th>年齢</th> </tr> </thead> <tbody> <tr v-for="(brother,i) in brothers" :key="i"> <td>{{brother.name}}</td> <td>{{brother.age}}</td> </tr> </tbody> </template> </v-simple-table> </div> </template> <script lang="ts"> import { Component, Vue } from 'nuxt-property-decorator' interface Person { name:string age:number } @Component({}) export default class extends Vue{ brothers:Person[] = [ {name:"太郎",age:22}, {name:"治郎",age:20}, {name:"三郎",age:18} ] } </script>↑こんな感じで描画される。
v-forなんて簡単
Vue.Jsなんて所詮はJavaScriptのフレームワーク(笑)って言ってみたかったのだが、
半年前の自分にいってやりたい。
まだまだひよっこエンジニアの私だが、最初は公式ページを見てもちんぷんかんぷん。
何度辞めたいと思ったことか、、、、、、Vue.Jsは慣れれば非常に便利なツールだと思っている。
なんなら私なんて、innerHTML とか書けない(笑)
恥ずかしくて言えないが、、、、、
それはいいか悪いのか別として、非常に楽なツールである。
最初、Vueの勉強を始めたときは、”学習コストが低いのがVue。だから、初心者向きなんだ”って書いていたサイトを見て、俺は全く理解できていないから、もう辞めた方がいいのか、、、なんて本気で悩んだこともある。勉強する順序が悪かっただけだった。
これからもVue.Jsを使ってガンガン開発に挑戦してみようと思っている。最後まで読んでくださった方ありがとうございました。
- 投稿日:2020-11-15T22:51:01+09:00
【vue.js】Vuexを学ぶ
Vuexの概念が何となく理解出来たので、ついでに使い方もまとめておく。
自社の開発においても大量のコンポーネントを扱わなければならない中でVuexが必須となっているので、勉強し直した。Vuex(ビューエックス)とは何か?
ググったら一番最初に出てくるこれを見てもイマイチ分からない人に向け、
vue.jsの基本は一応分かっている前提で説明すると、
沢山のコンポーネントがあるそれなりの規模のプロジェクトがあったとする。
さて、この図でComponentAのデータをComponentCやComponentEに渡すにはどうしたらいいだろうか。親コンポーネントから子コンポーネントへ、
また子コンポーネントから親コンポーネントへのデータのやりとりが出来るのはわかると思う。でも、孫から子へ、それを親に渡し、さらにそれを子から孫へ、みたいなやりとりは手間である。($emitして$emitしてpropsしてprops
して見たいにやるのはスマートではない。)
そこでVuexを使うと、グローバル変数のような感じでデータを扱うことが出来るようになると言う感じ(概念)。と言われれば分かり易いのではないだろうか。これで深いコンポーネント同士でのやりとりが出来るようになる。導入
Vuexのパッケージをインストールする。
vue cliでプロジェクト作成時にセットでインストール済みである場合は必要ない。npm install vuexvuex用のファイルを新たに設ける。
ファイル名は何でも良いのだが、store.js
とするのが一般的。store.jsimport Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); // vuexをvue全体で使用する宣言 export default new Vuex.Store({ // main.jsで読み込めるようにする // 以下で定義したものはどのコンポーネントでも使用出来る state: { number: 2 } })あとは
main.js
で読み込むだけ。main.jsimport Vue from 'vue'; import App from './App'; import router from './router'; import store from './store' // store.jsをインポート Vue.config.productionTip = false; new Vue({ el: '#app', router, store, // store: store, components: { App }, template: '<App/>', });stateを使ってみる
文字通り
storeの状態
をプロパティーとして返すと言う位置付けになろうか。前述の通り、ここで定義した内容はグローバルに取得する事が出来るようになる。store.jsexport default new Vuex.Store({ // main.jsで読み込めるようにする // 以下で定義したものはどのコンポーネントでも使用出来る state: { number: 2 } })ComponentAで表示する変数を
ComponentA.vue<template> <p>{{ number }}</p> </template> <script> export default { computed: { number() { // $storeとする事でどこからでも呼べるようになる return this.$store.state.number; } } } </script>ComponentEから操作出来る、みたいな事が出来る。
ComponentE.vue<template> <b-button @click="increment">+1</b-button> </template> <script> export default { methods: { increment(){ // $storeとする事でどこからでも呼べるようになる this.$store.state.number++; }, }, } </script>gettersを使ってみる
storeの状態
を算出したい時にgetters
を定義する事で、算出プロパティーとして使う事が出来る感じ。store.jsexport default new Vuex.Store({ // インスタンスをmain.jsで読み込めるようにする state: { // 状態を全体で使用出来るようにする number: 0 }, getters: { counter: state => state.number++, } })こんな感じでVuexの中で
computed
のような事が出来る。ComponentA.vue<template> <p>{{ count }}</p> </template> <script> export default { computed: { count() { return this.$store.getters.counter; // $store.gettersで呼ぶ } } } </script>まぁこの程度であればgettersを使う意味はないのだけど、
よりコンポーネントが複雑化してきた時に真価を発揮するものなのでそこはご容赦頂きたい。複数のgettersを扱う時にもっと便利に使う事が出来るのが、
mapGetters
である。store.jsexport default new Vuex.Store({ state: { number: 2 }, // 複数定義しているものがあるとする getters: { doubleCount: state => state.number * 2, tripleCount: state => state.number * 3 } })こう書くことも出来るが、
ComponentA.vue<script> export default { computed: { doubleCount() { return this.$store.getters.doubleCount; }, tripleCount() { return this.$store.getters.tripleCount; }, } } </script>
mapGetters
を用いると一気に整理される。ComponentA.vue<script> import { mapGetters } from "vuex"; // mapGettersをインポートする export default { // 配列 computed: mapGetters(["doubleCount", "tripleCount"]), } </script>配列ではなく、オブジェクトでも良い。
ComponentA.vue<script> import { mapGetters } from "vuex"; // mapGettersをインポートする export default { // オブジェクトでも良い computed: mapGetters({ doubleCount: "doubleCount", tripleCount: "tripleCount" }), } </script>必要なgettersだけを取ってくる事が出来るので是非使いこなしたい。
ちなみにこのままだと、computedにmapGetters以外に記述する事が出来ないので、普段使いではComponentA.vue<script> import { mapGetters } from 'vuex' export default { computed: { // スプレッド演算子(object spread operator)を使って組み込む ...mapGetters([ 'doubleCount', 'tripleCount', ]) } } </script>という感じで使うっぽい。
mutationsを使ってみる
stateを使う事でコンポーネント間のデータのやりとりは簡単にはなるけれど、
何でもかんでも自由にやりとりをさせ過ぎると、逆に分かりづらくなったり、追跡、管理が煩雑になる。そこで使用するのがmutationsという事のようだ。store.jsexport default new Vuex.Store({ state: { number: 2 }, mutations: { increment(state, str) { // 第一引数にstateをとり、実際の変更を記述する state.number += str; } } })呼び出す側ではstore.commitでmutationsを指定する。
ComponentA.vue<script> methods: { increment(){ this.$store.commit('increment', 2); }, }, <script>stateをあっちこっちで変更する事は思わぬバグを発生させる可能性があるから、
mutationsで扱いましょうという感じだろうか。また、mutationsにも
mapMutations
というヘルパーが存在する。ComponentA.vue<script> import { mapMutations } from "vuex"; // mapMutationsをインポートする export default { methods: { // mapGettersと同じようにスプレット演算子で書ける ...mapMutations(["increment","decrement"]), }, } </script>ちなみにmutationsは同期的でなければならないというルールがある。
では非同期を扱うにはどうしたらいいのか。actionを使ってみる
actionはmutationsと異なり、任意の非同期処理を含む事が出来る。
なので、setTimeoutで一定時間後に特定の処理を行うという記述も可能となっている。store.jsexport default new Vuex.Store({ state: { count: 2 }, actions: { increment({ commit },number) { commit('increment', number); } } })store内のactionsでcommitし、コンポーネントでdispatchする。
ComponentA.vue<script> methods: { increment() { this.$store.dispatch('increment', 2); } }, </script>非同期でもOK
store.jsactions: { incrementAsync ({ commit }) { setTimeout(() => { commit('increment') }, 1000) } }最後は
mapActions
ヘルパー、これまで出てきたmapと同じ感覚で使用出来る。ComponentA.vue<script> import { mapActions } from "vuex"; export default { methods: { // スプレット演算子で書ける ...mapActions(["increment","decrement"]), } </script>まとめると・・・
・stateでvuexの状態を管理
・gettersでstateの変更を算出
・mutationsでstateの状態を変更、commitで呼び出される
・actionで同期、非同期なデータの処理、必要に応じてcommitするこれらの基本に基づいて、導入するプロジェクト毎にどうVuexを使っていくかを取捨選択していく形になるという事が分かった。
- 投稿日:2020-11-15T22:21:56+09:00
AWS Lambdaを使ってサーバレスアプリを作成(CRUDのR)
記事を閲覧いただき、ありがとうございます。中村です!!
2020年10月にAWSエンジニアとして転職したので、AWSの予習も兼ねてLambdaを使ったアプリ作成について書いきます。AWS Lambdaとは?
ここでつらつら説明するより公式動画の方がわかりやすいという結論に至りました。まずはご覧ください。
以下を使用して作成ます。
フロントエンド : Vue.js
AWS(インフラ) : S3, API Gateway, Lambda, DynamoDB構成
構成はこんな感じです。
DBはDynamoDB、アプリケーションサーバの代わりにlambdaを使ってサーバレスにする。
lambdaプログラムをAPI Gatewayと連携し、APIとして呼べるようにする。
Webサーバのように使えるS3にフロントエンドのプログラムをデプロイする。
画面
画面こんな感じです。
めちゃシンプルですが、自分の尊敬する人/好きな有名人などの情報を記録するアプリを作ります。
(余談:gifって初めて使ったけど便利ですねぇ。)
手順
以下のような手順で作成します。
が、死ぬほど長くなってしまったので、本記事は①②までとしました!!
③〜⑤は、後ほど作成します!カミングスーン!①DynamoDBテーブルを作成する
1.1 テーブル作成
1.2 カラム作成②GETメソッド作成(CRUDのR)
Lambda関数作成
API Gateway作成
Vue.js作成③POSTメソッド作成(CRUDのC)
Lambda関数作成
API Gateway作成
Vue.js作成④Deleteメソッドの作成(CRUDのD)
Lambda関数作成
API Gateway作成
Vue.js作成⑤PUTメソッドの作成(CRUDのU)
前提条件
AWSアカウント作成ずみであること
(フルアクセス権限のIAMユーザを作ってそこから操作することをお勧めします。)① DynamoDBテーブルを作成する
※ここからAWSマネジメントコンソール画面を操作していきますが、
今後、画面のレイアウト、表示項目などが変わる可能性があります!
本記事と、実際の画面が違った場合は、心で感じ取って、うまく進めてください!!1.1 テーブル作成
AWSコンソールへログインし、DynamoDBの画面から「テーブル作成」を開く
テーブル名(作ろうとしているアプリ名が良いですかね)とプライマリーキーを入力し、
「作成」を押下する
1.2 カラム作成
画面左のメニューから「テーブル」をクリック
作成したテーブル名クリック
「項目」タブを開き
「項目の作成」をクリック
「+」をクリック、「Append」をクリック、「String」を選択
それぞれのカラムに適当なデータを入力します。(ドラえもんはPerson?? まぁいいやw)
「保存」ボタンを押下します。
※データを入れてあげないと下のような赤文字のエラーが出ます。。。?
② GETメソッド作成(CRUDのR)
ここからCRUDごとに処理を作っていきます。
2.1 Lambda関数作成
AWSコンソールからlambda画面へ行き、「関数の作成」をクリックする
「一から作成」を選択(デフォルト)、
関数名を入力(今回はgetメソッドなので-get
とする)
「Node.js」を選択(デフォルト)
「関数の作成」をクリックする
2.1.1 アクセス権限設定(IAMロール)
作成したlambda関数は現時点でDynamoDBにアクセスできません。(上記の作成手順の時に「アクセス権限」がノータッチだった為)
なので、先にそちらの設定をやっちゃいます。作成したlambda関数画面から「アクセス権限」タブを開き、ロール名をクリックします。
このIAMロールはlambda関数作成時に自動で作られています。
IAMロール画面が開き、「ポリシーをアタッチします」をクリック
検索窓に「dynamo」くらいで検査し、「AmazonDynamoDBFullAccess」を選択する
「ポリシーのアタッチ」をクリックする
2.1.2 Lambda関数内にGet処理を書く
いよいよlambda関数にgetメソッドを書いていきます。
index.jsconst AWS = require('aws-sdk') const dynamo = new AWS.DynamoDB.DocumentClient() exports.handler = (event, context, callback) => { const httpMethod = event.httpMethod const params = { // ここで作成したDynamoDBテーブル名を指定 'TableName': 'lambdaapp-persons' } dynamo.scan(params, function (err, data) { const response = { statusCode: 200, body: JSON.stringify(data.Items) } callback(null, response) }) };コードエディタの部分にコードを貼り付け、「Deploy」をクリックする
2.1.3 Lambda関数をテストする
「テストイベントの選択」プルダウンより「テストイベントの設定」を選択
任意のイベント名を入力し、「作成」をクリック
※Getメソッドではパラメータは特にないので、エディタの操作は不要
作成したテストイベントが選択された状態で「テスト」をクリック
結果の詳細を見てみると、DynamoDBに登録したデータが取れているのが分かります。
2.2 API Gateway作成
2.2.1 API Gateway作成
先ほど作成したGetメソッドをフロント側から呼び出せるようにAPI Gatewayを作成します。
API Typeを選択します。「REST API」の「構築」をクリック
「新しいAPI」(デフォルト)を選択、
API名を入力(1つのAPIでCRUD全て追加するので、ここではget、postなどは入れていません)、
「APIの作成」をクリック
2.2.2 メソッドの作成
作成したAPI Gatewayの画面に遷移します。
「アクション」から「メソッドの作成」をクリック
「GET - セットアップ」画面が現れます。
統合タイプ「lambda関数」を選択し、先ほど作成したlambda関数名を入力
「保存」をクリック
Lambda関数を見てみると、上記「権限を与える」操作によりAPI Gatewayがトリガーとして追加されます。
2.2.3 メソッドをテスト
作成したAPI Gatewayメソッドからlambda関数が実行できるかテストします。
ボタンの下にテスト結果が表示されます。
ステータス:200であればOK。 bodyの所にDynamoDB作成時に作ったデータが入っていますね。
2.2.4 CORSの有効化
説明しよう! CORSとは!
Cross-Origin Resource Sharing(オリジン間リソース共有)の略で
異なるオリジンとの通信ができるようにブラウザへ指示する仕組みだそうです。
今回の例で言うと、HTMLファイルをS3にアップロードして、S3から発行されたURLでブラウザに表示します。
さらにHTMLファイルの中にAPI GatewayのURLを書いて、メソッドを呼び出しています。
①S3のURLと②API GatewayのURLが異なるオリジンということになり、
それらを同時に扱うためにCORSが必要という事ですね。
参考GETメソッドに✅が入っていることを確認し、「CORSを有効にして既存のCORSヘッダーを置換」をクリック
2.2.5 メソッドをデプロイ
デプロイされるステージはプルダウンから「新しいステージ」を選択
ステージ名に今回は「dev」と入力。※開発用だったらdev、商用だったらprodといった具合に分けるようですね
ステージの説明、デプロイメントの説明の入力は任意
「デプロイ」をクリック
デプロイが成功すると、APIのURLが表示されます。先ほど入力したステージ名が末尾に入っていますね。
2.3 Vue.js作成
2.3.1 ローカルでVue.jsを作成
まずはローカル環境にindex.htmlを作成し、vue.jsで書いていきます。
[作成したAPI GatewayのURL]
の箇所をCtrl + F
で探して頂き、作成したAPIのURLを書きます。
ソースはGithubでも参照いただけます。index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Persons</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> </head> <body> <div id="app"> <div class="container mt-5"> <div class="row"> <div class="col-md-12"> <h1>{{ title }}</h1> <h1>{{ psersons }}</h1> <p>Please add persons whom you like or respect, and describe them in detail.</p> <table class="table"> <thead class="thead-dark"> <tr> <th>ID</th> <th>Name</th> <th>Description</th> </tr> </thead> <tbody> <tr v-for="person in persons"> <td>{{ person.id }}</td> <td>{{ person.name }}</td> <td>{{ person.description }}</td> </tr> </tbody> </table> </div> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script> const vue = new Vue({ el: "#app", data: { title: 'Persons', form: { id:'', name:'', description:'' }, persons: '', editIndex: -1, createFlag: true }, mounted(){ axios .get('[作成したAPI GatewayのURL]') .then(response => (this.persons = JSON.parse(response.data.body))) .catch(function(error){ alert('Personsデータの取得に失敗しましたっ!\n(・Д・)ナンダッテェ!!' ); console.log(error); }); } }); </script> </body> </html>HTMLファイルをブラウザで実行すると以下のような画面が表示されます。
(ドラえもんだけだと寂しいので、もう1レコード追加してみました。てかPersonじゃないっすねwww)
2.3.2 作成したVue.jsをS3へアップロード
まずS3バケットを作成します。
S3画面へ飛び、「バケットを作成」をクリック
「パブリックアクセスを全てブロック」の✅を外し、下2つに✅をつける。
なかなか分かりづらいですが、要するにS3にあるHTMLファアイルをインターネットから参照できるようにしています。
さらに下へスクロール。
バケットのバージョニングは有効にすると、間違ってアップロードした時なんかに
元に戻せたりするみたいですね。お好みでどうぞ。
筆者はデフォルトの「無効」とします。
正常に作成されました、と言うメッセージが表示されます。
作成したS3バケットをクリック
作成したバケットの画面に戻り、
アップロードしたHTMLファイルに✅をつけ、
「アクション」から「公開する」をクリック
URLをブラウザで実行して、動作確認
(余談ですが、Qiita執筆中にChromeのアプデが来ましたね。右上の「更新」ってやつ)
終わりに
Qiitaを書いてみて
Qiitaを書くこと自体にめちゃめちゃ時間がかかってしまい、
Getメソッドのみになってしまいました!!
画面操作をここまで細かく説明する必要あるのか?という疑問も浮かびましたが、なるべく初学者目線で、をモットーに
そのまま描き続けました。
果たして分かりやすい記事になったのだろうか。。。!!lambdaについて
今回はシンプルなアプリを作成しましたが、
きっともっと色んなことがlambdaを使ってできるはずなので、
(動画や画像など、様々なデータを暑かったりとか)
引き続き、何かを作りながら知見を深めていこうと思います!!
- 投稿日:2020-11-15T22:02:33+09:00
Vue-router基本まとめ 備忘録
Vou-router
VueRouterとは
Webページのルーティングを実装してくれる公式のプラグイン。ユーザーが入力したパスに対してどのようなページ(コンポーネント)を渡すのかをVueRouterによって定義される。
router-rink
- router-linkコンポーネントはナビゲーションとして使われる
- リンク先を'to'プロパティに指定
- デフォルトで<router-link>は<a>タグとして描画される
<p> <router-link to="/foo">Go to Foo</router-link> <router-link to="/Bar">Go to Bar</router-link> </p>router-view
- ルートとマッチしたコンポーネントがここへ描画される
<router-view></router-view>Routerの設定
// 1. ルートコンポーネントを定義する // 他のファイルからインポートすることもできます const Foo = { template: '<div>foo</div>' } const Bar = { template: '<div>bar</div>' } // 2. ルートをいくつか定義する // 各ルートは 1 つのコンポーネントとマッピングされる必要がある。 // このコンポーネントは実際の `Vue.extend()`、 // またはコンポーネントオプションのオブジェクトでも構わない const routes = [ { path: '/foo', component: Foo }, { path: '/bar', component: Bar } ] // 3. ルーターインスタンスを作成して、ルートオプションを渡す const router = new VueRouter({ routes // `routes: routes` の短縮表記 }) // 4. root となるインスタンスを作成してマウントする // アプリケーション全体がルーターを認知できるように、 // ルーターをインジェクトすることを忘れない。 const app = new Vue({ router }).$mount('#app')
- モジュールシステムを使っている場合 (例: vue-cli 経由で)、Vue と VueRouter をインポートし、
Vue.use(VueRouter)
を呼び出す必要がある。動的ルートマッチング
パターンを使って同じコンポーネントにルートをマップする必要がしばしばある。例えば、異なるユーザーIDを持つ場合など。これで /user/foo や /user/bar などの URL 両方とも同じルートにマッチする。
動的セグメントはコロン:を使って表される
const User = { template: '<div>User</div>' } const router = new VueRouter({ routes: [ // コロンで始まる動的セグメント { path: '/user/:id', component: User } ] })
- ルートがマッチした時、動的セグメントの値は全てのコンポーネント内でthis.$router.paramsとして利用可能
const User = { template: '<div>User {{ $route.params.id }}</div>' }実際の画像:pathの値によって動的に変化する
パラメーター変更の検知
- ルートのパラメーターを使う際に特筆すべき点は、ユーザーが /user/foo から /user/bar へ遷移するときに同じコンポーネントインスタンスが再利用されるということ
- これはコンポーネントのライフサイクルフックが呼ばれないことを意味している
- 同じコンポーネントでパラメーター変更を検知するためには、 $route オブジェクトを watch する、またはbeforeRouteUpdateナビゲーションガードを使用
const User = { template: '...', watch: { $route (to, from) { // ルートの変更の検知... } } }const User = { template: '...', beforeRouteUpdate (to, from, next) { // ルート変更に反応する... // next() を呼び出すのを忘れないでください } }すべてキャッチするルート/404 Not fountルート
通常のパラメータは、/ で区切られた url フラグメントの間にある文字だけにマッチする。何でも一致させたい場合は、アスタリスク(*)を使うことができる.
{ // 全てにマッチします path: '*' } { // `/user-`から始まる任意のものにマッチします path: '/user-*' }
- アスタリスク ルートを使用するときは、アスタリスク ルートが最後になるようにルートを正しく順序付けてる。
- { path: '*' }ルートは、通常クライアントサイドの404ページで使われる
マッチングの優先度
しばしば同じURLで複数のルートがマッチすることがあります。そのようなケースではマッチングの優先度はルートの定義された順番によって決定されます。先に定義されたルートほど優先度が高くなります。
つまり、404のエラーページへのルートは最後におく
- 投稿日:2020-11-15T22:02:12+09:00
vueのプロジェクトを「ロリポップ!」へGitHub Actionsをつかって自動デプロイする
はじめに
ロリポップなどのレンタルサーバにvueなどのものを手動でデプロイするのってめんどくさいですよね。
今回は「テスト->ビルド->デプロイ」の一連の流れをGitHub Actionsで自動にやってくれるように設定します。前提
- Vueのプロジェクトを作成しておりGitHubで管理している
- 今回レンサバの例としてつかうのはロリポップ
workflowの作成
vueのプロジェクトのリポジトリの「Actions」タブを開くと以下のようなページになります。
「Continuous integration workflows」の中にある「Node.js」を選択し、「set up this workflow」を押します。
このようなページになったら、好きなファイル名をつけ「Start commit」を押して保存しましょう。workflowの編集
ワークフローの編集をしていきます。
FTPの接続情報などは、ファイルに書いてしまうと良くないので「GitHub Secrets」にあとで書いていきます。.github/workflows/hoge.yml# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions name: Node.js CI on: push: branches: [ main ] - pull_request: - branches: [ main ] jobs: build: runs-on: ubuntu-latest strategy: matrix: - node-version: [10.x, 12.x, 14.x] + node-version: [12.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - run: npm ci - - run: npm run build --if-present - - run: npm test + - name: npm install # パッケージをインストール + run: npm install + - name: test + run: npm run test:unit # unitテストを実行 + - name: build # ビルド + run: npm run build --if-present + + - name: List output files + run: ls # ファイルリストを表示 + + - name: FTP-Deploy-Action + uses: SamKirkland/FTP-Deploy-Action@2.0.0 # FTPを使ってサーバーにDeployするアクションを実行 + env: + FTP_SERVER: ${{ secrets.FTP_SERVER }} # FTPサーバーのURLを設定 + FTP_USERNAME: ${{ secrets.FTP_USERNAME }} # FTPのユーザー名を設定 + FTP_PASSWORD: ${{ secrets.FTP_PASSWORD }} # FTPのパスワードを設定 + LOCAL_DIR: dist # どのディレクトリのデータをアップロードするか + REMOTE_DIR: / # ロリポップ!FTPサーバのどのディレクトリにアップロードするか + ARGS: --deleteこれで、mainブランチにpushされた際に自分のロリポップのFTPサーバのルートにvueのビルドした成果物が上げられるようになります。
FTP接続情報の設定
「GitHub Secrets」にFTPの接続情報を設定します。
まず、「ロリポップ!」にログインして「ユーザー設定ー>アカウント情報」を開いてください。
「サーバー情報」にある「FTPサーバー」、「FTPアカウント」、「FTPパスワード」をメモってください。
メモったら、先程のリポジトリに戻って、リポジトリの「Settings」を開きます。
その中の「Secrets」を選択し、「New Repository secrets」を押して以下のものを作成します。
- FTP_SERVER(先程メモったFTPサーバーをかく)
- FTP_USERNAME(先程メモったFTPアカウントをかく)
- FTP_PASSWORD(先程メモったFTPパスワードをかく)
これで、設定はすべて終了です。
このあとからmainブランチにPushされた場合、自動で「テスト・ビルド・デプロイ」が行われるようになっています。
結果や状態を見たい場合は、「Actions」を開くと以下のように表示されると思います。
このようにチェックマークがついていればすべてのjobが正常に終わっています。
参考にさせていただいた記事など
- 投稿日:2020-11-15T18:51:56+09:00
【Rails API + Vue】Active Storageを使って画像をアップロード・表示する
バックエンドはRails、フロントエンドはVueといった構成のときにActive Storageを使って画像をアップロード・表示する方法を、プロジェクトを1から作りながらまとめます
ソースコードはGitHubで公開しています画像をアップロード・表示する処理の流れをざっくりと
- Vueで画像を選択して送信するための画面を作る
- 送信ボタンを押した時、画像をアップロードする処理を行うRails APIを呼び出す
- Railsは受け取った画像を
storage
ディレクトリに保存し、保存した画像のURLを返す- Vueで画像のURLを受け取り、表示する
Railsプロジェクトを作成する
↓のようなディレクトリ構成で作成していきます
rails-vue-file-uploader-sample └── backend # Railsプロジェクト └── frontend # VueプロジェクトまずはRailsプロジェクトをAPIモードで作成します
$ mkdir rails-vue-file-uploader-sample $ cd rails-vue-file-uploader-sample $ rails _6.0_ new backend --api $ cd backend $ rails db:createActive Storageを使えるようにする
$ rails active_storage:install $ rails db:migrateこれらを実行するとactive_storage_blobsとactive_storage_attachmentsという名前の2つのテーブルが作成されます
これらはActiveStorage::BlobとActiveStorage::Attachmentの2つのモデルで扱われます
- ActiveStorage::Blob:アップロードファイルのメタ情報を管理するためのモデル
- ActiveStorage::Attachment:主となるモデルとActiveStorage::Blobとの中間テーブルに相当するモデル
例えばPostモデルに画像を持たせる場合は次のような関係になります
モデルを作成する
titleとimageを属性に持つPostモデルを作成します
imageの型にはattachment
を指定します$ rails g model post title:string image:attachment $ rails db:migrateこれらを実行するとpostsテーブルが作成されます
マイグレーションファイルを見てみるとわかるのですが、postsテーブルにimageカラムは作られません
image属性の中身はActiveStorage::Blob及びActiveStorage::Attachmentに保存され、それを参照するようになります生成された
app/models/post.rb
を見ると、has_one_attached :image
が指定されています
この指定によって画像を参照できるようになりますapp/models/post.rbclass Post < ApplicationRecord has_one_attached :image endコントローラを作成する
$ rails g controller posts
app/controllers/posts.rbclass PostsController < ApplicationController def index render json: Post.all end def create post = Post.new(post_params) if post.save render json: post else render json: post.errors, status: 422 end end def destroy post = Post.find(params[:id]) post.destroy! render json: post end private def post_params params.permit(:title, :image) end endとりあえず普通に書きます
routesも設定しますconfig/routes.rbRails.application.routes.draw do scope :api do resources :posts, only: [:index, :create, :destroy] end end保存したファイルのURLを返すようにする
Postモデルに、紐づいている画像のURLを取得するメソッドを追加します
url_for
メソッドを使うためにRails.application.routes.url_helpers
をincludeする必要がありますapp/models/post.rbclass Post < ApplicationRecord include Rails.application.routes.url_helpers has_one_attached :image def image_url # 紐づいている画像のURLを取得する image.attached? ? url_for(image) : nil end endアクションで返すJSONに
image_url
の値を追加しますapp/controllers/posts.rbclass PostsController < ApplicationController def index render json: Post.all, methods: [:image_url] # ここを変更 end def create post = Post.new(post_params) if post.save render json: post, methods: [:image_url] # ここを変更 else render json: post.errors, status: 422 end end def destroy post = Post.find(params[:id]) post.destroy! render json: post end private def post_params params.permit(:title, :image) end end画像のURLを取得するために
config/environments/development.rb
に次の設定を追加する必要がありますconfig/environments/development.rbRails.application.configure do ... # これを追加 Rails.application.routes.default_url_options[:host] = 'localhost' Rails.application.routes.default_url_options[:port] = 3000 endVueとのAPI通信をするためにCORSの設定をしておきます
Gemfileのgem 'rack-cors'
のコメントを外してbundle install
し、config/initializers/cors.rb
を次のように書きますconfig/initializers/cors.rbRails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins 'http://localhost:8080' resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] end endVueプロジェクトを作成する
ここからはVueを書いていきます
まずはルートディレクトリに戻ってVueプロジェクトを作成します$ cd rails-vue-file-uploader-sample $ vue create frontend $ cd frontendvue createの設定は以下のように選択しました
? Please pick a preset: Manually select features ? Check the features needed for your project: Vuex, Linter ? Pick a linter / formatter config: Prettier ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ? Save this as a preset for future projects? NoVuexストアを作成する
Vuexを次のように書きます
axiosを使用するのでインストールしておきます$ npm install --save axiossrc/store/modules/posts.jsimport axios from "axios"; const apiUrlBase = "http://localhost:3000/api/posts"; const headers = { "Content-Type": "multipart/form-data" }; const state = { posts: [] }; const getters = { posts: state => state.posts.sort((a, b) => b.id - a.id) }; const mutations = { setPosts: (state, posts) => (state.posts = posts), appendPost: (state, post) => (state.posts = [...state.posts, post]), removePost: (state, id) => (state.posts = state.posts.filter(post => post.id !== id)) }; const actions = { async fetchPosts({ commit }) { try { const response = await axios.get(`${apiUrlBase}`); commit("setPosts", response.data); } catch (e) { console.error(e); } }, async createPost({ commit }, post) { try { const response = await axios.post(`${apiUrlBase}`, post, headers); commit("appendPost", response.data); } catch (e) { console.error(e); } }, async deletePost({ commit }, id) { try { axios.delete(`${apiUrlBase}/${id}`); commit("removePost", id); } catch (e) { console.error(e); } } }; export default { namespaced: true, state, getters, mutations, actions };src/store/index.jsimport Vue from "vue"; import Vuex from "vuex"; import posts from "./modules/posts"; Vue.use(Vuex); export default new Vuex.Store({ modules: { posts } });画像をアップロードするコンポーネントを作成する
画像を選択して送信するフォームを表示するための
src/components/PostForm.vue
を作成しますsrc/components/PostForm.vue<template> <div> <h2>PostForm</h2> <section> <label for="title">title: </label> <input type="text" name="title" v-model="title" placeholder="title" /> </section> <section> <label for="image">image: </label> <input type="file" id="image" name="image" accept="image/png,image/jpeg" @change="setImage" /> </section> <section> <button type="submit" @click="upload" :disabled="title === ''">upload</button> </section> </div> </template> <script> import { mapActions } from "vuex"; export default { name: "PostForm", data: () => ({ title: "", imageFile: null }), methods: { ...mapActions("posts", ["createPost"]), setImage(e) { e.preventDefault(); this.imageFile = e.target.files[0]; }, async upload() { let formData = new FormData(); formData.append("title", this.title); if (this.imageFile !== null) { formData.append("image", this.imageFile); } this.createPost(formData); this.resetForm(); }, resetForm() { this.title = ""; this.imageFile = null; } } }; </script>選択された画像は
e.target.files
で取り出すことができます
POSTリクエストを送信するときはFormData
に必要な値をappendしたものをパラメータとして指定します画像を表示するコンポーネントを作成する
保存されている画像を取得して表示するための
src/components/PostList.vue
を作成しますsrc/components/PostList.vue<template> <div> <h2>PostList</h2> <div v-for="post in posts" :key="post.id"> <h3>{{ post.title }}</h3> <img :src="post.image_url" /> <br /> <button type="submit" @click="del(post.id)">delete</button> </div> </div> </template> <script> import { mapActions, mapGetters } from "vuex"; export default { name: "PostList", created() { this.fetchPosts(); }, computed: { ...mapGetters("posts", ["posts"]) }, methods: { ...mapActions("posts", ["fetchPosts", "deletePost"]), del(id) { this.deletePost(id); } } }; </script>
<img :src="post.image_url" />
でsrcに取得したURLを指定して表示させます最後にApp.vueを編集してコンポーネントを表示します
src/App.vue<template> <div id="app"> <PostForm /> <PostList /> </div> </template> <script> import PostForm from "./components/PostForm.vue"; import PostList from "./components/PostList.vue"; export default { name: "App", components: { PostForm, PostList } }; </script>完成
画像を選択してアップロードボタンを押すと、画像が保存されて表示されます
画像はbackend/storage
ディレクトリにバイナリ形式で保存されますソースコードはGitHubで公開しています
参考になれば嬉しいです
https://github.com/youichiro/rails-vue-file-uploader-sample参考
- 投稿日:2020-11-15T13:53:21+09:00
Error: EPERM: operation not permitted, open '/usr/src/app/src/App.vue'
npm run serve をした際に operation not permitted が出た。
Error: EPERM: operation not permitted, open '/usr/src/app/src/App.vue'vue-cliアプリをビルドしようとすると、エラーが発生します。依存関係のインストールとアプリの提供は正常に機能していました。
結論
npm install -g @vue/cli --cache /tmp/empty-cache上記コマンドで解決。
環境
- Docker
- Vue
- Vue CLI
$ npm --version $ @vue/cli 4.5.8起きた原因
Vue CLI Issue をみる限り、みなさん npm cache clean force はしているようですね。
自分が今回起きたのは 「build に時間がかかるな」 と思ったので 最近していなかった再起動をしました。
↑ 怒らないでください笑そうすると 今回のErrorが出ました。
理由は正直わかりません。もしわかる方がいらっしゃいましたら、ご教授頂けますと幸いです。
- 投稿日:2020-11-15T13:18:13+09:00
Nuxt.js + Firebaseで簡易書籍検索アプリを2日で作った話
はじめに
社会人2年目のエンジニアです。普段はC#、TypeScriptを使ったWebアプリの開発やXamarin.Formsを使ったモバイル開発を行っています。
普段の業務では全く触る機会はありませんが、モダンなフロントエンドフレームワークに興味があったので
勉強がてらNuxt.jsとFirebaseで作った「Bookle」という簡易書籍検索アプリを作った話をまとめます。
期間としては、2日(16h)くらいで作成できました。プロジェクト作成
プロジェクトの作成にはnpxを利用しました。
$ npx create-nuxt-app <project-name>上記コマンドを実行すると、いくつか聞かれるのですが、今回は以下のように選択しました。
質問 選択した項目 Programming language TypeScript Package manager Npm UI framework Vuetify.js Nuxt.js modules Axios Linting tools ESLint, Prettier Testing framework None Rendering mode Single Page App Deployment target Server (Node.js hosting) Development tools 選択なし Continuous integration None Version control system Git Vuexの導入
VuexとTypeScriptを組み合わせて使いました。
VuexをTypeScriptで書く場合様々な方法があるのですが、今回はvuex-module-decorators
を使いました。
VuexでTypeScriptを用いる際にちょっとつまずいたので、ちょっとだけサンプルコードを載せます。以下のサイトを参考にして作成しました。
https://github.com/championswimmer/vuex-module-decoratorsディレクトリ構造はこのような感じです
├── store | ├── Book | | └── Interface.ts | ├── book.ts | └── index.ts | ├── utils └── store-accessor.tsbook.tsimport { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators'; import { GoogleBookItem } from './Books/Interface'; @Module({ stateFactory: true, namespaced: true, name: 'books' }) export default class Books extends VuexModule { // state private books: GoogleBookItem[] = []; // Getter public get Books(): GoogleBookItem[] { return this.books; } @Mutation public SET_BOOKS(books: GoogleBookItem[]) { this.books = books; } @Mutation public SET_BOOK(book: GoogleBookItem) { this.books.push(book); } @Action({}) public async setBooks(books: GoogleBookItem[]): Promise<void> { // 省略 this.SET_BOOKS(books); } @Action({}) public async setBook(book: GoogleBookItem): Promise<void> { this.SET_BOOK(book); } }index.tsimport { Store } from 'vuex'; import { initializeStores } from '~/utils/store-accessor'; const initializer = (store: Store<any>) => initializeStores(store); export const plugins = [initializer] export * from '~/utils/store-accessor'store-accessor.tsimport { Store } from 'vuex'; import { getModule } from 'vuex-module-decorators'; import Books from '@/store/books' let bookStore: Books; function initializeStores(store: Store<any>): void { bookStore = getModule(Books, store); } export { initializeStores, bookStore }上記のような手順を踏むことで別途初期化せずにつかうことができます。
またTypeScriptを使用しているので、TypeSafeにストアへアクセスすることができます。(使用例)
index.vue<template> <!-- 省略 --> </template> <script lang="ts"> import Vue from 'vue' import { bookStore } from '@/store' import axios from '@nuxtjs/axios' import { GoogleBookItem } from '~/store/Books/Interface' export default Vue.extend({ // 省略 methods: { async searchBooks() { await this.$axios.$get('https://hogehoge/api') .then((x: GoogleBookItem) => { bookStore.setBooks(x); }) .catch(error => { console.error(error); }); } } })GoogleBooksApiを叩いて書籍情報を取得
書籍情報は様々なAPIがあるそうですが、今回はGoogle Books APIを使用しました。
基本的な使い方はこんな感じです。
this.$axios.$get('https://www.googleapis.com/books/v1/volumes?q=title:search value')
例えば以下のようにしてAPIを叩くことができます。
sample.tsthis.$axios.$get('https://www.googleapis.com/books/v1/volumes?q=title:nuxt&maxResults=30')デフォルトだと10件しか取得できないですが、maxResultsをつけて件数の上限を増やすこともできます。(0 ~ 40で指定可能)
Firebaseを利用してデプロイ
HostingにはFirebase hostingを利用しました。
無料で利用できて、比較的わかりやすかったので利用しました。完成したアプリ
アプリ
Bookleトップ画面
検索
タイトルでの検索しかできませんが、入力後Enterまたは検索ボタンを押すと関連書籍が出てきます。
詳細ページ
本のカードを選択すると、詳細ページに遷移します。
タイトルや著者が閲覧できます。(画像が荒い・・・)
まとめ
NuxtとFirebaseを使うと比較的簡単にアプリを作ることができるんだなと感じました。
Vue.jsの勉強が必要だったりしますが、JSの知識があればそこまで難しくない気がしました。
ただVuexはちょっとムズイ・・・参考資料
Vueドキュメント
今回は使ってませんが、Vue 3.0も気になる
https://jp.vuejs.org/v2/guide/Firebaseドキュメント
日本語なのでわかりやすい
https://firebase.google.com/docs/?hl=ja
- 投稿日:2020-11-15T01:17:16+09:00
Vue.jsでカスタムなページタイトルを設定する
はじめに
ちょっとの間悩んでた表題の問題が他人のコード見てたらふと解決したので軽くまとめていきます。Vue.jsでページタイトルをデフォルトのアプリ名から変更する方法です。
手順
まず、Vue.jsでページタイトルを規定してるコードはpublic/index.html内にあります。
public/index.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <title><%= htmlWebpackPlugin.options.title %></title> <!--ここですここ、ここ--> </head> <body> <noscript> <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>しかし、ここは変数で指定してありますしあまりこのファイルを弄るのはよくないようです。なのでどこでこの変数を指定するのか書いていきます。
まず、rootフォルダにvue.config.jsというJavaScriptファイルを作ります。
そしてそこに次のように入力してください。vue.config.jsmodule.exports = { pages: { index: { entry: 'src/main.js', // ここは変えないで title: 'ページタイトル', // 好きな文字列をいれてください } } }そうしてビルドし直すと無事ページタイトルが変更されてると思います。
最後に
サイト自体のタイトルの後にページのタイトルをいれる方法はそのうち実装しようと思うので成功したら追記します。
- 投稿日:2020-11-15T01:15:23+09:00
vue.jsで湯婆婆を実装してみた(はやりに乗っかてみた)
初めに
原作者様 Javaで湯婆婆を実装してみる
はやりに乗っかってみました
これで湯屋から契約作業のwebページ作成案件が来ても安心です湯婆婆コード
VueCLI version2.6.12を使用しています
設定はデフォルトですyubaba.vue<template> <div class="yubaba"> <div>契約書だ。そこに名前を書きな!</div> <input type="text" v-model="name"><br><br> <button v-on:click="outputName(),createNewName()" id="keiyakubtn">契約する</button> <div>{{displayName}}</div><br> <div>{{newName}}</div> <div v-if="!name">{{allClear()}}</div> <button v-on:click="contractCancellation">契約を破棄する</button> </div> </template> <script> export default { data(){ return { name: '', displayName:'', newName: '', setTimeID: '', }; }, methods: { outputName() { if (!this.name) { // フォームが空白だったらアラートを表示する alert('名前を書きな!!') } else if (this.name) { // フォームに入力された文字列を表示する this.displayName = 'ふん。「'+this.name+'」っていうのかい。贅沢な名前だねぇ' } }, createNewName() { // this.newName = this.name.substring(0,1) // 取得した文字列を配列に変換 const displayNameArray = this.name.split('') // 取得した配列を表示 // clearTimeout(this.setTimeID) this.setTimeID = setTimeout(() => { const randomName = displayNameArray[Math.floor(Math.random() * this.name.length)] this.newName = 'お前の名前は「'+randomName +'」だよ。いいかい「'+randomName+'」だよ。わかったら返事をするんだ!!' },3000) }, allClear() { // フォームから要素が消えたら if (!this.name) { // すべてを空白にする this.displayName = '' this.newName = '' } }, contractCancellation() { alert('契約破棄') this.name = ''; this.displayName = '' this.newName = '' for(let i = 0; i < 1000; i++) { document.write(' わたしゃ残念だよ ') } } }, } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> h3 { margin: 40px 0 0; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } #keiyakubtn { background-color: red; color: white; width: 4cm; height: 4cm; } </style>実行画面
ちょっとした説明
以下使用した変数たちです
name: '', displayName:'', newName: '', setTimeID: '',
契約書ページ
以下のHTMLコードはただ単にボタンやフォームの表示、スクリプトの呼び出しなどしています。
<template> <div class="yubaba"> <div>契約書だ。そこに名前を書きな!</div> <input type="text" v-model="name"><br><br> <button v-on:click="outputName(),createNewName()" id="keiyakubtn">契約する</button> <div>{{displayName}}</div><br> <div>{{newName}}</div> <div v-if="!name">{{allClear()}}</div> <button v-on:click="contractCancellation">契約を破棄する</button> </div> </template>
契約処理の実装
以下のコードでは、なんちゃって空白処理と奪われる前の名前表示を実装をしています。
名前が入力されなかったらアラートを表示します。
名前を入力したら湯屋の一員として一歩前進できます!outputName() { if (!this.name) { // フォームが空白だったらアラートを表示する alert('名前を書きな!!') } else if (this.name) { // フォームに入力された文字列を表示する this.displayName = 'ふん。「'+this.name+'」っていうのかい。贅沢な名前だねぇ' }
名前を奪われます
以下のコードでは名前を奪う処理を実装しました。
入力された名前を配列形式に変換してからからランダムで一文字選びそれを新しい名前にします。createNewName() { // 取得した文字列を配列に変換 const displayNameArray = this.name.split('') // 取得した配列を表示 this.setTimeID = setTimeout(() => { const randomName = displayNameArray[Math.floor(Math.random() * this.name.length)] this.newName = 'お前の名前は「'+randomName +'」だよ。いいかい「'+randomName+'」だよ。わかったら返事をするんだ!!' },3000) }
見たまんまです。フォームが空白になると要素が消えます。
作成した理由はとくにありませんwallClear() { // フォームから要素が消えたら if (!this.name) { // すべてを空白にする this.displayName = '' this.newName = '' } }
おばばのやさしさ
湯婆婆からの救済処置です。
契約破棄ボタンのスクリプトです。
破棄したら怖いことになります。contractCancellation() { alert('契約破棄') this.name = ''; this.displayName = '' this.newName = '' for(let i = 0; i < 1000; i++) { document.write(' わたしゃ残念だよ ') } } }説明なんてなかったんや...
最後に
今回初めての投稿です。目を通してくれた方がいましたらとてもうれしいです。
ブログ的なもの自体初めての経験なのでとても読みにくいと思います。
勉強中なのでご容赦いただきたいです。。。
気が向いたらこれからも、はやりに乗っかってみたり、自分でネタ的なものを書こうかなとおもいます
趣味でコーディングの勉強をしている学生なのでいろいろと適当です。
添削やコメントなど頂けましたら喜びます。
拙い文章でしたが最後まで見ていただきありがとうございました。