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

<初心者>Vue.jsとviz.jsでgraphvizのオンラインエディタを作ってみた。<Vue.js練習>

Vue.jsの公式にはマークダウンエディタの例がある。これのgraphviz版を作ってみる。
公式のマークダウンエディタ

作ったもの

Viz.jsにはサンプルがあるので、この記事で作っているのは車輪の再発明(バージョンダウン)です。
Vue.jsもViz.jsもcdnから読み込むため、htmlファイル1つで完結します。

test10.gif

Viz.jsとGraphvizについてはそれぞれの公式参照。
Viz.js-公式
Viz.j-github
Graphviz-公式

サンプルだけ引用します。
Graphvizを使用してDOT言語でこのように書くと

digraph G {
    subgraph cluster_0 {
        style=filled;
        color=lightgrey;
        node [style=filled,color=white];
        a0 -> a1 -> a2 -> a3;
        label = "process #1";
    }
    subgraph cluster_1 {
        node [style=filled];
        b0 -> b1 -> b2 -> b3;
        label = "process #2";
        color=blue
    }
    start -> a0;
    start -> b0;
    a1 -> b3;
    b2 -> a3;
    a3 -> a0;
    a3 -> end;
    b3 -> end;
    start [shape=Mdiamond];
    end [shape=Msquare];
}

こう出力される。
index.png

中身

最低限、下記をhtml形式で保存するだけで動きます。
CSSとpng保存用のmethodが付いた全文はgithubで公開しています。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>Vue.js-Viz.js</title>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
  <script type="text/javascript" src="https://unpkg.com/viz.js@1.8.0"></script>
  <style>
  </style>
</head>
<body>
  <div class="container">
    <div id="graph">
      <div class="write">
        <h2>入力</h2>
        <textarea v-model="input"></textarea>
      </div>
      <div class="out">
        <h2>結果</h2>
        <div class="graph" v-html="svg"></div>
      </div>
    </div>
  </div>
</body>
<script>
  new Vue({
    el: '#graph',
    data: {
      input: `digraph x {
graph [
charset = "UTF-8",
rankdir = LR,
];
start -> a0;
start -> b0;
a1 -> b3;
b2 -> a3;
a3 -> a0;
a3 -> end;
b3 -> end;
}
`,
    },
    computed: {
      svg: function() {
        return Viz(this.input, {
          format: 'svg'
        });
      },
    },
});
</script>
</html>


vue.jsで初めて作ったwebサービスが外部サーバーを挟んでもっさりしており、なにかリアルタイムで書き換わるものを作ってみたかったので作りました。

まだまだ勉強を始めたばかりです。突っ込みどころがあるかもしれません。お気づきの点ありましたら、コメントなどいただけますと大変ありがたいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Vue]v-ifに特定のメソッドを連動させたい場合、v-onがとても手軽な件

いきなりサンプル

See the Pen [Vue]v-ifのメソッドを連動させる by riotam (@riotam4) on CodePen.

サンプルの使い方

入力欄に「b」「y」「r」のいずれかを入力したら、下の文字が非同期的に変わります。
「r」を入力した場合だけ、メソッドが発火してalertがでます。

ポイント

当初は、dataのstateをwatchフックで監視して、変更のたびにif分岐させる必要があるかと考えていました。
しかし、サンプルの通り、v-ifのあるタグ内にそのままv-on="メソッド名"とすれば、v-ifがtrueのときに勝手に発火してくれます。

最後に

v-ifとv-onの使い方として、あまりいい方法ではないのかもしれませんが、とりあえず方法との1つとして共有させて頂きます。
何かご意見等ありましたら、ご連絡いただけると嬉しいです。

最後まで、ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsを勉強する Session�8

イベントハンドラ

イベントの購読(サブスクライブ)

v-onディレクティブを使うことで、DOMイベントの購読、イベント発火時のjavascriptの実行

<div id="example">
  <p>ボタンを押すとカウントが増えます</p>
  <p>カウント: {{ counter }}</p>
  <button @click="counter += 1">カウントアップ!</button>
</div>
let app = new Vue({
  el: '#example',
  data: {
    counter: 0
  }
})

ブラウザ上ではボタンを押すたびにカウントが増えていきます。

メソッドイベントハンドラ

イベントハンドラのロジックは複雑になっていくので、v-on属性の値に式を記述し続けるのはアンチパターンです。
そのためv-onは呼び出したいメソッド名を記述する事ができます。

<div id="example">
  <p>ボタンを押すとカウントが増えます</p>
  <p>カウント: {{ counter }}</p>
  <button @click="countUp">カウントアップ!</button>
</div>
let app = new Vue({
  el: '#example',
  data: {
    counter: 0
  },
  methods: {
    countUp:function(){
      this.counter += 1
    }
  }
})

インラインメソッドハンドラ

メソッド名を直接指定する代わりに、インラインメソッドを指定することもできます。

<div id="example">
  <p>{{ message }}</p>
  <button @click="say('こんちわ')">こんにちは</button>
  <button @click="say('こんばんわ')">こんばんわ</button>
</div>
let app = new Vue({
  el: '#example',
  data: {
    message: '挨拶'
  },
  methods: {
    say:function(message){
      this.message = message
    }
  }
})

インラインステートメントハンドラでオリジナルのDOMイベントを参照する方法があります。

特別な $event変数を使うことでメソッドにDOMイベントを渡すことができます。

<div id="example">
  <p>{{ message }}</p>
  <button @click="say('こんちわ', $event)">こんにちは</button>
</div>
let app = new Vue({
  el: '#example',
  data: {
    message: '挨拶'
  },
  methods: {
    say:function(message, event){
      console.log(event)
      this.message = message
    }
  }
})

console.log()でボタンが押された時にDOMイベントを出力しています。

