「AngularJS」と一致するもの

ブラウザ側に情報を取っておく手段としてlocalStorageがあります。 昔はcookieを使っていたことが多く、今でもまだよく使われているのですが、 モダンブラウザ対応ならlocalStorageの方が適しているのではないかと思います。

以前、JavaScript nativeでlocalStorageを使うコードは書いたことがあって、 別に不満はなかったのですが、AngularでlocalStorageを簡単に扱えるライブラリがあると 思って調べてみました。

このあたりがメジャーそうです。順当に行くとStar数が一番多いangular-local-storageを選ぶのですが、 じっくりREADMEを読んでみます。APIは自明で分かりやすいです。bindはangularならではのAPIで便利そうです。 localStorageが使えない場合はcookieにfallbackするそうです。APIは抽象的に作られているということですね。

さて、2つ目のangularLocalStorageはどうでしょう。名前が似ていますが別物です。 bindもあるしAPIもシンプルで分かりやすいです。cookieへのfallbackもあるそうです。 angular-local-storageと比較すると若い分、Star数が少ないだけでしょうか。issueも処理されて アクティブに見えます。でも違いがない分、ドキュメントが充実しているangular-local-storageを選びたくなります。

最後の、ngStorageは、in the Angular Wayということで少し変わった感じです。

No Cookie Fallback - With Web Storage being readily available in all the browsers AngularJS officially supports, such fallback is largely redundant.

だそうです。確かにangularを使うようなブラウザでは、localStorageをサポートしていないケースは無いですね。 これは思い切りがよいです。気に入りました。

$scope.$storage = $localStorage;

使い方もこれだけです。APIとかそういうレベルではない感じ。とてもいいです。

And use it like you-already-know

だそうです。気に入りました。 これはつまり、localStorageを加えた3-way data bindingということですね。 これは応用が広がりそうです。時間があったらコードも読んでみたいです。

というわけで、おすすめはngStorageでした。

ng-conf 2015の動画を見ていたら、angular meteor integrationの動画がありました。

https://www.youtube.com/watch?v=uFmf-DeCdEE

Meteorを試した時に、angularを利用する可能性は考えましたし、 そのような記事もどこかで読んだ気がします。 しかし、Meteorが元から持っているテンプレート機能と重複しますし、 Meteorの恩恵をあまり受けられないかと思っていました。

ところが、Angular-Meteorはそれなりにうまく作られているようです。 興味がある方は、チュートリアル を見てみるとよいでしょう。 話が脱線しますが、このチュートリアルとてもやりやすいです。 Meteor本体のチュートリアルもそうですが、チュートリアルなのにかなり本格的な アプリを作っています。 Famo.us universityを見たときも思いましたが、チュートリアル周りの ベストプラクティスがたまってきていますね。 自分でもうまく活用できればいいと思います。

Angular-Meteorのチュートリアルをやってみて一番気に入ったのは、 3-way data bindingです。 これができるということは、AngularFireの代替になるのではないでしょうか。 ちょっとなにか作ってみたくなりました。 逆に一番気になったのは、getReactively周りの処理です。 特に手動でunsubscribeしなければいけないのは面倒です。

Maybe in the future we will add an automatic way to open and close subscriptions in scope's load and destroy events.

とあるので、解決されることを期待します。

話はまた変わりますが、Firebaseのtalkもng-conf 2015にありました。

https://www.youtube.com/watch?v=4nD5fjpIesk

こちらは、本家本流なのですごいですね。v1.0がリリースされたそうです。 彼らが3-way data bindingの名付け親です、たぶん。

Meteorはとても魅力的なのですが、自分が本当に気に入るか分からずちょっと悩んでいます。 その理由は、Fiberを使っていることと、jQueryを使っていること。 callbackは結構好きなのでFiberに魅力をあまり感じません。 一応、Fiberの流儀を使わず、callbackでも書けるので プロジェクトのコーディング規約で気をつければいいのですが。 jQueryの方は、非依存にする話もあるようですが、コアが依存しているという記述もあり、 完全に非依存になるには時間がかかりそうです。

https://groups.google.com/forum/?fromgroups=#!topic/meteor-talk/21y9NbM9v90

別に、jQuery依存だからといって困ることはないのですが、単に気分の問題です。

