※スマホ対応はしてません。

バンクーバーの電車遅延情報をSlackで流すよ。(Frog Advent Calendar 2016 – 16日目)

カテゴリー: JavaScript

Frog Advent Calendar 2016 – 16日目

カナダの西海岸に位置する、海も山も和食もある街バンクーバーのFrog Houseからお届けします。

技術の話ですが、せっかくなのでバンクーバーの紹介もさせて頂こうかと思います。

概要

  • シェアハウス内はSlackで連絡を取っている
  • バンクーバーの電車遅延はウェブページで公開されている
  • ページをスクレイピング、加工して情報取得
  • Slack botで定期的に情報を取得、問題があれば通知する

こんな感じ。チュートリアルではないのであんまり細かい手順やコードは載せてません、各種公式ドキュメントや実装コードをご確認願います。

遅延と回復を検知して通知。

フロッグハウス

Frog Houseというシェアハウスがバンクーバーにございまして、私ギンペイは現在そこに在住しております。住人は七名、うち私を含む五名がウェブ制作方面の人間です。

Slack

かつてはFacebookメッセージで家の中の連絡をしていたんですが、なかなか煩雑なのと検索性能が低いことから、Slackを導入しました。今のところうまく回っているようです。Slackはいいぞ。

最近二軒目Frog House 2ndも統合して、別チャンネル同チームで運用しております。なんか家賃とかそういうアレの管理をまとめてしたかったんですって。

現在チームには20のチャンネルが作成されていまして、全体用 #general と一軒目用 #general-1 みたいに分けて運用してます。まあ各自で適当に作ったりしてもらえれば良いかと。

先に申し上げた通りウェブ系の人が多いので、 #webdev チャンネルを設けて情報共有したりもしてます。たのしい。

とまあそんな感じで、日常生活にSlackを組み込んだ感じですね。

バンクーバーの交通事情

割と北米は車文化が根強い感じはあるんですが、バンクーバーでは “SkyTrain” という呼称で電車が走ってます。バンクーバーのダウンタウン(中心街)を軸に三本の路線が稼働中です。

ちなみに電車、無人運転です

東京のゆりかもめ線みたいな感じ。すげーぞカナダ人。

車両の先頭はこんな景色。あ、わかりづらいこれ……

あと改札も無人。前は性善説もとい諸事情により扉もなく開放されていたんですが、最近はSuicaみたいな “Compass” というカードが導入されまして、通るたびにピッピッとやります。オートチャージはなくて、駅に設置の機械でチャージしてやります。

(さらに…)

短縮記法でちゃちゃっと書こう。(Vue.js始めるおれおれアドベントカレンダー2016 – 15日目)

カテゴリー: JavaScript

Vue.js始めるおれおれアドベントカレンダー2016 – 15日目

テンプレートの短縮記法

よく使う v-bind と v-on は短く書けます。

<!-- 完全な構文 -->
<a v-bind:href="url"></a>
<a v-on:click="doSomething"></a>

<!-- 省略記法 -->
<a :href="url"></a>
<a @click="doSomething"></a>

JavaScript

Vueは割と先進的なJS構文を使ってる場合が多いすね。

オブジェクト

一部のチュートリアルでオブジェクト作るときにこういうの書いてるのをみかけるかもしれません。

const state = require('./store.js')
const mutations = require('./mutations.js')

module.exports = { store, mutations }

この { store } なんですが、 { store: store } の省略です。プロパティ名と変数名が同じ場合にこう書けます。つまりこういうこと。

const store = require('./store.js')
const mutations = require('./mutations.js')

module.exports = {
  store: store,
    mutations: mutations
}

関数

一部のチュートリアルでオブジェクト作るときにこういうの書いてるのもみかけるかもしれません。

new Vue({
  computed: {
    sayHello () {
      return 'Hello'
    }
  }
})

function が書いてないけど、関数です。つまりこういうこと。

new Vue({
  computed: {
    sayHello: function () {
      return 'Hello'
    }
  }
})

getter/setter

もしかしたらこういうのもあるかも。

const obj = {
  get fullName () {
    return this.firstName + ' ' + this.lastName
  },
  set fullName (name) {
    const [ firstName, lastName ] = name.split(' ')
    this.firstName = firstName
    this.lastName = lastName
  }
}

これは短縮記法とは違う気もするんだけど、getter, setterをちゃちゃっと作る構文です。

ついでに [ a, b ] = array も短縮記法ですね。こんな風に書けます。

const obj = { a: 123, b: 234 }
const { a } = obj
console.log(a)  // => 123

const arr = [ 11, 22 ]
const [ first ] = arr
console.log(first)  // => 11

よくわからないこと

HTML的に妥当?

先の公式ガイドによると、以下。

これらは普通の HTML とはちょっと違うように見えるかもしれません。ですが、 : や @ は属性名に利用可能な文字です。