気になる人は実装してみてください。

イベント修飾子

イベントハンドラ内でのevent.preventDefault( )、またはevent.stopPropagation( )の呼び出しは、様々な場面で必要になります。
DMOの込み入った処理をおこなうよりも、単純なデータロジックだけになっていた方が扱いすいです。
Vue.jsはこの問題を解決するためにv-onのためにイベント修飾子(event modifiers)がサポートされています。

.stop

通常、同じイベントをハンドルしたDOMがネストされている場合、親要素に向かってイベントが連鎖する。
下記の例ではdiv(親要素)の中にbutton(子要素)があり、両方とも同じイベントをハンドルしています。
.stopを使用することでhandler(子要素)は実行されるが、handler(親要素)は実行されない。

<div id="example">
  <p>.stopなし</p>
  <div @click="handler('親要素')">
    <button @click="handler('子要素')">
      イベント実行ボタン
    </button>
  </div>

  <p>.stopあり</p>
  <div @click="handler('親要素')">
    <button @click.stop="handler('子要素')">
      イベント実行ボタン
    </button>
  </div>
</div>
let app = new Vue({
  el: '#example',
  methods: {
    handler:function(element){
      alert(element + 'のイベント実行')
    }
  }
})

.prevent

Event.preventDefault()を呼ぶ。v
下記の例ではイベント(handler())は実行されるが画面遷移は行われない

<div id="example">
  <p>.preventなし</p>
  <a href="https://qiita.com/Sthudent_Camilo/items/58b8c21af0db5bea2340" @click="handler('Vue.js を勉強する Session1に遷移する')">
    Vue.js を勉強する Session1に遷移
  </a>

  <p>.preventあり</p>
  <a href="https://qiita.com/Sthudent_Camilo/items/58b8c21af0db5bea2340" @click.prevent="handler('Vue.js を勉強する Session1に遷移しない')">
    Vue.js を勉強する Session1に遷移しない
  </a>
</div>
let app = new Vue({
  el: '#example',
  methods: {
    handler:function(element){
      alert(element)
    }
  }
})

.capture

キャプチャーモードでDOMイベントをハンドルする。
ルート要素から順番にイベントが実行される。

<div id="example">
  <div @click.capture="handler('親要素')">
    <button @click="handler('子要素')">イベントを実行</button>
  </div>
</div>
let app = new Vue({
  el: '#example',
  methods: {
    handler:function(element){
      alert(element + 'が実行されました')
    }
  }
})

handler('親要素') -> handler('子要素')の順番で実行されます。

通常、イベントが発生すると、キャプチャーフェーズでルート要素から要素を探し、ターゲットフェーズで発生要素を検出、パブリングフェーズでルートまで要素をさかのぼりますが、キャプチャーモードにするとキャプチャーフェーズでイベントが発生します。

.self

実行した要素のみが処理を行われ、他の要素のイベントは実行されない。

<div id="example">
  <div @click.self="handler('親要素')">
    <button @click="handler('子要素1')">イベントを実行</button>
    <button @click="handler('子要素2')">イベントを実行</button>
  </div>
</div>
let app = new Vue({
  el: '#example',
  methods: {
    handler:function(element){
      alert(element + 'が実行されました')
    }
  }
})

.native

コンポーネントのルート要素上のネイティブイベントをハンドルする
※コンポーネントについて勉強不足なため今回はこんな感じだよ程度に記載

<!-- コンポーネントをクリックするとhandlerが呼び出される -->
<my-component @click.native="handler"></my-component>

<!-- コンポーネントをクリックしてもhandlerが呼び出されない -->
<my-component @click="handler"></my-component>

.once

1回だけイベントが実行される。

<div id="example">
  <button @click.once="handler('初回イベント実行')">イベントを実行</button>
</div>
let app = new Vue({
  el: '#example',
  methods: {
    handler:function(element){
      alert(element + 'が実行されました')
    }
  }
})

.passive

event.preventDefault()を呼び出さない事を明示する。
(.preventとの併用不可)

修飾子は繋げることができる

<div id="example">
  <div @click="handler('親要素')">
    <button @click.stop.once="handler('子要素')">
      イベント実行ボタン
    </button>
  </div>  
</div>
let app = new Vue({
  el: '#example',
  methods: {
    handler:function(element){
      alert(element + 'が実行されました')
    }
  }
})

イベント修飾子は繋げて書くことができ、上記の例では子要素だけ実行されるイベントが1回だけ行われ、そのあとは親要素のイベントだけ呼ばれるようになります。

キー修飾子

キーボードイベントを検出するにあたって、特定のキーチェックが必要になった時にVue.jsではv-onに対してキー修飾子を追加することができます

  • .entser
  • .tab
  • .delete (windowsだとbackspace)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

上記のキー修飾子から .enter を利用して例を作ります。
下記の例ではbuttonは左クリックでは実行されず、Enterキーで実行されます。

<div id="example">
  <button @keyup.enter="handler('イベント実行')">
    Enterで実行ボタン
  </button>
</div>
let app = new Vue({
  el: '#example',
  methods: {
    handler:function(element){
      alert(element + 'が実行されました')
    }
  }
})

KeyboardEvent.keyで公開されているキャメルケースのキー名は、ケバブケースに変換することで修飾子として直接使用できます。

キーコード

【注意】
keyCodeイベントの使用は非推奨で、新しいブラウザではサポートされない可能性があります。
Vue.jsで使用することはできますが、今回は説明しません。

システム修飾子

下記の修飾子を使用すると、対応するキーが押された時にのみキーボードのイベントリスナを実行できます。

  • .ctrl
  • .alt
  • .shift
  • .meta