いずれにしても、Angualr-Meteorにはちょっと興味を持ちました。

Angularのdependency injectionはminifyに弱いです。 angular.jsのチュートリアルにも書かれています。

https://code.angularjs.org/1.3.8/docs/tutorial/step_05

A Note on Minificationのセクションに、 annotationをつけると書いてあります。

しばらく悩まずこの書式で書いていましたが、これはとても気をつかいます。スペルミスや並び替えをするときなど。なによりも、同じ文字を2回タイプするのは効率が悪いです。

ngminというのは聞いたことがあったのですが、100%変換できるわけではないという噂を聞いて使っていませんでした。今ではng-annotateというツールがあります。

https://github.com/olov/ng-annotate

ngminとの違いも説明されています。うまく行かない場合のためにコメントで明示的に指示できるというのが安心感があります。

これで快適になりました。やはり、自動化できることを人がやるのは無駄ですね。

ng-annotateはminifyをしてくれないので、minifyツールと合わせて使うことになります。今回は、

https://github.com/mishoo/UglifyJS2

を使うことにしました。下記にpackage.jsonを載せます。

{
  "scripts": {
    "minify:main": "ng-annotate -a public/main.js | uglifyjs -c -m > public/main.min.js"
  },
  "devDependencies": {
    "ng-annotate": "*",
    "uglify-js": "*"
  }
}

これで、

npm run minify:main

で動きます。 grunt等を使いたい人は、対応しているプラグインを使えばできるでしょう。

GitHub便利です。その一つが、Compare ViewのDiffです。Pull Requestを確認するときもよく見ます。コマンドラインでgit diffと普通にやるより見やすいです。

さて、背景は省略しますが、HTMLでdiffを表示したいと思いました。AngularJSでの利用を想定しているので、JavaScriptのライブラリが欲しいです。

ところが探してもあまり見つかりません。よくまとまっているのはこちら。

http://stackoverflow.com/questions/3053587/javascript-based-diff-utility

ちょっと古い投稿ですが。一番人気なのは、これです。

https://github.com/cemerick/jsdifflib

オンラインデモはこちら。

http://cemerick.github.io/jsdifflib/demo.html

だいぶ求めているもののイメージに近いです。 コードも枯れているらしい。 HTMLが出力されるので、Angularで使うならdirectiveを書かなければいけないですが、それは仕方ないですね。

これで行こうかと思ったところでしたが、なにか物足りません。 なんというか、見た目がワクワクしないのです。 改めてGitHubのDiffと比較してみると、全体的に色が濃すぎる。 あと、単語の差分がハイライトされない。色については自分でcssを書いて調整すればよいでしょうが、単語のハイライトはそれでは済みません。

というわけで、もう一度検索しました。見つけたのがこちらです。

https://github.com/rtfpessoa/diff2html

オンラインデモはこちら。

http://rtfpessoa.github.io/diff2html/

おお、おしゃれ。これですよこれ。なんとStar数は3でした。まったく注目されていません。コードも枯れていないでしょう。まだ不具合もあります。でも、魅力的です。これを使いましょう。

ところが、使うには一つ問題がありました。diff2htmlはdiffの出力を入力としているのです。例えば、

diff --git a/foo.txt b/foo.txt
---
+++
@@ -1,4 +1,5 @@
+zero
 one
 two
-three
+tree
 four

このようなものです。つまり、次はこれを出力するライブラリを探しました。いくつかありそうでしたが、比較的すんなり結合できたのはこちらです。

https://github.com/qiao/difflib.js

入力の行が配列になることと、出力の一行目のヘッダが付かないことなど不足点はありまして、それらは自力で対応しました。

最後にこれらを全部つなげて、angular.jsのdirectiveを書いてできあがりです。下記のような感じで使えるようになりました。

<my-diff path="foo.txt" old-content="one\ntwo\nthree\nfour\n" new-content="zero\none\ntwo\n\tree\nfour\n">

便利です。ちょっと気になることと言えば、依存javascriptが3ファイルもあって、グローバルに関数を定義するようなものであることです。なにかうまく隠蔽する方法があるのでしょうか。

AngularJSの流れにちゃんとついていくためng-europeをyoutubeで見ようと思い始めて一ヶ月が過ぎてしまいました。 まだ全部は見ていませんが、今日は一本見ました。興味深かったので記事にします。