ほんと?

原文の方でもvalid chars for attribute namesだから誤訳でもなさそう。

いやまあ別に妥当じゃなくても個人的には構わないんだけど。

VueでAjaxやるときはaxiosがおすすめなんだって。(Vue.js始めるおれおれアドベントカレンダー2016 – 14日目)

カテゴリー: JavaScript

Vue.js始めるおれおれアドベントカレンダー2016 – 14日目

Vueあんまり関係ない感じだけど、全く関係ないわけでもないので良しとしてください。

VueでAjax

かつては公式で vue-resource というライブラリを用意していたけれど、今は axios というライブラリが公式推奨であるらしい。

axios

使い方も簡単そう。

IE 8+で動作。手厚い。(ちなみにVue.js v2はIE 9+。) もちろん昔の環境で動かすには最近の書き方は通じませんので、なんかまあバベるとか何とかしてください。

インストール

$ npm i -S axios
const axios = require('axios')

使い方

axios.get(url) みたいにして送信、戻り値はPromise。

res.data に応答内容が入ってるみたい。

data: {
  sending: false
},
methods: {
  form_submit (event) {
    this.sending = true

    axios.get(url, data)
      .then(res => {
        this.sending = false
        console.log(res.status, res.statusText, res.data)
        // => 200, "OK", { message: "You just sent the data!" }
      })
      .catch(error => {
        this.sending = false
        throw error
      })
  }
}

もっとVueに繋げる

vue-resourceはVueと密に繋がって、 this.$http.get() みたいな感じで使うそうだ。

axiosは完全に独立しているけれど、 this から参照できるようにしたい場合用に、vue-axiosというものを用意している人がいた。

これを導入すると this.axios.get() が使えるようになるとか。(試してません。)

長所は require() を省けることくらい? んーでも省かない方が思想的に良いのでは。

よくわからないこと

公式推奨って

何だろ? どこかに一覧とかあるのかな?

原文の表現は、引用符付きで “official recommendation” 。

(ビルトインの)フィルターは衰退しました。(Vue.js始めるおれおれアドベントカレンダー2016 – 13日目)

カテゴリー: JavaScript

Vue.js始めるおれおれアドベントカレンダー2016 – 13日目

簡単なフィルタは自作しなくても最初から用意されてたりしないかな、と思ったんだけど、v1にはあったものの現在v2では削除されたとか。

テキストを展開するためのフィルタは、現状はまだ利用できますが、すべてのフィルタが削除されました。それらの代わりに、(例えば日付のフォーマットには date-fns 、通貨処理には accounting と言った形で) より専門的なライブラリの使用を推奨します。

いや「vue フィルタ」とかで検索したら公式ドキュメントが出てきたんだけど、よく見たら古い版のドキュメントでした。

それでも使おうとすると

開発用(vue.js)の場合は警告が出ます。本番用(vue.min.js)では何も出ません。

[Vue warn]: Failed to resolve filter: capitalize
(found in anonymous component - use the "name" option for better debugging messages.)

エラーにはならないけど、フィルターがないので元の内容がそのまま出てきます。

自分で作ろう

同じ名前でカスタムフィルタを作って登録すれば良いらしい。

先のガイドに、一通りのフィルタの代替コードが掲載されてます。

つくりかた

こんな感じで、 Vue.filter に名前と実装を教えてあげる感じらしい。

Vue.filter('uppercase', function (value) {
  return result.toUpperCase()
})

あとコンストラクタ?コンポーネント?何と呼ぶの? まあこんな感じで指定することもできた。

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World!'
  },
  filters: {
    uppercase: function (value) {
      return value.toUpperCase()
    }
  }
})
<div id="app">
  <h1>{{message | uppercase}}</h1>
</div>

昔あったフィルタの一覧

あんまり歴史を知らないけれどもこれで大丈夫ですか?

文字列以外のフィルタ

今回は自分の目的ではないんだけれども、例えば繰り返し時の並び替えなんかのフィルタも、前はあったけどv2でなくなったそうです。

よくわからないこと

キャッシュ

とかどうなってるんだろ? やっぱり {{theValue | myFilter}} みたいな書き方したら、 theValue 変更まで結果を確保して再計算しないようになってるんだろうか。

調べてない。

おしまい

観たことなかったけど面白いじゃないか。四年前かあ。

ファイルを読み込んでプレビュー表示してみる。(Vue.js始めるおれおれアドベントカレンダー2016 – 12日目)

カテゴリー: JavaScript

Vue.js始めるおれおれアドベントカレンダー2016 – 12日目

昨日のフォームのやつで「ファイルはアレだよ」という話になってたので、アレするのを試してみました。

コードは抜粋しか記載してないのでGitHubの方で確認してください。

選択したファイルをプレビュー表示

ファイル情報の取得

  • App.vue
<input @change="file_change" ref="file" type="file" multiple />