【注意】
Macintosh キーボードの場合、meta はコマンドキー(⌘)です
Windows のキーボードでは、meta はウィンドウキー(⊞)です
Sun Microsystems のキーボードでは、メタは実線のダイヤモンド(◆)とマークされています。
特定のキーボードでは、特に MIT や Lisp マシンのキーボードと Knight キーボード、space-cadet キーボード、メタのような後継機には “META” と表示されます。 Symbolics のキーボードでは、 “META” または “Meta” というラベルが付いています。

<div id="example">
  <button @keyup.enter.ctrl="handler('イベント実行')">
    Enterで実行ボタン
  </button>
</div>
let app = new Vue({
  el: '#example',
  methods: {
    handler:function(element){
      alert(element + 'が実行されました')
    }
  }
})

【注意】
修飾子キーは通常のキーとは異なり、keyup イベントで使用する場合、イベント発生時に押されていなければならない。
言い換えると、keyup.ctrl は ctrl を押しながら何かのキーを離したときにのみ、実行されます。
ctrl キーだけを離しても、トリガされません。
トリガされることを望むなら、ctrl ではなく keyup.17 のように keyCode を使用します。

.exact 修飾子

イベントを実行するために必要なシステム修飾子の正確な組み合わせを制御する。

<div id="example">
  <!-- これは Ctrl に加えて Alt や Shift キーが押されていても発行されます -->
  <button @click.ctrl="handler('イベント実行')">@click.ctrl</button>

  <!-- これは Ctrl キーが押され、他のキーが押されてないときだけ発行されます -->
  <button @click.ctrl.exact="handler('イベント実行')">@click.ctrl.exact</button>

  <!-- これは システム修飾子が押されてないときだけ発行されます -->
  <button @click.exact="handler('イベント実行')">@click.exact</button>
</div>
let app = new Vue({
  el: '#example',
  methods: {
    handler:function(element){
      alert(element + 'が実行されました')
    }
  }
})

マウスボタンの修飾子

イベント実行のハンドリングを特定のマウスのボタンのみに制限します。
使用方法は上記の修飾子と同様です。

なぜHTMLにイベントリスナを記述するのか

v-onを利用することでいくつかの利点があるからです。

  • HTML テンプレートを眺めることで、JS コード内のハンドラ関数を探すことを容易にします

  • JS内のイベントリスナーを手作業で加える必要がないので、ViewModelをDOM依存のない純粋な処理にできます。これはテスタビリティも向上します。

  • ViewModel が消去されるときに、すべてのイベントリスナーは自動で削除されます。手動でそれらの消去をおこなうことを気にする必要がない。

あとがき

今回の記事ではhtmlとjsのコードにタグを付けずに掲載することにしました。
Qiita独特の機能らしくmdをエディタでプレビューするときに上手く反映されず、変更を加えるのが手間だったためです。

参考資料

[メモ]Vue.jsイベント修飾子一覧

Vue.js公式リファレンス

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

脱jQueryしてみて、憧れのVue.jsでLPコーディングしたとき必要だった知識たち

こんちは、森高千里の「17才」を聴きながらサーモン春巻き食べてます。
Perfumeがカバーしたら素敵になると思ってます。

先日、

LPを禁jQueryで、ピュアJsでコーディングしてみました。

(ちなみに私はjQuerもLoveです。)
トキメキ不足だったのでとても楽しかったです!皆さん試したいのではないでしょか!

その時のTips私的メモです。
欲しい情報が、公式やいろんなブログに散らばってたので改良してまとめました。

(ps.世間を知るたび無力な自分の記事投稿って怖いですよね)

ひとまずVue.jsのハローワールドを初体験しました。

vue.html
<div id="app">
  {{ message }}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>

<script>
//el:にはVue.jsであれこれしたい部分のIDを
new Vue({
  el: '#app',
  data: {
    message: '憧れのVue,js なんかオシャレ'
  }
})
</script>

これでHTMLの{{ message }}にjs側のmessageのテキストが表示されました!

お次は、クリック検知。死ぬほど使うonclickです。

click.js
$("#target").on("click",function(){
    $(this).text("へロー");
});

jQuery だとこうですよね。

click.html
<div id="app">
  <button v-on:click="changeBg">背景変更</button>
</div>

<script>
// v-on:click="ここ" で関数名を登録するようです。
// methods:内に内容を記述

new Vue({
  el: '#app',
  data: {
    message: 'ハロー Vue.js!'
  },
  methods: {
    changeBg: function () {
       document.body.style.backgroundColor = 'red';
    }
  }
})
</script>

el: '#app'の中に入ってなきゃダメみたいです。
v-on:click="ここ" で関数名を登録するようです。
methods:の中にどんどん関数を登録してくわけですね! おっけー!

toggleClassの検知 これがきっと味噌や!

onclickしたらtoggleClassをvue.jsでやります!!

click.js
$("#target").on("click",function(){
    $(".target").toggleClass("active");
});

jQuery だとこうですよね。

sample:vue.html

<div id="app">
  <p class="target" v-bind:class='{active:isActive}'>表示されるエリア</p>
  <button v-on:click='isActive=!isActive'>クラスtoggleボタン</button>
</div>

<style>
.target{
    transition:3s all;
    opacity:0;
}
.active{
    opacity:1.0;
}
</style>

<script>
  data: {
    message: '憧れのVue,js なんかオシャレ',
    isActive: false,
  }
</script>

簡単なので落ち着いて解説読んでください

v-on:click='isActive=!isActive'の指定で、
クリックするたびに
isActiveがtrueかfalseに切り替わり(toggle)
.activeがついたり消えたりする仕組みです!
vueの方はisActive: false,のフラグを書いてあげるだけです

スクロールイベント,リサイズイベントの検知 これもめっちゃ使いますよね!

click.js
// スクロール
$(window).on("scroll",function() {
    console.log($(this).scrollTop());
});

