1. akkun_choi

    No comment

    akkun_choi
Changes in body
Source | HTML | Preview
@@ -1,280 +1,326 @@
## 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では微妙な動作だった記憶があります)
+現時点、使えそうで使えない標準モジュールも結構あります。touchとかcookieとか。。。(ngTouchは1.2で使えるようになってるかもしれないです。自分が試した1.1では微妙な動作だった記憶があります)cookieはexpires指定ができません。
+
+
+# プロバイダーに関して (factory, service, provider)
+
+## 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
-# 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'。以下のを指定する
+- restrict: デフォルトは'A'。以下のいずれかを指定する
E: 要素名
A: 属性名
AE: 属性名、要素名
+C: クラス名
-- scope: {}を指定すると isolated scope を作成ディレクティブ内での名称 => 属性名のマッピングを行う。
-
-'=' は 属性名が同じ意味
+- `scope: {}`を指定すると isolated scope を作成し、ディレクティブ内での名称 => 属性名のマッピングを行う。
+'=' は 属性名同じで値を解釈する
'&' は 関数として実行可能
'@' は 常に文字列として解釈
+- `scope: {}`のスコープ(isolate)は、どこが隔離されているかというと、templateの内部とlink()で受け取るscope。DOM要素の子は隔離されていない(この辺りの仕組みはよく理解できいない) http://jsfiddle.net/LqzxP/2/
+
+- `scope: true`のスコープはプロトタイプ継承を行う。プリミティブな値や親で未定義の値を変更しても、親には反映されない
- template: HTMLを記述する
- templateUrl: URLを指定する
-
- link: function link(scope, element, attrs) { ... }
- transclude: trueにするとng-transcludeが使えるようになる。ディレクティブのDOM内部のデータが、テンプレート内で"ng-transclude"した場所に埋め込まれるようになる。
- controller: 実行したいコントローラーを指定できる
- require: 必須のcontrollerを指定する
- replace: テンプレートで内容を置き換える
## その他
-- 同じ名前のディレクティブで何度も定義できる
+- 同じ名前のディレクティブで何度も定義できる。その場合上書きにはならず、全て実行される。
- 未定義のディレクティブをテンプレートに記述してもエラーにはならない。
-- decoratorする場合は"〜Directive"
+- decoratorする場合は "〜Directive"
- `element.on('$destroy', ...)`または`scope.$on('$destroy', ...) `でクリーンアップするコードを書く必要がある(??)
-当然なんだけど、ng-controllerもdirectiveのひとつ。内部でcontrollerオプションを指定しているだけ。
+当然ですが、ng-controllerもdirectiveのひとつです。内部でcontrollerオプションを指定しているだけです
# スコープ
-- スコープに関して http://qiita.com/akkun_choi/items/f9db1e920069a2909602
-- `$apply`と`$digest`について http://qiita.com/akkun_choi/items/22048f31f9add7fda2c5
+スコープ階層はJavaScriptのプロトタイプ継承そのものです。
+
+別の記事にしました。
+- [スコープに関して](http://qiita.com/akkun_choi/items/f9db1e920069a2909602)
+- [`$apply`と`$digest`について](http://qiita.com/akkun_choi/items/22048f31f9add7fda2c5)
-## $scope event list
+
+## $scope.$onが受け取る特殊なイベント
- $viewContentLoaded: ng-viewによって中身の読み込みが完了した時に呼ばれます。jQueryMobileの`pageinit`的なもの。
```javascript:
$scope.$on('$viewContentLoaded', function() {
//call it here
});
```
- $includeContentLoaded: ng-includeによって中身の読み込みが完了した時。
その他のイベント
- $locationChangeStart
- $locationChangeSuccess
- $routeUpdate
- $routeChangeStart
- $routeChangeSuccess
- $routeChangeError
- $destroy
+## $emit vs $broadcast
+
+- $scope.$emit: 上位のスコープにイベントの発火を伝えます
+- $scope.$broadcast: 下位のスコープにイベントの発火を伝えます
+
+## $scope.$watch
+
+
+値が変更した時に何か実行したい時使います。オブジェクトや配列の中身が変わった時を知りたい場合は $watchCollection というのもあります。
+
# その他
+## angular.module
+
+登録したモジュールは `ng-app` で指定すると実行されます。
+
+```html:
+<html ng-app="sample">
+```
+
+`angular.module`の第二引数では依存モジュールを指定できます。実行順は依存関係の一番深いモジュールが先になります。
+
+循環した場合でも起点(ng-app)があるのでエラーは出ないです。
+
+依存モジュールを指定した場合、注入できるのは先に読んだモジュールで定義されたproviderだけのようです。
+http://jsfiddle.net/akkunchoi/2DLKc/
## 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でインジェクションの書き換えが動作しない場合があります
+ngminでインジェクションの書き換えが動作しない場合があります
- angular.moduleして後で、別の変数に変更した場合
- `$routeProvider`の`resolve`
- `$provide.decorator`の`$delegate`注入時
+## ng-attr-*
+
+IE11でtextareaにplaceholderを使うと「Error: 引数が無効です」というエラーが出ました。起きない場合もあり、動的にtextareaを生成をしているのが条件かもしれないですが、よくわかってません。
+
+https://github.com/angular/angular.js/issues/5025
+http://stackoverflow.com/questions/20647572/angularjs-v1-2-5-script-error-with-textarea-and-placeholder-attribute-using-ie11/20649762#20649762
+
+調べた所 `ng-attr-placeholder` にすると解決できました。
+はて、ng-attr-ってなんだろ?APIリファレンスにはありません。[ディレクティブの ngAttr attribute bindings](https://docs.angularjs.org/guide/directive)に書いてありました。
+ブラウザが属性値を参照して何か特別な動作をする場合には`ng-attr-`というプレフィックスを付けるとangularが解釈した後の値に変えてくれます。例えばSVGの属性を指定する時などにもこれが必要になるようです。
+
+## select の ng-options
+
+select要素の生成にng-optionsがどうも思ったように動作しない。具体的に言うとselectのvalue属性が指定できない、変な空白要素が出てくる、など。
+
+改めてAPIリファレンスを見ると[このようなNote](https://docs.angularjs.org/api/ng/directive/select)が書かれていました。
+
+> ngModelは値ではなく参照で比較します。これはオブジェクトの配列をバインドする際に重要なポイントです。[このjsfiddleを参照下さい](http://jsfiddle.net/qWzTb/)
+
+つまり、このようにして使います。
+
+``` javascript:
+$scope.genres = [
+ {label: '日本', group: 'アジア'},
+ {label: '中国', group: 'アジア'},
+ {label: 'アメリカ', group: '欧州'}
+];
+// genreに label の名称を入れるのは間違い。参照を設定する。
+$scope.genre = $scope.genres[0];
+```
+
+```html:
+<select ng-options="genre.label group by genre.group for genre in genres" ng-model="genre">
+```