<ul v-if="form.files.length > 0">
  <li v-for="(file, index) in form.files">
    #{{index + 1}}
    <ul>
      <li>名前: {{file.name}}</li>
      <li>サイズ: {{file.size.toLocaleString()}} bytes</li>
      <li>種類: {{file.type}}</li>
    </ul>
  </li>
</ul>
file_change (event) {
  const elFile = this.$refs.file
  const files = elFile.files
  form.commit('setFiles', files)
}
  • form.js
mutations: {
  setFiles (state, files) {
    state.files = Array.from(files).map(file => {
      const data = {
        name: file.name,
        size: file.size,
        type: file.type
      }

      state.files.push(data)
    })
  }
}

情報の所在

<input type="file"> の要素オブジェクトの files プロパティに格納されてます。

change イベントのタイミングで ref を使って参照します。でもってStoreに格納しました。

情報の形式

elForm.files は配列じゃなくてFileListなので、情報を配列に変換しました。

FileListをそのまま突っ込むと二度目以降の change イベントが発火しなくなった。

あ、あと multiple の有無によらずFileListなので、ひとつだけ選択してもらう場合は el.files[0] でアクセスします。

ファイル情報を純粋なオブジェクトへ変換

この例では単純に state.files = Array.from(files) だけでも間に合うんだけど、これから処理を足すので map() も併せて使ってます。あ、でもFileオブジェクトを突っ込むのも良くないのかな。なら今回みたいにして正解だ。

表示

v-for でくるくる出力。

というわけで、名前とか出すだけならこれで終わり。

画像を表示する

プレビュー機能を追加します。

state.files = Array.from(files).map(file => {
  const data = {
    name: file.name,
    size: file.size,
    type: file.type
  }

  if (file.type.startsWith('image/')) {
    data.previewImageSrc = window.URL.createObjectURL(file)
  }

…
<li v-if="file.previewImageSrc">
  <img :src="file.previewImageSrc" :alt="`${file.name}のプレビュー画像`" class="form-files-imagePreview" />
</li>

画像かどうかの判定

file.type に種類が格納されていて、例えば image/png とか text/html とか。

これが "image/" で開始していれば画像と判断します。

画像URLの作成

createObjectURL() を使ってFileオブジェクトから特殊なURLを作成します。あんまりよくわかってない。

本当は使用後に revokeObjectURL() で解放してやるのが良いらしい。

オブジェクト URL が不要になった場合にはこれらを逐一 window.URL.revokeObjectURL() で削除するのが望ましいでしょう。ブラウザーは、文書がアンロードされた際にこれらのオブジェクト URL をメモリから解放します。しかし、パフォーマンスとメモリ使用を考慮し、明示的にアンロードできる安全な機会があるならば、そうするべきです。

テキストを表示する

ついでにテキストも。

state.files = Array.from(files).map(file => {
  const data = {
    name: file.name,
    size: file.size,
    type: file.type
  }

…

  if (file.type.startsWith('text/')) {
    data.textContent = 'loading...'

    const reader = new window.FileReader()
    reader.onloadend = event => {
      const text = event.target.result
      data.textContent = text
    }
    reader.readAsText(file)
  }

…
<li v-if="file.textContent">
  ファイル冒頭:
  <pre>{{file.textContent | textPreview}}</pre>
</li>
Vue.filter('textPreview', function (value, length = 128) {
  let result = value.slice(0, length)
  if (value.length > 128) {
    result += '…'
  }
  return result
})

テキストかどうかの判定

画像と同じく "text/" で開始するかどうかで判定しました。

ただし、 *.js ファイルが application/javascript だったり *.csv が application/vnd.ms-excel だったりもするので、もうちょっとうまくやった方が良いかも。

テキストの内容を取得

ファイルの内容を読み込んで表示します。

読み込みにはFileReaderを使います。

読込処理が終了すると readyState は DONE に変わり、loadend イベントが生じます。それと同時に result プロパティにはファイルの内容が文字列として格納されます。

だそうです。

カスタムフィルターで冒頭だけ表示

冒頭部分に必要なら "…" を付けて抜き出してくれるフィルターを用意しました。

よくわからないこと

mutationの処理の中の非同期処理

良くないと聞いた。actionを通すとか?

まだそこらへん知らないので愚直にここに書いた。々ドキュメントを読もう……。

Bootstrapと <input type="file">

Vue関係ないんだけど。

ドキュメントの例だと何もしてない。

<div class="form-group">
    <label for="exampleInputFile">File input</label>
    <input type="file" id="exampleInputFile">
    <p class="help-block">Example block-level help text here.</p>
</div>

こんなもんか?

あとファイル情報の表示はもうあきらめてあんなになりました。 Thumbnail コンポーネント(?)を使うと良さそうだろうか。でも三種類あるんだよなー。

おしまい

デザインェ……。