// リサイズ
var w = 0;
$(window).on('load resize',function(){
    w = $(window).width();
    if(w < 768){
        // スマホごにょごにょ
    }
});

jQuery だとこうですよね。

そして肝心のvueバージョン!
これはもうシンプルに、これでいいのかな。。

vue.html
<div id="app">
  <p style="height:4000px;"></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>

<script>
//el:にはVue.jsであれこれしたい部分のIDを
new Vue({
  el: '#app',
  data: {
    scrollY: 0,
    windowWidth: 0,
    loadWidth:0,
  },
  //mounted DOMにアクセスできるようになった状況
  mounted: function(){
    window.addEventListener('scroll', this.handleScroll);
    window.addEventListener('resize', this.handleResize);
    window.addEventListener('load', this.handleLoadSize);
  },
  methods: {
    handleResize: function(){
        this.windowWidth = window.innerWidth;
        console.log(this.windowWidth);
    },
    handleScroll: function() {
        this.scrollY = window.scrollY;
        console.log(this.scrollY);
    },
    handleLoadSize: function(){
        this.loadWidth = window.innerWidth;
        console.log(this.loadWidth);
    }
  }
})
</script>

感想は、超絶楽しかったけど、なんかもう途中で何やってんだ間でてきて、普通のシンプルなLPはjQueryとCSSで綺麗に書いて作ればいいと思いました。笑
使い方が甘いのかな!

毎回データ取ってきて値が変わったりするインタラクティブなやつは向いてるんだなと思いましたが、どうなんでしょかね。フレームワークだしな、、。泣
あんまフレームワークやらんから、、泣

しかし記事にしてアウトプットすると整理されて理解が深まります。

以上、ご静聴有難うごいました。
お前こっちのが楽だぞとか、ここ違うぞとかあれば教えてください。
(プラグインとか調べて、使いこなせばもっと楽なんだろなとこっそり気づいてます笑)

最後になりましたが、僕はjQuery好きです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue + AWS Amplifyでシンプルな画像共有アプリを作成する (作成〜デプロイ) #2

 
前 → Vue + AWS Amplifyでシンプルな画像共有アプリを作成する (概要) #1

〜この記事でやること〜

  • VueProjectの作成
  • Amplifyの導入
    • Auth ( Cognito )
    • Storage ( S3 )
  • 作成したprojectをAmplifyConsoleからBuild & Deploy

VueProjectの作成

まずはProjectを作成します。

■ 前提条件

  • Node.js
  • npm

がインストールされていること

■ 参考
Node.js / npm をインストール (Mac環境)
Node.js / npmをインストールする(for Windows)
とりあえず Ubuntu で新しい Node.js, npm をインストール

VueCLIのinstall

$ npm install -g @vue/cli

VueProjectの作成

今回は全部盛りにしていますが各自好きなように選択してください。

※この記事はclass-styleではなくobject-styleで書きます

$ vue create <your-project-name>

Vue CLI v3.9.2
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, TS, PWA, Router, Vuex, CSS Pr
e-processors, Linter, Unit, E2E
? Use class-style component syntax? No
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfill
s, transpiling JSX)? Yes
? Use history mode for router? (Requires proper server setup for index fallback in
 production) Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by
 default): Sass/SCSS (with node-sass)
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save, Lint and fix on commit
? Pick a unit testing solution: Jest
? Pick a E2E testing solution: Cypress
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In package.
json
? Save this as a preset for future projects? No

起動してみる

$ yarn serve

スクリーンショット 2019-07-15 18.37.32のコピー.png

このように表示されればOK

GitHubへpush

Remoteリポジトリの追加

$ git remote add origin <your-remote-repository>

Push

$ git add .
$ git commit -m "first commit"
$ git push origin master

Amplifyの導入

Amplifyを導入するにあたってdevelopブランチを作成しておきます。

developブランチの作成

$ git checkout -b develop

Amplify-CLIのinstall

$ npm install -g @aws-amplify/cli
$ amplify -v

1.8.1

次に、AWSアカウントを紐付けます。以下のコマンドを打つとAmplifyで使用するユーザーを新規作成することが出来ます。
詳しくはAWS Amplify クイックスタートを参考にしてください。

$ amplify configure

もし既にAWSのユーザーがあり、そちらを使う場合はこの作業は必要ありません。

AWS Backendの設定

Amplify-CLIのプロジェクトの設定を選択することが出来ます。
amplifyの環境名はdevにしています。

$ amplify init

? Enter a name for the project <project name>
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building typescript
Please tell us about your project
? What javascript framework are you using vue
? Source Directory Path:  src
? Distribution Directory Path: dist
? Build Command:  yarn build
? Start Command: yarn serve

設定が終わるとしばらく動いた後にS3にbucketが作成されます。
スクリーンショット 2019-07-16 12.01.37.png

これはDeploy用のCloudFormationTemplateを配置するbucketになります。

Amazon Cognitoの追加

ユーザー管理・認証をしたいのでAmazonCognitoを使えるようにします。
commandで簡単に作成出来ます。

$ amplify auth add

// Manualを選択
Do you want to use the default authentication and security configuration? Manual

configuration

// IAM controlをするかどうか
 Select the authentication/authorization services that you want to use: 
User Sign-Up, Sign-In, connected with AWS IAM controls (Enables per-user Storage features fo
r images or other content, Analytics, and more)

 Please provide a friendly name for your resource that will be used to label this
category in the project: <label-name>
 Please enter a name for your identity pool. <identity-pool-name>
 Allow unauthenticated logins? (Provides scoped down permissions that you can cont
rol via AWS IAM)
No
// GoogleアカウントやTwitterアカウントでのサインアップ(今回はなし)
 Do you want to enable 3rd party authentication providers in your identity pool?
No


 Please provide a name for your user pool: <user-pool-name>

