typeof Diary

VimとかJSとか。やったことのメモ。自分のため。

Vueで動的にコンポーネントを追加する

ちょっとしたダイアログを出したいとか、ポップアップみたいなの出したいとか・・・。
そういうことありませんか?

コンポーネントライブラリ使ってれば、大体網羅できますが、要件に合わないとか、思ってるのとは違うなーみたいな状況とか。

方法

Vue.componentコンポーネント作って、その場で$mountまでしてあげます。 マウントしたやつをappendChildなりで入れたいとこに追加。

import PopupComponent from '@/components/popup'

export default {
  handleClick(e) {
    const Popup = Vue.component('popup', PopupComponent)
    const popup = new Popup().$mount()
    document.body.appendChild(popup.$el)
  }
}

props渡したいときは、new Popup({ propData: { } })で渡せます。

破棄もしっかりと

上で追加はできますが、閉じたときとかに消す必要がありますね。
例えば、画面上のどこかしらクリックされたら消す場合、上でいうとPopup側をこんな感じにしてやるといけます。

export default {
  created() {
    document.addEventListener('click', this.handleClickRemove)
  },
  beforeDestroy() {
    document.removeEventListener('click', this.handleClickRemove)
  },
  methods: {
    handleClickRemove() {
      this.$destroy()
      this.$el.parentNode.removeChild(this.$el)
    }
  }
}

createdでイベントつけてあげて、beforeDestroyで消してあげましょう。
あとはthis.$destroy()で自らをdestroyして、追加されたものもremoveChildで消してしまうと良いです。

ng-kyoto Angular Meetup #9に行ってきた

ng-kyotoに行ってきたので簡単に感想書いておきます。
先週の金曜日の話だけど・・・。

ng-kyoto.connpass.com

Canvasでスクロールを扱う際の座標計算と苦労

いきなりAngularの話ではなく、ReactHooksの話とか。

座標計算との戦い的なお話がありましたが、本当に大変そうでした。
音符のD&Dはmousemoveで頑張るとか、そういったところは参考になりました。
単純な要素の並び替えならdragStartとかでいいんですけど、物自体を動かすとなるとそうしないといけないんですね。

AWS AppSync + ApolloではじめるGraphQL

GraphQLは去年の1月にちょっと触って会社で発表して、あのときはgraphql-php使って書いてかなり辛かったのを覚えています。
1年経って、2019年の始めにまたGraphQLを触って、Apolloを触ってみたばかりだったので、かなりタイムリーな内容でした。

「GraphQL Server書くのって結構辛い」

そのとおりで、楽したいんですよね・・・。

AWS AppSyncとgraphql generator使うと良いらしい。
graphql generatorに関して言えば、頑張って書いてたのがコマンドライン一発でできてしまう・・・。
試してみたいと思います。

以下余談。 Apolloの既存のREST APIをそのままData Sourceにできるのあるけど、あれってどうなんですかね。
イマイチGraphQLのベストな感じが分からないというか。

初心者だからこそのAngular

Angularこういう風に使ってるよ。とかAngularのここがいいよ。みたいなお話。
「Angularは未来への投資」みたいなニュアンスが良いと思いました。

雰囲気でやっている人向けのRedux再入門

わかりやすいRedux講座。
Redux自体はとてもシンプルなんだけど、なぜか躓く。
とてもわかり易く説明していただきました。

自分もReduxは意味があまり分からずやっていて、Vuexでなんとか理解して、Vuex→ngrx→Reduxとかいう順番。
結局、この辺りの状態管理ライブラリは、どれかひとつそれらしく理解したら、他も分かると思います。

Reduxで個人的に分からなかったのは、どこにデータ加工ロジック置くんだろう?といったところ。
話聞く限りMiddlewareになるのかな。

感想

Angularの話は・・・?と思いつつ、フロントまわりのいろいろな話が聞けて満足。
D&Dの話とか、結構面倒なUI求められることが多いので、参考になりました。

最近Angular触ってる!ということで参加を決めたんですけど。
いろいろあってAngular触ることがまたなくなってしまった。Angular欲高まってたところでコレなので結構堪える。

それでも、また次回も参加できるならすると思います。