https://www.youtube.com/watch?v=tfVA1syv-3o

AngularUIのui-bootstrapのお話です。 単にラップするわけではなくAngular流に作られているのと、カスタマイズ性を重視しているあたりは参考になります。

ui-bootstrapはBootstrap 3のcssのみを利用していてjavascriptの部分は全部書き直しているようです。つまり、jQuery依存もなしです。

ちょっと昔話を。当初、Bootstrap 2を使っていたのですが、クラス名などがどうも好きになれず、その後inkというcssフレームワークを見つけて 、しばらく使っていました。

その時から、inkのcssのみを使ってコーディングしていました。ロジックが必要な場合はAngular側で自前で作ってました。 例えば、カレンダーも当初はAngularUIのui-calendarを使っていましたが、 jQuery依存に抵抗があったため、カレンダーも単純な機能ならinkで自前で実装していました。

その後、Bootstrap 3が登場し、クラス名がinkのようにシンプルになったので、少し気に入ってBootstrap 3を使い始めました。でも、cssだけで、jsは使いません。例えば、alertもロジックはangularで自前で書いていました。と言っても、あまりコーディング量は多くなく、特に困っていません。

さて、話を戻しますと、ui-bootstrapは上記でやっていたようなことをちゃんとやっているような印象です。cssだけを使って、ロジックはangular用にdirectiveを設計してあるようです。 alertを見る限りでは単純なので自前で用意しても変わりませんが、他の複雑な機能はui-bootstrapのdirectiveを使うとだいぶ楽できそうです。

というわけで、jQuery非依存のui-bootstrapはおすすめです。

AngularJSの1.3.0がリリースされました。

これまで、1.2系を使っていたのですが、これを機に1.3も試してみようと思いました。

1.0から1.2に移行したときは、はまったのですが、今回はなにも不都合がありませんでした。

ところが、e2eテストが動かなくなってしまいました。具体的には、下記のようなテストがエラーになりました。

expect(element(by.repeater('item in list').row(0).column('{{item.name}}')).getText()).toEqual('foo');

エラー文は、

NoSuchElementError: No element found using locator: ...

です。

https://github.com/angular/protractor/blob/master/docs/api.md

のドキュメントを見ても問題なさそうですし、StackOverflowなどでも話題になっているのは見つからず、途方にくれていました。

が、試しに下記のように変更したら動きました。

expect(element(by.repeater('item in list').row(0).column('item.name')).getText()).toEqual('foo');

これって正しい解決法なのでしょうか。

過去にKarma(Testacular)を使ったことはありますが、今回やっと時代に追いついてProtractorを使ってみました。特にPhantomJSを使ってheadlessでE2Eテストするときに気づいたことをまとめます。

Protractorは、 Seleniumの WebDriverJSのラッパーで、AngularJSのE2Eテストのためのフレームワークです。

https://docs.angularjs.org/guide/e2e-testing

がエントリーポイントになるでしょう。 しかし、これだけ読んでも最終的な実行方法は分かりづらいです。 今回の目的は、コマンド一つ、例えば、

$ npm run e2e-test

でE2Eテストを実行してするための環境セットを作ることです。また、headlessで動かしたかったので、典型的に使われるChromeではなく、PhantomJSを前提にしました。サーバはExpress/Node.jsの想定ですが、それ以外の場合にも応用できるかと思います。

初めによくある環境設定です。

$ npm install protractor
$ npm install phantomjs
$ ./node_modules/.bin/webdriver-manager update

この例ではローカルディレクトリにモジュールをインストールしていますが。グローバルにインストールしたい人はそのようにどうぞ。

まず初めにはまったことはwebdriverが起動しないことでした。

$ ./node_modules/.bin/webdriver-manager start
INFO - Launching a standalone server
WARN - null
java.net.SocketException: No such device
    at java.net.NetworkInterface.isLoopback0(Native Method)

このようなエラーがでました。ワーニングかと思って無視していたらいけませんでした。どうやら、IPv6のアドレスのみを使っていてうまく行かないようだったので、Javaのオプションを追加して次のようにするとうまく行くことが分かりました。

$ env _JAVA_OPTIONS=-Djava.net.preferIPv4Stack=true ./node_modules/.bin/webdriver-manager start