// サインイン時に使える属性(今回はユーザー名)
 Warning: you will not be able to edit these selections.
 How do you want users to be able to sign in? Username

// 多要素認証
 Multifactor authentication (MFA) user login options: OFF

// Emailを使って登録変更する
 Email based user registration/forgot password: Enabled (Requires per-user email e
ntry at registration)

// アカウント認証コード送付用の文面
 Please specify an email verification subject: Your verification code
 Please specify an email verification message: Your verification code is {####}

// パスワードポリシー(デフォルト使用)
 Do you want to override the default password policy for this User Pool? No

// 登録時に必要な属性の選択(今回はEmailのみ)
 Warning: you will not be able to edit these selections.
 What attributes are required for signing up? (Press <space> to select, <a> to tog
gle all, <i> to invert selection)Email

 Specify the app's refresh token expiration period (in days): 30
 Do you want to specify the user attributes this app can read and write? No

// OAuth(今回はなし)
 Do you want to enable any of the following capabilities? (Press <space> to select
, <a> to toggle all, <i> to invert selection)
 Do you want to use an OAuth flow? No

// Lambdaトリガー(今回はなし)
? Do you want to configure Lambda Triggers for Cognito? No


Successfully added resource <project-name> locally

これでOKです。

S3 Storageの追加

今回は画像共有アプリなので画像を保存するbucketを用意します。

$ amplify storage add

// Imageが含まれている方を選択
? Please select from one of the below mentioned services Content (Images, audio, v
ideo, etc.)

? Please provide a friendly name for your resource that will be used to label this
 category in the project: <label-name>
? Please provide bucket name: <bucket-name>

// 認証済みユーザーのみがアクセスできる
? Who should have access: Auth users only

// どこまでの権限を与えるか
? What kind of access do you want for Authenticated users? create/update, read, de
lete

// Lambdaトリガー(今回はなし)
? Do you want to add a Lambda Trigger for your S3 Bucket? No
Successfully added resource <label-name> locally

リソースの作成

amplifyの状態を確認します。

$ amplify status

Current Environment: dev

| Category | Resource name | Operation | Provider plugin   |
| -------- | ------------- | --------- | ----------------- |
| Auth     | resourcename  | Create    | awscloudformation |
| Storage  | resourcename  | Create    | awscloudformation |

AuthとStorageがCreateの状態になっていますが、あくまでLocalの設定が追加されただけでリソースは作成されません。
実際にリソースを作成するには

$ amplify push

を実行します。色々走った後に、

✔️ All resources are updated in the cloud

となればOKです。
Cloud上にS3 bucketとCognitoのUserpoolが作成されているのが確認できます。

変更をGitHubにpush

$ git add .
$ git commit -m "Add amplify ~"
$ git push origin develop

Amplify Consoleを使ってDeploy

いよいよdeployです。
まずはAmazon Amplify Consoleを開きましょう。

アプリの作成をクリックするとGitプロバイダーを選択する項目があるのでGitHubを選択してcontinueをクリック

スクリーンショット 2019-07-15 18.12.59.png

対象のリポジトリとブランチを選択して次へをクリック

スクリーンショット 2019-07-15 18.12.24.png

バックエンドの設定の画面が表示されます。
Amplifyの環境名を選択し、AmplifyConsole用のロールを選択します。無い場合はCreate new roleを選択

スクリーンショット 2019-07-15 18.14.48.png

「アプリの作成」を押すと早速ビルドが始まります。

スクリーンショット 2019-07-15 18.38.37.png

ビルドが終わり、プロビジョンから検証までオールグリーンであればDeploy成功です!
ドメインのところに書かれているURLを開いて以下のように表示されればOK!

スクリーンショット 2019-07-15 18.37.32.png

Basic認証を追加

このままだと誰でもアクセス出来てしまうので簡単な認証を追加することでそれを防ぎます。

Amplify Console左メニュの「アクセスコントロール」をクリック
スクリーンショット 2019-07-16 12.58.50.png

「アクセスの管理」をクリック
Apply a global password を ON にして usernamepassword を設定してSaveをクリック
スクリーンショット 2019-07-16 12.59.33.png

キャッシュをクリアしてからURLを開いて以下のように表示されればOKです!

スクリーンショット 2019-07-16 13.00.49.png

終わりに

VueProjectの作成からリソースの生成、ビルド&デプロイまで行いました。
次回はUIデザインを詰めていこうと思います。
ありがとうございました!

前 → Vue + AWS Amplifyでシンプルな画像共有アプリを作成する (概要) #1
次 → Vue + AWS Amplifyでシンプルな画像共有アプリを作成する (UIデザイン) #3

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsで簡単デバッグ

最近、Vue.jsを触る機会が多くなりました。
APIから受け取ったデータをなんやかんやと確認したいときに「created」に「console.log」を記述してデバッグしています。
dataに登録したデータは「this」で参照できるのですが、急いでいると忘れて怒られたり...σ(^_^;)アセアセ...)

sample2.PNG

<script>
export default {
  name: 'App',
  data: function () {
    return {
      test: 'hogehoge'
    }
  },
  created: function () {
    console.log(this.test)
  }
}
</script>

「this」をつけてと・・・出ました。

sample1.PNG

公式ガイドによると、「created」フック(処理を割り込ませる仕組み)はインスタンスが生成された後にコードを実行したいときに使われます。
Vue.jsのライフサイクルフックの1つ。
下の図を見ると結構早い段階で呼ばれることがわかります。

今回は備忘録として簡単デバッグの方法を記載させていただきました。
また機会があればVue.jsに関して記載したいと思います。

lifecycle.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsと<input type=range>でSoundCloudみたいなシークバーを作る

挨拶

