1. akkun_choi

    No comment

    akkun_choi
Changes in body
Source | HTML | Preview
@@ -1,280 +1,280 @@
## angular雑感 2014-04-30
シングルページのWebアプリケーションにはとても相性が良いです。サーバー側でAPIだけ定義しておけば、`$resource`で簡単にCRUDするモデルを扱うことができ、`$routeProvider`を使えばルーティングができます。
これまでjQueryでごりごり書いてきた人も多いと思います。ただ残念ながらこれまでのjQuery資産の9割は使えないと思います。うまくプラグイン化されていれば使えなくはないです。どうしても使いたい場合は、`$apply`を呼び出したりして、angularの世界に合わせる必要があります。
学習コストは最初は低いですが、ある程度複雑なことをやろうとすると、一気に高くなります。フレームワークというのは往々にしてそういうものですね。特にDI、スコープ、ディレクティブが肝であり鬼門だと思います。
利点の1つとして、JSファイルの読み込み順を気にしなくて良いというのがあります。DIの依存性解決のおかげです。gruntでディレクトリ以下のJSファイルをそのままconcatしても、簡単に動作させることができます。
IE8以下必須の場合には使わない方が良さそうです。1.3ではサポートしないそうです。https://docs.angularjs.org/guide/ie
現時点、使えそうで使えないモジュールも結構あります。touchとかcookieとか。。。(ngTouchは1.2で使えるようになってるかもしれないです。自分が試した1.1では微妙な動作だった記憶があります)
# provider, factory, serviceに関して
## provider
http://jsfiddle.net/akkunchoi/Dcpq2/
-```
+```javascript:
var app = angular.module('sample', []);
app.provider('api', function(){
return {
$get: function(){
return 'api';
},
connect: function(){
// 処理...
}
};
});
app.provider('person', function(apiProvider){
apiProvider.connect();
return {
init: function(){
// 設定とか...
},
$get: function(){
return 'person';
}
};
});
// config: ここでは主にproviderの設定を行う
// 例えば $routeProvider
app.config(function(personProvider){
personProvider.init();
console.log(personProvider);
});
// run: $get()した結果を取得
// 例えば $route
app.run(function(person){
console.log(person);
});
```
`app.config()`には`app.provider()`か`app.constant()`で登録したものしか注入できません。プロバイダーは`Provider`を付ける必要があります。
`app.run()`には`app.constant()`や、プロバイダーが`$get`した戻り値であるインスタンスを注入できます。
## factory
http://jsfiddle.net/akkunchoi/QR7FZ/
```javascript:
var sample = angular.module('sample', []);
// core factoryを定義
sample.factory('person', function(){
return {
name: 'akkunchoi'
};
});
// 依存性注入により、上で定義したオブジェクトが入る
sample.run(function(person){
console.log(person.name); // => 'akkunchoi'
});
```
## service
http://jsfiddle.net/akkunchoi/37LGj/
```javascript:
var sample = angular.module('sample', []);
var MyUserService = (function(){
var number = 0;
MyUserService = function(){
this.number = ++number;
};
MyUserService.prototype.getNumber = function(){
return this.number;
};
return MyUserService;
})();
sample.service('person', MyUserService);
// 依存性注入により、上で定義したオブジェクトが入る
sample.run(function(person){
console.log(person.getNumber()); // => 1
});
sample.run(function(person){
console.log(person.getNumber()); // => 1
});
```
MyUserServiceクラスをnewした結果が、注入するオブジェクトになります。そのため、functionやプリミティブな値を注入することはできません。
## constant, value, service, factory, providerの使い分け
- constant: プロバイダーに注入したい時や、configフェーズで使いたい場合。絶対不変な値。decoratorできない
- value: factory, serviceに注入したい時や、runフェーズで使いたい場合。値が変わる可能性がある場合。
- service: CoffeeScriptのclassで定義したものを使いたい場合
- factory: runフェーズで注入したい場合
- provider: インスタンス化する前にconfigフェーズで設定する必要がある場合
`value`も`factory`も`service`も、結局は`provider`の機能制限版のようなものなので、よくわからない場合は`factory`だけ使っていれば済むと思います。
詳細はこちら https://docs.angularjs.org/guide/providers
# angular.module
登録したモジュールは `ng-app` で指定すると実行されます。
```html:
<html ng-app="sample">
```
`angular.module`の第二引数では依存モジュールを指定できます。実行順は依存関係の一番深いモジュールが先になります。
循環した場合でも起点(ng-app)があるのでエラーは出ないです。
依存モジュールを指定した場合、注入できるのは先に読んだモジュールで定義されたproviderだけのようです。
http://jsfiddle.net/akkunchoi/2DLKc/
# directive
## 簡単な例
```html:
<div sample-code>Content</div>
```
```javascript:
app.directive('sampleCode', function(){
return function(scope, element, attrs){
console.log(scope, element, attrs);
};
});
```
- scopeは[ng.$rootScope.Scope](http://docs.angularjs.org/api/ng.$rootScope.Scope)
- elementはjQuery(またはjQuery Lite)
- attrsは [ng.$compile.directive.Attributes](http://docs.angularjs.org/api/ng.$compile.directive.Attributes)
属性名はコロン、アンダーバー、ハイフンで単語を区切りますが、 **directive定義する時はキャメルケースになることに注意!**
## ディレクティブのオプション
http://jsfiddle.net/akkunchoi/dGLUT/
- restrict: デフォルトは'A'。以下のを指定する
E: 要素名
A: 属性名
AE: 属性名、要素名
- scope: {}を指定すると isolated scope を作成。ディレクティブ内での名称 => 属性名のマッピングを行う。
'=' は 属性名が同じ意味
'&' は 関数として実行可能
'@' は 常に文字列として解釈
- template: HTMLを記述する
- templateUrl: URLを指定する
- link: function link(scope, element, attrs) { ... }
- transclude: trueにするとng-transcludeが使えるようになる。ディレクティブのDOM内部のデータが、テンプレート内で"ng-transclude"した場所に埋め込まれるようになる。
- controller: 実行したいコントローラーを指定できる
- require: 必須のcontrollerを指定する
- replace: テンプレートで内容を置き換える
## その他
- 同じ名前のディレクティブで何度も定義できる
- 未定義のディレクティブをテンプレートに記述してもエラーにはならない。
- decoratorする場合は"〜Directive"
- `element.on('$destroy', ...)`または`scope.$on('$destroy', ...) `でクリーンアップするコードを書く必要がある(??)
当然なんだけど、ng-controllerもdirectiveのひとつ。内部でcontrollerオプションを指定しているだけ。
# スコープ
- スコープに関して http://qiita.com/akkun_choi/items/f9db1e920069a2909602
- `$apply`と`$digest`について http://qiita.com/akkun_choi/items/22048f31f9add7fda2c5
## $scope event list
- $viewContentLoaded: ng-viewによって中身の読み込みが完了した時に呼ばれます。jQueryMobileの`pageinit`的なもの。
```javascript:
$scope.$on('$viewContentLoaded', function() {
//call it here
});
```
- $includeContentLoaded: ng-includeによって中身の読み込みが完了した時。
その他のイベント
- $locationChangeStart
- $locationChangeSuccess
- $routeUpdate
- $routeChangeStart
- $routeChangeSuccess
- $routeChangeError
- $destroy
# その他
## setTimeout, window, location, document
setTimeout, window, location, documentは使わないようにします。特にsetTimeoutは$timeoutにしなければ動作しない場合があります。その他はテストしやすくするためです。
- setTimeout => $timeout
- window => $window
- location => $location
- document => $document
## ng-classである条件の時だけクラスをつけたい
http://jsfiddle.net/akkunchoi/95VJR/
`flag`にbooleanが入るようして、`selected`というCSSクラスを付けたい場合
```html:
<span ng-class="{selected: flag]">項目</span>
```
こういう書き方もできる
```html:
<span ng-class="{true: 'js-selected'}[flag]">項目</span>
```
## デフォルト値
```html:
{{gallery.date || 'Various'}}
```
## ngminの注意点
ngminでインジェクションの書き換えが動作しない場合があります
- angular.moduleして後で、別の変数に変更した場合
- `$routeProvider`の`resolve`
- `$provide.decorator`の`$delegate`注入時