さて、Protractorのチュートリアルには、webdriverをコマンドラインで手動で実行するように書いてありますが、個人的にはこれは面倒に感じます。時間はかかりますが、毎回自動で起動して欲しいです。これは簡単で、protractor.conf.jsにseleniumAddressを書かなければよいだけでした。下記に、protractor.confを載せます。

exports.config = {
  specs: [
    '*-spec.js'
  ],
  capabilities: {
    'browserName': 'phantomjs',
    'phantomjs.binary.path': './node_modules/.bin/phantomjs'
  },
  baseUrl: 'http://localhost:' + (process.env.PORT || 3000) + '/'
};

これだけで大丈夫でした。baseUrlは、specにURLをフルパスで書かなくてもよいようにするためで、expressが下記のように起動されている想定です。

app.listen(process.env.PORT || 3000);

specの細かい内容は今回は書きませんが、もっとも単純なものは例えば次のようになります。

describe('tutorial', function() {
  it('should have a title', function() {
    browser.get('index.html');
    expect(browser.getTitle()).toEqual('The Title');
  });
});

ところで、Protractorはテストのフレームワークであるもののアプリの起動には関与せず、アプリは自分で起動しなければいけません。また、テスト終了後はアプリも終了したいです。今回はお手軽にシェルスクリプトを書きました。

#!/bin/sh

export _JAVA_OPTIONS=-Djava.net.preferIPv4Stack=true
export PORT=12345
node ./app.js &
PID=$!
./node_modules/.bin/protractor ./e2e-test/protractor.conf.js
kill $PID

このスクリプトをpackage.jsonのscriptsに書いておけば、当初の目的であったnpmで一発でE2Eテストを走らせることができました。

Happy e2e testing!

famo.usは描画エンジンです。 先日紹介したときに言及したデモは下記にありました。

こちら

このfamo.usをAngularと統合した人たちがいます。

http://famo.us/integrations/angular/

これがなかなかいいのです。famo.usはJavaScriptですべて書くのですが、ちょっと手続き的な感じになります。それをAngularのdirectiveに書けるようにするのです。ちゃんと、bindingも動くので、更新系がなんとも簡単なのです。Angularの経験があれば普通なので、特に驚きもないと言えばそれまでなのですが、これに慣れると、生のfamo.usを書くのは面倒くさく感じるようになるかもしれません。

というわけで、サンプルを作ってみました。 1つのHTMLにすべて詰め込みたかったので、single fileのfamous-angularを使いました。通常は、bowerでインストールする方法が紹介されています。

single fileのものはcdnにないので、http://rawgit.com/を使いました。ところで、rawgitはcdn版(beta)もあってすばらしいです。これは続いて欲しいものです。

さて、出来上がったサンプルは

こちら

です。ソースコードはgistにありますが、下記にも貼り付けます。

<html ng-app="MyApp">
<head>
  <title>famo.us angular sample</title>
  <link type="text/css" href="//code.famo.us/famous/0.2/famous.css" rel="stylesheet" />
  <link type="text/css" href="//cdn.rawgit.com/Famous/famous-angular/fee2b717a53ad762c9e3157580ce255901f4ccad/dist/famous-angular.min.css" rel="stylesheet" />
  <script type="text/javascript" src="//code.famo.us/lib/require.js"></script>
  <script type="text/javascript" src="//code.famo.us/famous/0.2/famous.js"></script>
  <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.min.js"></script>
  <script type="text/javascript" src="//cdn.rawgit.com/Famous/famous-angular/fee2b717a53ad762c9e3157580ce255901f4ccad/dist/famous-angular.min.js"></script>
  <script type="text/javascript">
angular.module('MyApp', ['famous.angular'])
.controller('MyController', ['$scope', function($scope) {
  $scope.items = [];
  for (var y = 0; y < 1; y += 0.1) {
    for (var x = 0; x < 1; x += 0.1) {
      $scope.items.push({x: x, y: y, color: 'yellow'});
    }
  }
}]);
  </script>
</head>
<body ng-controller="MyController">
  <fa-app>
    <fa-modifier ng-repeat="item in items" fa-origin="[item.x, item.y]">
      <fa-surface fa-size="[30, 20]" fa-click="item.color = 'red'" fa-background-color="item.color" fa-properties="{textAlign: 'center', lineHeight: '20px'}">{{$index}}</fa-surface>
    </fa-modifier>
  </fa-app>