こんにちはkaijiです。
最初に、初投稿かつ今までWebをほとんどやったことのない人なので、色々と間違っていたり不足している部分があるかと思います。もしそういった点があった時はコメントで優しく教えてもらえると嬉しいです。

経緯・注意

個人で開発しているサイトでオーディオを再生するためシークバーを実装しようとして色々調べてたのですが、HTML5のAudioタグで作っていたり、inputの標準のUIで作っているものだったり、オリジナルの見た目で作っていてもプログレスバーまで作っているものはほとんど見当たらなかったので、Vue.js上でJavascriptのWeb Audio APIとinputタグを使って自分で作ってみました。
ここでは、シークバーを作ることをメインにしているので、再生、停止、ボリューム調整等の基本的な動作に関しては他のサイトとかを参考にしてください。

全体的な流れ

  1. シークバーの見た目を作る
  2. 実際にシークさせてみる
  3. 現在の再生時間と、全体の再生時間を表示させる(分:秒表示)
  4. 再生済み用のプログレスバーを作り、元のinputタグに重ねる

といった感じです。

シークバーの見た目を作る

シークバーの見た目に関しては、基本好きなように作ってねっていう感じなんですが、いくつか注意点があるので、それについて書きます。

まずinputタグにはデフォルトで見た目が設定されています。(Mac版Chromeの場合はこんな感じ↓)
スクリーンショット 2019-07-15 23.06.19.png
ですが、これだとブラウザごとに見た目が変わってしまったり、サイトによってはデザイン的に浮いてしまうことがあるため(ちなみにこれはHTML5のオーディオタグにも言える)今回は自分でCSSを書いて見た目を作っていくのですが、その前にこのデフォルトの見た目を表示させないように<style>内に以下のCSSを書いてみましょう。

input[type=range] {
    -webkit-appearance: none;
}

input[type=range]::-webkit-slider-thumb {

}

input[type=range]::-ms-tooltip {
    display:none;

}

input[type=range]::-moz-range-track {

}

input[type=range]::-moz-range-thumb {

}

ここではinput[type=range]の見た目を表示させないでくださいと各ブラウザ用に書いています。
こうすることで、真っさらなinput[type=range]ができたので、ここからは実際に見た目を作っていきます。
なぜ-msだけthumbの設定がないのかや、-mozには何も書いていないのか等は以下のページにわかりやすく書かれていたので、そちらを参考にしてみてください。

input type=range タグをカスタマイズするために

実際にシークさせてみる

次は実際にシークをさせてみましょう。
ここではWeb Audio API上でオーディオファイルが再生できる状態になっていることを前提に話をするので、まだ実装できてない人は他の投稿やリファレンス等をみて実装してからもう一度来てください。

今回はVue.jsを使っているので双方向データバインディングのできるv-modelを現在の再生時間、v-bindを全体の再生時間を表示させるために使っていきます。

まず<script>内でオーディオのcurrentTime(現在の再生時間)とdurationTime(全体の再生時間)を取得します。

const audio = new Audio

export default {
    data() {
        return {
            currentTime: 0,
            durationTime: 0
        }
    },

    mehods:{
        play() {
            audio.src = //オーディオのURLとか

            audio.addEventListener("loadedmetadata", function () {
                return {
                    durationTime: audio.duration.toFixed(0)
                }
            });
            audio.addEventListener("timeupdate", function () {
                return {
                    currentTime: audio.currentTime.toFixed(0)
                }
            });
            audio.addEventListener("ended", function () {
                return {
                    currentTime: 0,
                    durationTime: 0
                }
            });
            audio.play();
        },
        seek() {
            audio.currentTime = this.currentTime;
        }
    }
} 

ここでやっていることはaudio.srcで取得したオーディオデータからaddEventListenerを使ってメタデータを抽出できたタイミングでaudio.durationTimeを取得し、その値を返させています。
それと同じように再生中にaudio.currrentTimeを取得しその値を返させています。
また再生が終了したら両方の値を0(初期値)にしています。

そして下に書かれているseek()というメソッドではv-modelの特性である双方向データバインディングを使ってthumbを移動させるたびにその位置(時間)まで実際に再生されている音源のaudio.currentTimeを移動させています。

またaudio.durationTimeaudio.currrentTimeを取得するときtoFixed(0)と書いていますが、これは取得してきた値を整数に変換しています。
なぜこんなことをするのかというと、標準で取得してくる再生時間はミリ秒(1/1000秒)で表されているため、今回作るようなプレイヤーの場合あまり適している形とは言えません。
そのため今回は整数で表すようにしていますが、もし作るプレイヤーがミリ秒まで表示できるものであって欲しいならtoFixed(n)を書く必要はありませんし、toFixed(n)はnの値を変えることによって0.1秒単位(その場合nは1になる)などもっと細かい値にすることも可能なので、自分の用途に合わせて調整してみてください。
詳細は下記のURLから見てください

Number.prototype.toFixed()

では次に<template>内に記述していきます。

<a v-on:click=play>再生</a>
<input type="range" v-model="currentTime" v-on:input="seek" v-bind:max="durationTime"/>

最初のaタグやinputのtype="range"は単に処理を呼び出したり、inputのタイプを指定しているだけなので気にしないでください。

まずv-modelを使ってcurrentTimeを取得しています。なんども言っていますが、v-modelは双方向データバインディングが可能なため、値が変化する度にリロード等の処理をせず、直接表示される値を変化させることが可能です。
そしてv-on:input="seek"はシークバーに触れる(thumbが動く)度に先ほど記述したseek()メソッドが呼び出されます。
最後にv-bind:max="durationTime"はシークの最大値をdurationTimeにしています。そのほか最小値やステップは記述していないためデフォルトの値が使われますので、これも必要に応じて設定してみてください。

現在の再生時間と、全体の再生時間を表示させる(分:秒表示)