Observableの結合のこと

今年はブログ書くって言うたので書いてみます。
ネタは別のとこに書いたやつではあるけど。

前の記事でも触れていますが、昨年の秋ごろからAngularを触っています。 Angularでngrx使いつつです。

ことのなりゆき

(本題だけなら飛ばしてね)

年末にふとng-japanのSlackに以下の質問。

  • ngrxのeffects内でstoreのデータを使いたい場面に出くわした。
  • withLatestFrom(this.store$.select(fromRoot.getSelectedId))みたいにして繋げてしまうのはありなのか?

背景的には、選択中のカテゴリに属する記事を一覧表示。
その一覧から記事を削除したとき、表示中の一覧を更新したい。
更新用のAPI呼びたいけど、選択中のカテゴリIDがほしい。  みたいな、結構あるあるな状況ではないでしょうか?

もらった回答が、

  • withLatestFromでも良いけど、このケースならActionのpayloadにID含めて渡すのはどうか?

言われてみれば、確かに。。。

いざ実装してみると、payloadを次に渡してあげないといけなくて、どうするんだろう?

foo$: this.actions$.pipe(
  ofType(ActionName),
  map(action => action.payload),
  mergeMap(payload => {
    return this.api.fetchList().pipe(
      map(...)
      catchError(...)
    )
  }),
  mergeMap( /*ここにpayloadがほしい */
  ...

forkJoinを使ってまとめたらとやりたいことはできたのですが、テストが通らない。。。
なんで・・・?

理由は、結合方法にも何パターンかあって、それぞれの動きが異なるから。
この辺りちゃんと理解せずやったのでハマりました。

結合方法

Observableを結合するには、何パターンか方法があります。

  1. zip
  2. combineLatest
  3. forkJoin
  4. withLatestFrom

ざっくりこの4つ。

こんなObservableがあるとして・・・

const observable1 = Rx.Observable.interval(1000).map(x => x).take(5)
const observable2 = Rx.Observable.interval(2000).map(y => y + 1).take(5)

それぞれsubscribeすると、

observable1.subscribe(x => console.log(x))
// 0 - 1秒間隔で
// 1
// 2
// 3
// 4

observable2.subscribe(y => console.log(y))
// 1 - 2秒間隔で
// 2
// 3
// 4
// 5

こんな感じでobservable1は1秒間隔で0〜4observable21〜5が出力されます。
これを使ってそれぞれの違いを見ていきます。

zip

まずはzip

Observableのそれぞれの値から順に値が計算されるObservableを返す。

訳はChromeの翻訳。
意味を汲み取りましょう。

Rx.Observable.zip(observable1, observable2).subscribe(value => console.log('zip', value))

// 出力間隔を見るためのカウンター
const counter = 0
const interval = setInterval(() => {
  console.log(counter++)
  if (counter === 10) clearInterval(interval)
})

コンソールの出力は以下。[x, y]の形になっています。

0
1
zip [0, 1]
2
3
zip [1, 2]
4
5
zip [2, 3]
6
7
zip [3, 4]
8
9
zip [4, 5]

普通にsubscribeしたやつが、配列になっただけのような感じです。
間隔に注目してみると、observable1も2秒間隔になっています。

zipは、observable2を待って、1ペアずつ出力されている!

combineLatest

次、combineLatest

Observableのそれぞれの最新値から計算されるObservableを返す。

Rx.Observable.combineLatest(observable1, observable2).subscribe(value => console.log('combineLatest', value))

これを同じように出力してみます。

0
combineLatest [1, 1]
1
combineLatest [2, 1]
2
combineLatest [3, 1]
combineLatest [3, 2]
3
combineLatest [4, 2]
4
combineLatest [4, 3]
5
6
combineLatest [4, 4]
7
8
combineLatest [4, 5]
9

zipよりも出力回数が増えています。
zipobservable1observable2が揃って出力されていたのに対して、combineLatestはお構いなしに、その時の最新の値を出してきます。
ところどころ2連続で出力があるところはobservable2の間隔。
observable1が先に終わるので、最後の出力回数が減っているのも分かりますね。

forkJoin

次、forkJoin

全てのObservableが完了するのを待って、最後の値を返す。

Rx.Observable.forkJoin(observable1, observable2).subscripbe(value => console.log('forkJoin', value))

同じく出力します。

0
1
2
3
4
5
6
7
8
9
forkJoin [4, 5]

0〜9の間の出力がなくなりました。
observable1observable2の両方が完了したタイミングで出力されています。

withLatestFrom

withLatestFromも見ておきましょう。

ソースObservableが値を発行するたびに、その値と他の入力Observablesからの最新の値を使用して式を計算し、次にその式の出力を発行します。

これは前の3つとは少し違っています。
今回はobservable2observable1を入れて使ってみます。

observable2.withLatestFrom(observable1, (y, x) => y * x)
  .subscribe(value => console.log('withLatestFrom', value))

出力は

0
1
withLatestFrom 1 -- 1 * 1
2
3
withLatestFrom 6 -- 2 * 3
4
5
withLatestFrom 12 -- 3 * 4
6
7
withLatestFrom 16 -- 4 * 4
8
9
withLatestFrom 20 -- 5 * 4

2秒間隔で、observable2 * observable1が行われていますね。

まとめ

ユニットテストforkJoinが上手くいかなかったのは、おそらく完了を待ち続けていたのかな?
値が揃えばOKなら、zip
常に最新値でやってほしいならcombineLatest
1つのObservableに別のObservableを急に入れたいみたいな場合はwithLatestFrom

今回のも似てるようで全然違う挙動をするので、使い分けできるようにならないとなーと思いました。

mergeMapswitchMapあたりも怪しい・・・。

2018年まとめ

ブログ放置しまくってました。
2018年のまとめしときます。

2018年にやったこと

Vue.js

昨年末ぐらいからVue.jsをちまちまと触っていたこともあって、年明けからギョームの方でもVue.js使いました。 このときはVuexはまだ触っていなくて、busを使って、イベントでやり取りするような構成でやってました。

1年経って、そこそこ辛さが目立ってきました。

UIはElementUI使いました。

Electron

 GWぐらいでElectron触りました。
とはいってもrendererばっかりで、mainはほぼ書いてないので、あまりこっちの知識はつかずな感じに。
丁度Vue.js使ったあとだったこともあり、その流れでelectron-vue使って書いてました。

こっちはVuex使ってみましたが、あまり使い方が良くなかったようで、結構微妙な感じに・・・。
UIはこっちもElementUIでさくっと。

Angular

秋ぐらいからAngular触りはじめました。
あわせてTypeScriptも。

Angular + ngrxみたいな環境で絶賛やってます。
触り始める前は学習コスト高くてなんだかなーと思ってましたが、いざ触ってみると結構楽しいです。

Dart

なんとなく、触りだけやってみた。
結構サクサク書けるけど、読みやすいかと言われると微妙な印象でした。

Docker

今更!?って感じではあるのですが、やっと開発環境をDockerに作るようにできました。
やっとです。

エディタ

3/4Vim残りがVSCode

Angularをやると必然的にTypeScriptを書くことになるわけですが、やっぱりここはVSCodeの方が楽でした。
VSCodeにはVimキーバインドにするプラグインを放り込んで使ってます。
ただキーバインド(hjkl)真似てあるだけではなくて、そこそこ機能が使えるので、あまり不自由なく使えています。

1点問題があって、行数が多いファイルのとき、文字入力のラグが目立って使い物にならないのです・・・。

その他

jQueryはできず。
ReactもVueもAngularも一通り軽く触って、抜けられるような感じにはしていっていますが、jQueryガンガン使って書いたやつから、脱jQueryとはなかなかいかず、辛さだけが増している状態です。

あまりに辛くて、一部にVue.js入れてやってみたりもしました。
結構楽にはなるけど、それはそれでややこしくなって・・・。

参加したカンファレンス

PHPカンファレンス関西、Vue Fes Japan、VimConf、HTML5Confに参加しました。
来年もいろいろと参加していきたいです。

Angularのカンファレンスも行きたいな。

2019年は・・・

以前のようにブログちょいちょい書いていきたいかも。
丁度Angularやってるし。

それでは良いお年を。

PHPカンファレンス関西2018に参加した

なんやかんやで都合が合わずに参加できてなかったPHPカンファレンス関西。
今年は問題なく参加できたので、そのレポ。

脱「なんちゃって」フロントエンド / 榊原昌彦

脱「なんちゃって」フロントエンド

PHPカンファレンスなんですけどね?
朝一からフロントエンドの話を聞くモノ好き。

自分自身がフロントエンドばっかりの人間なので、聞きたかったセッション。

SPAから最近流行りのPWAまで。
SPAはそこそこ知ってたけど、PWAのことはあまり知らなかったので参考になった。

PWA

  • オフライン表示
    • キャッシュ表示。
  • Background Sync
    • オフライン時に送信したデータを持ち続ける。
    • オンラインに復帰したら自動的に送信。
  • Push通知
    • Push通知のためにモバイルアプリ化する必要がなくなった。
  • ホーム画面に追加
  • GPSやカメラなどのNative機能へのアクセス

単に、Webアプリでホーム画面に追加できるぐらいの知識しかなかった。

プラットフォームのはなし

CRDOVACapacitorなど。

CORDOVA

オープンソースモバイル開発フレームワーク
HTML5JavaScriptCSSといったWeb開発技術でモバイルアプリ開発ができる。

Capacitor

これも似たような感じ。
Web開発技術でモバイルアプリ開発ができる。

この辺りの技術を使えば、SPAでしっかりアプリを作れば...
SPA → Capacitor → PWA、iOSAndroid、Electron...などなど。
各プラットフォームに対応させられる。

エモい。

その他

・ユーザだけでなくサーバにも優しく
ViewをPHP(サーバ)でレンダリングするのは重い。

・自由自在なエラーハンドリング
サーバ側のどこかでエラーがあると、そこで止まる。
REST APIでHTTP Response、Status Codeをちゃんとコントロールすれば、その辺りは解決できる。

いずれ、PHPでフロントエンドを描画するのはアンチパターンかも?
だから、フロントエンド学びましょう。

若干極論感はあるけども。

質疑応答のターン。
せっかくSPAの話もあったし。良い機会なのでSPAに関する疑問をぶつけてみた。
ワシ
「SPA、SPAって言われてるけど、ネガティブな意見に'メモリリークするから・・・'みたいなのを聞くんですが。あれって実際のところどうなの?」

メモリリークするから、SPAじゃなくて、ページだけ切り分けといた方が。
っていう意見があって、結局PHPでルーティング、webpackでページごとにJSファイル作るみたいなことをしたことがある。

回答としては、あまり気にしなくても良いとのこと。
フレームワークがそれなりによしなにしてくれるのもあるみたい。

周りにフロントエンドの相談持ちかけられるような人もいないので、疑問視してたとこが吹き飛んでよかった。
しょうもない疑問に答えていただいて感謝。

続・SOLIDの原則ってどんなふうに使うの? / 後藤秀宣

続・SOLIDの原則ってどんなふうに使うの?

こういう話、最近大好き。
コードをいかに保守性とかを考えて綺麗に書けるようになるか。

無限に考えるけど、なかなかできなくって、すぐに散らかってしまう。

SOLIDの原則の中でも、「オープン・クローズドの原則」に絞ったセッション内容。
そもそもオープン・クローズドの原則とは...

・オープン = 拡張できる
・クローズド = 拡張するとき、既存コードが影響を受けない

モジュールに新たな振る舞いを追加する際、既存コードを修正せず、
単に新しいコードを追加するだけで、目的を達成できる状態になっていること。

バリエーションからコードを保護
①バリエーションによって変化する部分は?
②バリエーションの軸にそってまとめられているか

このあたりに着目。

これ実践できたら、いいけど、なかなか難しい。
こういう設計系の話は、試してみないことには・・・ってことが大半なので一度試してみる。

フロントエンドエンジニアが伝たない最近の事情 / 大原壯太

フロントエンドエンジニアが伝えたい最近の事情

フロントエンドPart2。
セッションタイトル通り、最近の事情をざっくりと。

途中までは知ってることが大半なので、「そうだなー」ぐらいの感覚で聞いていた。

得たモノ→ Swagger
REST APIを構築するためのオープンソースフレームワーク
これは便利そうだ。

HerokuでPHPアプリ開発速度を倍にする / 岡本充洋

現場編として、後藤知宏さんによるセッションとのスイッチ形式。

デモの内容としては、LaravelのアプリをHeroku上に上げるとこ。DBも使うようなやつ。
一瞬だった。

めちゃくちゃ便利そうに感じた。
Herokuのアカウントは持っているけど、全然使っていないし、この際に一度やってみたい。

GAE に PHPアプリを継続的デプロイする / 岸田健一郎

GAEにPHPアプリを継続的デプロイする

↑がHerokuなら、今度はGAE。
こっちも似たような感じで、GAEに上げましょうっていう内容。

この時間、うさみさんのpixivのやつ聞く予定だったけど、人多すぎて断念。

感想

お前、PHPカンファレンスってちゃんと読めてる?感じのセッション巡りなのはお許しを。
午前中のセッションにはVue.jsのセッションもあったし、フロントエンドの需要ってまだまだあるんだなと実感。

最近、自分の持ってるフロントエンドの技術って需要あんの?って悲観的だったけど、
まだまだ需要ありそうで安心。

とはいえ、フロントエンドのことを相談できる人が身近にほしい。
コミュニティ参加がベストかな。

そんなことを思ったPHPカンファレンス

Vue.js / Nuxt.js Meetup Osaka #0に参加した

Vue.js / Nuxt.js Meetup Osaka #0に参加したので、そのレポ。

デザイナーの私が Vue.js を触ってみた / Yasui Risaさん

資料→ デザイナーの私が Vue.js を触ってみた

タイトル通り。内容は初心者向け。
環境構築のやり方は飛ばして、CodePenで実演しつつの発表。

なぜCodePenか?→環境構築からやると難しくて心が折れる可能性が・・・

この考え方には納得。
自分も何か作るときは、JSFiddleで断片を作ってというのが多い。

Vue.jsを始めるキッカケにはとても良いセッションだった。
データの表示から、各ディレクティブの説明が丁寧。

AVAでのテストとpuppeteer / 豊川 泰弘さん

AVAでJavaScriptのテストとE2Eテストのこと。

AVA → JavaScriptのテストフレームワーク
mochaと比べるとassertの書き方が見やすい?

どんなときにテストを書いていたか?
→ コードレビュー時とか。テストケースの追加、バグの発見などにつながるかも。
puppeteer
Chrome Devtools Protocolでブラウザの情報を取得・操作するためのライブラリ。

自分は、vue-cliでてきとーに作って、E2E環境はNightWatch。
puppeteer試してみたい。

全体的にJavaScriptでテスト書いてる人は少ない感じだった。
理由は様々。テストの書き方が分からないとかから、時間がないなど。

自分も分からないなりに探り探りでやってる状態。
書き出して思うのは、「これ、書かないと一生書けないし、書かないな」。
Qiitaとかにテストの書き方みたいな記事は溢れているけど・・・。
結局add(a, b) assert(add(1,2)).to.equal(3)みたいな単純なもの。
これじゃ書けないよなとも思う。

Nuxt ディレクトリ設計パターン / andoshin11さん

資料→ Nuxt ディレクトリ設計パターン

Clear Architectureについて。
Clear Architectureは初めて聞いた。

アーキテクチャ周り、しっかりと見に付けてあげると、良い設計ができそうだと感じた。
セッション内でもあった通り、2、3人程度でならない方が良い。

結局VuexだとActionsにロジックが集中して太っていくのは実体験としてあって、どうにかならないかなとは思っていた。
例えば、ファイルアップロードするAPIを叩くにしても、
Viewでファイル受け取って、それをActionsのfileUploadみたいなのに渡す。
何も考えずにできるならそれだけで終わるけど、ならそのファイルのバリデーション系はどこに置くの?みたいな悩み。

またそのバリデーション内でもAPI叩いて、同名の存在確認とか容量確認とかしなきゃならないみたいな。。。
canUploadFileみたいなのをActionsに書いて、Promise返してresolveされたらfileUploadみたいにした。

なんかおかしいなと思いつつ・・・。安易にUtilsみたいなのは怖い。

こういった悩みの解決にも繋がる考えな気もするので、役立つセッションだった。

Vue.js とはなにか、Nuxt.js とはなにか / 後藤知宏さん

Vue.jsとNuxt.jsのことを分かりやすく。
いったいどんなことができるのか。どういったものなのか。

Vue.jsはCDNで読み込むだけで使える。

普段、vue-cliで一からVueを使うということをやっているが、CDNで読み込む方は盲点だった。
new Vueだけして、データバインディングとカスタムコンポーネントを軽く入れられる。
jQueryガリガリ書かれた辛いコードでも、これなら少し楽になりそうだと感じた。
実務でjQueryで辛くなってしまったPJがあるので、そこに導入して試したい。
(API一気に8個とか叩くんですよ・・・それ整形して画面に・・・辛い)

Vue.jsでスタイルを動的に変更する3つの方法(LT) / qwerty8tさん

Vue.jsのスタイル変更方法。

classオブジェクトがいいよ!っていう結論。
あれ初めて使い方知ったときは感動したのを思い出した。

Vue.jsのmethodsとcomputed(LT) / ショウノシオリさん

methodsのcomputedの違いについて。

あまり考えないとmethodsだけでもどうにでもできてしまう。
computedはキャッシュされるので、毎回処理が走らない。computedで使っているデータが変化したときだけ走る。
methodsは毎回。

全体的な感想

参加者の様子、Vue.jsを触ったことがある人もちらほら。
まだない人のほうがちょっと多めな印象だった。

触ってない人も多いところから考えると、Vue.jsに対する関心は高まっているのだなと感じた。

もっと難しいことを話さないといけないのかなと思っていたが、connpassのページにも書いてある通り、
「難しい話をしないと...!」ではなかった。

あ、こういうのでも良いんだ。と感じた。
Vueを使ってるなら当たり前なことでも全然大丈夫そう。
登壇のハードルは今回参加してみて自分中ではぐっと下がった。

何か話したいことがあったら、LTでもしてみたいな。

次回が楽しみ!

にしても、関西圏でVue.jsの勉強会は嬉しすぎた。

NativeScript-Vueを触った

久々の更新。

2月にこんな発表がありました。
Announcing NativeScript-Vue 1.0

要約。「nativescript-vueの1.0が出たよ」

NativeScript

JavaScriptとかTypeScript、Angularを使って、ネイティブなモバイルアプリを作ることができるフレームワークです。
詳しいことは公式参照

JavaScriptでモバイルアプリと聞くと、React Nativeなんかもありますね。

で、今回はそれをVueで書けるでっていうやつです。
最近Vue推しの自分にとっては格好のおもちゃですね。

早速遊びます。

準備

まずはNativeScriptがないと始まりません。

npm install -g native-script

次にモバイルアプリなので、こっちもやっておいてください。
IOSだけなら、JDKは入れなくてもまぁOK。
NativeScript Advanced Setup: macOS

長いのでカット。

次に、vue-cli放り込みましょう。プロジェクトのテンプレート作ってくれます。

npm install -g @vue/cli @vue/cli-init
vue init nativescript-vue/vue-cli-template <project-name>
cd <project-name>
npm install

npm installまで終わったら、npm run watch:<platform>を実行します。
今回はIOSなので、npm run watch:iosとします。

すると、シミュレーターが開くはず。
f:id:lisia:20180310220241p:plain

ひとまずここまで。
基本はここの通りです。

アプリを動かす

シミュレーター動いたのは良いけど、アプリないやん!って話ですよね。
多分そもそもビルドされてないから・・・?

package.jsonのscript内を見てみると、使えるコマンドが分かります。
package.json抜粋

  "scripts": {
    "build": "webpack --env.tnsAction build",
    "build:android": "npm run build -- --env.android",
    "build:ios": "npm run build -- --env.ios",
    "debug": "webpack --watch --env.tnsAction debug",
    "debug:android": "npm run debug -- --env.android",
    "debug:ios": "npm run debug -- --env.ios",
    "watch": "webpack --watch --env.tnsAction run",
    "watch:android": "npm run watch -- --env.android",
    "watch:ios": "npm run watch -- --env.ios",
    "clean": "rimraf dist"
  },

それっぽいnpm run debug:ios叩いてみます。

f:id:lisia:20180310220514p:plain

こんな感じでサンプルアプリが立ち上がりました。
(この後、watchしたら大丈夫でした。)

ちょっとサンプルを作ってみる

APIも叩きたいので、GiphyのAPI叩いてgifを表示するようなものにしてみます。
Vuexのサンプルで作ったことがあるこれが元ネタ。

サンプルなので、ひとつのコンポーネントにまとめてしまいます。

とりあえず画面を作る

src以下がコードを書くところです。
src/components以下にGiphy.vueを作成します。
touch src/components/Giphy.vue

<template>
 <Page class="page">
    <ActionBar class="action-bar" title="Giphy">
      <NavigationButton
        text="Go Back"
        android.systemIcon="ic_menu_back"
        @tap="$router.push('/home')"
      />
    </ActionBar>

    <StackLayout>
      <label text="Welcome to GiphyPage!"/>
    </StackLayout>
  </Page>
</template>

HelloWorldを真似て、こんな感じでtemplateを書いておきます。
次に、ページへのルーティングが必要なのでsrc/router/index.jsに書き加えます。

// ...
import Giphy from '../components/Giphy'

const router = new VueRouter({
  pageRouting: true,
  routes: [
    // ...
    {
      path: '/giphy',
      component: Giphy,
      meta: {
        title: 'Giphy'
      }
    }
  ]
}) 
// ...

homeの画面もボタンを追加しておかないとですね。

<StackLayout>
  <Button class="btn btn-primary" @tap="$router.push('/counter')">Counter</Button>
  <Button class="btn btn-primary" @tap="$router.push('/hello')">Hello World</Button>
  <Button class="btn btn-primary" @tap="$router.push('/giphy')">Giphy</Button>
</StackLayout>

画面を確認してみましょう。
f:id:lisia:20180310220536p:plain

タップして、画面が遷移することを確認します。
f:id:lisia:20180310220549p:plain

文字見えにくいけど気にしない。とりあえず、画面遷移を文字が表示できました。

処理とか入れていく

テンプレートに関しては、検索用のテキストボックスと表示領域があれば事足ります。
処理の流れは、テキストが入力されたらGhipyのAPIを叩いて、その結果をテンプレートに表示する。

これだけです。
では早速、まずはテンプレート。

<template>
  <Page class="page">
    <ActionBar class="action-bar" title="Giphy">
      <NavigationButton text="Go Back" android.systemIcon="ic_menu_back" @tap="$router.push('/home')"/>
    </ActionBar>

    <ScrollView>
      <StackLayout>
        <SearchBar
          :text="searchWord"
          hint="Please input search word"
          color="#000"
          margin="20"
          v-model="searchWord"
          @textChange="handleChangeSearchWord"
        />
        <template v-if="gifList.length">
          <Image
            v-for="(gif, index) in gifList"
            :src="gif.images.fixed_height_still.url" stretch="none"
          />
        </template>
      </StackLayout>
    </ScrollView>
  </Page>
</template>

続いて、script部分。

export default {
  name: 'Giphy',

  data() {
    return {
      searchWord: '',
      gifList: []
    }
  },

  methods: {
    handleChangeSearchWord() {
      const params = encodeURIComponent(this.searchWord).replace(/%20/g, '+')
      const url = `https://api.giphy.com/v1/gifs/search?api_key=YOURAPIKEY&q=${params}`

      fetch(url)
      .then(res => res.json())
      .then(json => {
        this.gifList = json.data
      })
    }
  }
}

軽く説明。

テンプレート側は、スクロールさせたいので、<ScrollView>で囲んでいます。
検索は<TextFiled>を最初は使っていたのですが、ドキュメント読んでると、<SearchBar>なるものがあったので、こちらを採用。

script側も特に言うことはなくて、単純にAPIを叩いているだけです。
結果をdataのgifListに放り込みます。

完成!

f:id:lisia:20180310220403g:plain

まとめ

  • JavaScriptでモバイルアプリ書ける 便利
  • しかもVue使える 便利
  • 慣れ親しんだもので書けるのはやっぱり 便利

以上!