</body>
</html>

Famo.us/Angularはまだ発展途上ですべての機能がdirective化されていませんが、今後に期待しています。

モバイルファーストの次はオフラインファーストってことで、前から作ってみたいと思っていたアプリを作ってみました。

アプリ自体は「メモ」アプリで特に目新しいものではありません。機能も単純です。

ですが、BMEANスタックで作ったことが新しいかと思っています。 BMEANとは、

を利用するソフトウエアスタックです。

今回、「Railsより簡単にTwitterクローンを作るためのnode.jsのライブラリ」を目指したsocial-cms-backendを改良してBreezeをサポートする機能を追加しました。そのため、サーバ側のコードはほとんど書かずにアプリを作ることができます。また、HTML5のキャッシュマニフェストを使うために、connect-cache-manifestも利用しています。

アプリは、OpenShiftを使って配置しました。ぜひ、一度お試しください。

http://notesappsample-nodeangularapp.rhcloud.com/

左上にLoginがありますが、Loginしなくても使用できます。 その場合、localStorageにデータが保存されます。 LoginするとSyncが使えるようになり、サーバ側にデータを保存することができるため、複数のデバイスでデータの同期ができます。

iPhoneのWebアプリにもなります。「ホーム画面に追加」してください。スプラッシュイメージがうまく表示されないのですが、これは深追いしていません。

ソースコードはこちらです。よろしければ見てみてください。細かいところは、Breezeとsocial-cms-backendがやってくれるので、だいぶシンプルになっていると思います。

https://github.com/dai-shi/notes-app-sample

ng-conf 2014っていうイベントが1月にあったようですね。

YouTubeで動画が公開されている(でもすべてではない)ので助かります。とても勉強になりますね。ほんと、いい時代になったものです。

さて、Daniel ZenのTalkを見ました。AngularJSでモバイルアプリを作るために使えるライブラリがいっぱい紹介されていたのでメモしておきます。

PhoneGap

まず、PhoneGap。 今では、Open Sourceなんですね。

http://cordova.apache.org/

使ったことはないですが興味はあります。 一部ネイティブ関数にもアクセスできるようです。

http://en.wikipedia.org/wiki/Phonegap#Supported_platforms

APNも使えるようですね。個人的にはこれが一番大きいかな。

ngTouch

次は、ngTouch。 これは、Angularのモジュールです。タッチイベントを扱えるそうです。 一方、300ms問題だけなら、fastclickでよさそうです。

angular-mobile-nav

続いて、angular-mobile-nav。iOS5くらいのLook&Feelが簡単に作れるライブラリのようですが、READMEによるとangular-1.2だったらもっと簡単にできるということなので、今後使う機会はなさそうです。

angular-gestures

angular-gesturesは、その名の通りジェスチャーを使うためのディレクティブを提供してくれるようです。基本的には、hammer.jsのラッパーなのでしょうか。

angular-jqm

angular-jqmはjQuery Mobileのラッパーディレクティブです。jQueryなんて使わないぜ、と思っていたら、なんとこのライブラリ、jQueryとjQuery MobileのJSには非依存だそうです。つまり、jQuery MobileのCSSを使っているだけ。一気に気に入りました。そのセンスがいいです。機会があれば使ってみたいと思います。

AngularJS Native

これは、PhoneGapのネイティブ関数を呼ぶためのラッパーのようなものでしょうか。それぞれモジュールごとに分かれているようです。

angular-carousel

angular-carouselは、carouselを簡単に実現するためのライブラリです。 carouselってなんて訳すのでしょう?カルーセル? デモを見れば一発で分かると思います。

angular-snap

angular-snapは、snap.jsのラッパーディレクティブです。最近のモバイルアプリでよくある画面全体がスライドしてメニューがでるUIができるようです。snap.jsは依存ライブラリもなく、サポートブラウザも多く、設定も柔軟なようで魅力的です。

Ionic Framework

Ionic Frameworkは、PhoneGap用のフレームワークです。AngularJSも使われているとのことです。このフレームワークの範囲内でできることをやる分には簡単にできそうです。


その他、Tips等も紹介されていますので、興味がある方は直接スライドを見るとよいでしょう。

http://bit.ly/zen-ng-phonegap