v-modelを使ったcurrentTime(現在の再生時間)、v-bindを使ったdurationTime(全体の再生時間)を反映させる処理を見てきた皆さんであれば、おそらくinputに値を反映させたように、文字にも同じように反映させればいいとすぐにわかったと思いますが、今回は少し発展して分:秒(mm:ss)で時間を表示していきたいと思います。

const audio = new Audio

export default {
    data() {
        return {
            currentTime: 0,
            durationTime: 0,
            convertedDurationMin: "00",
            convertedDurationSec: "00",
            convertedCurrentMin: "00",
            convertedCurrentSec: "00"
        }
    },

    mehods:{
        play() {
            audio.src = //オーディオのURLとか

            audio.addEventListener("loadedmetadata", function () {
                const durationMin = Math.floor(audio.duration.toFixed(0) / 60);
                const durationSec = audio.duration.toFixed(0) % 60;

                return {
                    durationTime: audio.duration.toFixed(0),
                    convertedDurationMin: ("00" + durationMin).slice(-2),
                    convertedDurationSec: ("00" + durationSec).slice(-2)
                }
            });
            audio.addEventListener("timeupdate", function () {
                const currentMin = Math.floor(audio.currentTime.toFixed(0) / 60);
                const currentSec = audio.currentTime.toFixed(0) % 60;

                if ((currentMin > this.convertedCurrentMin 
                    && audio.currentTime !== 0)
                    || (currentMin < this.convertedCurrentMin)) {
                    return {
                        currentTime: audio.currentTime.toFixed(0),
                        convertedCurrentMin: ("00" + currentMin).slice(-2),
                        convertedCurrentSec: ("00" + currentSec).slice(-2)
                    }

                } else {
                    return {
                        convertedCurrentSec: ("00" + currentSec).slice(-2)
                    }
                }
            });
            audio.addEventListener("ended", function () {
                return {
                    currentTime: 0,
                    durationTime: 0,
                    convertedDurationMin: "00",
                    convertedDurationSec: "00",
                    convertedCurrentMin: "00",
                    convertedCurrentSec: "00"
                }
            });
            audio.play();
        }
    }
} 

オーディオから取得したdurationTimeを整数に変換し、その値を60で割った数をdurationMin、60で割った数の余りをdurationSecとします。
covertedDurationMinへは"00"にdurationMinを加算した要素から最後の2つを取り出して返し、
covertedDurationSecへは"00"にdurationSecを加算した要素から最後の2つを取り出して返しています。

わかっている人もいると思いますが、"00は"文字列のため、それにdurationMinを加算すると9分以下なら3桁(Ex.009)、10分以上なら4桁(Ex.0010)となってしまいmm表記にはなりませんが、最後の二つの要素のみを取り出せば、9分以下の時は10の位が0になり、10分以上の時はdurationMinと同じ値になるので、mm表記にすることができます。
covertedDurationSecにも同じことが言えるので、これでdurationTimeをmm:ss表記にすることができました。

currentTimeにも同じことが言えますが、currentTimeは値がどんどん変わっていくため、それも考慮してコードを書くと上記のようなコードになります。

currentMinconvertedCurrentMinより大きく、かつaudio.currentTimeが0でない場合というのはcurrentMinが加算されるタイミング(currentSecが0になるタイミングとも言える)の時呼び出されるものです。
thumbを移動させないシークであればこれで問題ありませんが、今回はthumbを移動するため、それに加えてcurrentMinconvertedCurrentMinより大きいときという条件を加えました。こうすることにより1分以上戻った時でも正常に表示できるようになりました。

次に<template>内に記述していきます。

<span v-text="convertedCurrentMin + ':' + convertedCurrentSec"></span>
<span v-text="convertedDurationMin + ':' + convertedDurationSec"></span>

正直そのまますぎて説明することがないので注釈を一つ。ここではspanタグを使っていますが、もちろん別のタグを使っても構いません。
例えばSoundCloudではdurationTimeはaタグになっていて、一度クリックすると残り時間の表示になり、もう一度クリックすると従来通り全体の再生時間を表示することができます。

再生済み用のプログレスバーを作り、元のinputタグに重ねる

まず↓のようなinputと同じサイズのプログレスバーを作ります。
<span class="progress" id="progress"></span>
もちろんタグはdiv等でも構いません。

次にstyleを書いていきます。

span.progress {
    width: /*width*/
    background: linear-gradient(/*YourFavoriteColor*/, /*YourFavoriteColor*/) no-repeat;
    background-size: 0;
    position: absolute;
    pointer-events: none;
}

まずプログレスバーにposition: absoluteを加える等をして、inputの上に重ねます。
background: linear-gradient(/*YourFavoriteColor*/, /*YourFavoriteColor*/) no-repeat;でプログレスバーの色をグラデーションで指定し、backgroud-sizeを使ってプログレスの度合いを表しています。
また最後のpointer-events: none;はプログレスバーの操作を無効化して、その下に配置されているシークバーの操作をできるようにしています。これを指定することにより、プログレスバーが真上に重なっても操作ができるようになりました。

ちなみにプログレスを表示する項目が1つであれば、spanを足さずに直接inputに書くこともできます。
しかしこういったWebサービスではストリーミング再生、つまり再生済みだけでなく読み込み済みのプログレスも作る必要があることが多いため、今回は別のタグにして複数のプログレスを重ねられるようにしました。

次に<script>のtimeUpdate内に記述していきます。

const percent = audio.currentTime.toFixed(0) / audio.duration.toFixed(0) * 100;
document.getElementById("progress").style.backgroundSize = percent + "%";

currentTimeをdurationTimeで割った数に100を掛けた数を定数percentとします。
定数percentの範囲は0 <= percent <= 100なので、background-sizeの値をwidthに対しての百分率とすることができます。そして、span.progressbackground-sizeに定数parcentに"%"を足した値を反映させます。そうするとthumbに従ってプログレスバーが動いているように見えるかと思います。

最後に

長々と書いてきましたが、これで終わりです。初めてな上に眠気と戦いながら書いたので、ミスや分かりづらいところがあったかもしれないですが、少しでも読んでくださった方の参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ASAP Vue SPA入門:コンポーネント

Vue CLI3 で作成した SPA(Single Page Application)プロジェクト上で、段階的に Vue.js を学んで行きましょう。

目次はこちら

今回はコンポーネント編です。
TodoList を作りながらコンポーネントの使い方を学びます。

前提事項

ASAP Vue SPA入門:Vue の基本 が完了していること。

ページの追加

新規に Todo List ページを作成します。

  • TodoList.vue を作成
  • router.js を修正
  • App.vue にナビゲーションを追加

やり方を忘れてしまった人は「ページの追加」を振り返ってください。

src/views/TodoList.vue
<template>
  <div class="todolist">
    <h1>Todo</h1>
    <ul v-if="todos.length">
      <li
        v-for="todo in todos"
        :key="todo.id"
      >{{ todo.text }}</li>
    </ul>
    <p v-else>
      TODO 一覧はありません
    </p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      todos: [
        { id: 1, text: "Learn Vue" },
        { id: 2, text: "Learn about single file components" },
        { id: 3, text: "Learn about conponents" }
      ]
    };
  }
};
</script>

<style>
h1 {
  text-align: center;
}

.todolist {
  max-width: 400px;
  margin: 0 auto;
  text-align: left;
}
</style>

コンポーネントの作成とプロパティによる値渡し

Todo List から Todo List Item を別コンポーネントに切り出します。

TodoListItem.vue コンポーネントを作成します。

src/components/TodoListItem.vue
<template>
  <!-- todo プロパティのテキストを表示 -->
  <li>{{ todo.text }}</li>
</template>

<script>
export default {
  // 親コンポーネントから todo プロパティを受け取る
  props: ["todo"]
};
</script>

親コンポーネントから子コンポーネントに値を渡すにはプロパティを使用します。
上記では todo プロパティを受け取れるよう宣言しています。

TodoList.vue を下記の様に修正します。

TodoList.vue
<template>
  <div class="todolist">
    <h1>Todo</h1>
    <ul v-if="todos.length">
      <!-- TodoListItem コンポーネントを使います -->
      <!-- v-bind を使って動的に todo プロパティを渡します -->
      <TodoListItem
        v-for="todo in todos"
        :key="todo.id"
        :todo="todo"
      />
    </ul>
    <p v-else>
      TODO 一覧はありません
    </p>
  </div>
</template>

<script>
// TodoListItem コンポーネントを import します
import TodoListItem from "../components/TodoListItem";

export default {
  // 使用するコンポーネントを Vue に伝えます
  components: {
    TodoListItem
  },
  data() {
    // ...
  }
};
</script>

<style>
/* ... */
</style>

JavaScript のところで先程作成したTodoListItem.vueをインポートして、Vue オブジェクトのcomponents属性に設定しています。こうすることで、HTML のところで、TodoListItemタグが使用できるようになります。

HTML のところではliタグの代わりにTodoListItemタグを使用するように変更しました。子コンポーネントに値を渡すためにtodoプロパティにtodoオブジェクトをバインドしています。

プロパティのバリデーション

プロパティは以下のように記述することでバリデーションチェックをすることができます。

export default {
  props: {
    // 基本的な型の検査 (`null` と `undefined` は全てのバリデーションにパスします)
    propA: Number,
    // 複数の型の許容
    propB: [String, Number],
    // 文字列型を必須で要求する
    propC: {
      type: String,
      required: true
    },
    // デフォルト値つきの数値型
    propD: {
      type: Number,
      default: 100
    },
    // デフォルト値つきのオブジェクト型
    propE: {
      type: Object,
      // オブジェクトもしくは配列のデフォルト値は
      // 必ずそれを生み出すための関数を返す必要があります。
      default: function () {
        return { message: 'hello' }
      }
    },
    // カスタマイズしたバリデーション関数
    propF: {
      validator: function (value) {
        // プロパティの値は、必ずいずれかの文字列でなければならない
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
}

TodoListItem.vue もバリデーションチェックをするよう修正します。

TodoListItem.vue
props: {
  todo: {
    type: Object,   // オブジェクト型であること
    required: true  // 必須項目
  }
}

イベント

親コンポーネントから子コンポーネントへはプロパティを使って値を渡すことができますが、子コンポーネントから親コンポーネントへ値を渡すことは出来ません。その代わりにイベントを使って通信することができます。

イベントの発火

TodoListItem.vue に削除ボタンを設け、removeイベントを発火するよう修正します。

TodoListItem.vue
<li>
  {{ todo.text }}
  <button @click="$emit('remove', todo.id)">x</button>
</li>

botton がクリックされると $emit('イベント名', ペイロード) でイベントを発火します。

イベントハンドリング

TodoList.vueremove イベントをハンドリングして Todo を削除します。

TodoList.vue
<!-- remove イベントをハンドリングして removeTodo メソッドをコールします -->
<TodoListItem
  v-for="todo in todos"
  :key="todo.id"
  :todo="todo"
  @remove="removeTodo"
/>
TodoList.vue
methods: {
  removeTodo(idToRemove) {
    this.todos = this.todos.filter(todo => {
      return todo.id !== idToRemove;
    });
  }
}

removeTodメソッドにはremoveイベントが発火された時のペイロードが引数として渡されます(この場合は todo の id です)。

演習

TodoInputText.vue コンポーネントを作成して、TodoList.vue でそれを使って TODO を追加できるようにしてください。

次回

Coming Soon...

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む