├── _template.html
├── .bowerrc
├── .gitignore
├── todo
├── step02
│ ├── app.js
│ ├── style.css
│ ├── article.md
│ └── index.html
├── step03
│ ├── app.js
│ ├── article.md
│ └── index.html
├── step04
│ ├── app.js
│ ├── article.md
│ ├── index.html
│ └── article.html
├── step00
│ ├── article.md
│ └── article.html
├── step05
│ ├── app.js
│ ├── article.md
│ ├── article.html
│ └── index.html
├── step08
│ ├── article.md
│ ├── article.html
│ ├── app.js
│ └── index.html
├── step06
│ ├── app.js
│ ├── article.md
│ ├── index.html
│ └── article.html
├── step01
│ ├── article.md
│ └── article.html
├── step07
│ ├── app.js
│ └── index.html
└── step09
│ ├── app.js
│ └── index.html
├── assets
├── fonts
│ ├── mplus-1p-thin.ttf
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.ttf
│ ├── glyphicons-halflings-regular.woff
│ ├── LICENSE_J
│ ├── LICENSE_E
│ ├── README_J
│ └── README_E
├── js
│ ├── location.js
│ └── learn.js
├── css
│ ├── example.css
│ └── docs.css
└── config.json
├── components
├── angular
│ ├── bower.json
│ ├── .bower.json
│ └── README.md
└── angular-route
│ ├── bower.json
│ ├── .bower.json
│ ├── README.md
│ └── angular-route.min.js
├── bower.json
├── README.md
├── package.json
├── guide
├── template
│ ├── demo1.js
│ ├── demo2.js
│ ├── article.md
│ ├── article.html
│ ├── demo2.html
│ └── demo1.html
├── controller
│ ├── script.js
│ ├── index.html
│ ├── article.md
│ └── article.html
├── scope
│ ├── demo1.html
│ ├── demo1.js
│ ├── demo2.js
│ ├── demo2.html
│ └── article.md
├── filter
│ ├── index.html
│ ├── script.js
│ ├── article.md
│ └── article.html
├── service
│ ├── script.js
│ ├── article.md
│ ├── index.html
│ └── article.html
├── directive
│ ├── index.html
│ ├── script.js
│ ├── article.md
│ └── article.html
├── terminology
│ ├── article.md
│ └── article.html
└── module
│ ├── article.md
│ └── article.html
├── account
├── step03
│ ├── app.js
│ ├── article.md
│ └── index.html
├── step07
│ ├── style.css
│ ├── app.js
│ └── article.md
├── step00
│ ├── article.md
│ └── article.html
├── step04
│ ├── article.md
│ └── article.html
├── step01
│ ├── article.md
│ └── article.html
├── step05
│ ├── app.js
│ ├── article.md
│ └── index.html
├── step08
│ ├── article.md
│ └── app.js
├── step06
│ ├── app.js
│ ├── article.md
│ └── index.html
├── step09
│ ├── app.js
│ └── article.md
└── step02
│ └── article.md
├── LICENSE
└── Gruntfile.js
/_template.html:
--------------------------------------------------------------------------------
1 | <%=content%>
2 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "components"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Thumbs.db
2 | .DS_Store
3 |
4 | node_modules
5 |
--------------------------------------------------------------------------------
/todo/step02/app.js:
--------------------------------------------------------------------------------
1 | angular.module('App', ['LocationBar']);
2 |
--------------------------------------------------------------------------------
/assets/fonts/mplus-1p-thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/8th713/LearnAngularJS/HEAD/assets/fonts/mplus-1p-thin.ttf
--------------------------------------------------------------------------------
/assets/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/8th713/LearnAngularJS/HEAD/assets/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/assets/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/8th713/LearnAngularJS/HEAD/assets/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/8th713/LearnAngularJS/HEAD/assets/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/components/angular/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular",
3 | "version": "1.2.0-rc.3",
4 | "main": "./angular.js",
5 | "dependencies": {
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "learn",
3 | "version": "0.0.1",
4 | "dependencies": {
5 | "angular": "~1.2.0",
6 | "angular-route": "~1.2.0"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/todo/step02/style.css:
--------------------------------------------------------------------------------
1 | /* example.css より一部抜粋 */
2 | .todo-item .todo-title {
3 | color: #888;
4 | }
5 |
6 | .done .todo-title {
7 | text-decoration: line-through;
8 | }
9 |
--------------------------------------------------------------------------------
/components/angular-route/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-route",
3 | "version": "1.2.0-rc.3",
4 | "main": "./angular-route.js",
5 | "dependencies": {
6 | "angular": "1.2.0-rc.3"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | すぐできる AngularJS
2 | =======================
3 |
4 | AngularJS 学習用ドキュメントです。
5 |
6 | ## ライブラリ
7 | * AngularJS
8 | * Bootstrap
9 | * highlight.js
10 |
11 | ## フォント
12 | * M+ WEB FONTS
13 |
14 | ## Author
15 | * @8th713 ([twitter](https://twitter.com/8th_713))
16 |
--------------------------------------------------------------------------------
/todo/step03/app.js:
--------------------------------------------------------------------------------
1 | angular.module('App', ['LocationBar'])
2 | .controller('MainController', ['$scope', function ($scope) {
3 | $scope.todos = [];
4 |
5 | $scope.addTodo = function () {
6 | $scope.todos.push({
7 | title: Math.random(),
8 | done: false
9 | });
10 | };
11 | }]);
12 |
--------------------------------------------------------------------------------
/assets/fonts/LICENSE_J:
--------------------------------------------------------------------------------
1 | M+ FONTS Copyright (C) 2002-2013 M+ FONTS PROJECT
2 |
3 | -
4 |
5 | LICENSE_J
6 |
7 |
8 |
9 |
10 | これらのフォントはフリー(自由な)ソフトウエアです。
11 | あらゆる改変の有無に関わらず、また商業的な利用であっても、自由にご利用、
12 | 複製、再配布することができますが、全て無保証とさせていただきます。
13 |
14 |
15 | http://mplus-fonts.sourceforge.jp/
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "learn-angular",
3 | "version": "0.0.1",
4 | "description": "learn AngualrJS",
5 | "author": "8th713",
6 | "license": "MIT",
7 | "devDependencies": {
8 | "grunt": "~0.4.1",
9 | "grunt-este-watch": "~0.1.10",
10 | "grunt-contrib-connect": "~0.5.0",
11 | "grunt-markdown": "~0.4.0"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/guide/template/demo1.js:
--------------------------------------------------------------------------------
1 | angular.module('Example', ['LocationBar'])
2 | .run(['$rootScope', function (scope) {
3 | scope.example = 'Parent';
4 |
5 | scope.example2 = 'Parent Only';
6 |
7 | }])
8 | .controller('MainCtrl', ['$scope', function (scope) {
9 | scope.example = 'Child';
10 |
11 | scope.square = function (n) {
12 | return n * n;
13 | };
14 | }]);
15 |
--------------------------------------------------------------------------------
/todo/step04/app.js:
--------------------------------------------------------------------------------
1 | angular.module('App', ['LocationBar'])
2 | .controller('MainController', ['$scope', function ($scope) {
3 | $scope.todos = [];
4 |
5 | $scope.newTitle = '';
6 |
7 | $scope.addTodo = function () {
8 | $scope.todos.push({
9 | title: $scope.newTitle,
10 | done: false
11 | });
12 |
13 | $scope.newTitle = '';
14 | };
15 | }]);
16 |
--------------------------------------------------------------------------------
/guide/template/demo2.js:
--------------------------------------------------------------------------------
1 | angular.module('Example', ['LocationBar'])
2 | .run(['$rootScope', function (scope) {
3 | scope.count = 0;
4 | scope.string = 'Hello world';
5 |
6 | scope.countUp = function () {
7 | scope.count++;
8 | };
9 |
10 | scope.delay = function () {
11 | setTimeout(function () {
12 | scope.count += 100;
13 | scope.$apply(); // 手動更新
14 | }, 2000);
15 | };
16 | }]);
17 |
--------------------------------------------------------------------------------
/account/step03/app.js:
--------------------------------------------------------------------------------
1 | angular.module('App', ['ngRoute', 'LocationBar'])
2 | .config(['$routeProvider', function ($routeProvider) {
3 | $routeProvider
4 | .when('/', {
5 | templateUrl: 'index-tmpl'
6 | })
7 | .when('/new', {
8 | templateUrl: 'new-tmpl'
9 | })
10 | .when('/sheet/:id', {
11 | templateUrl: 'sheet-tmpl'
12 | })
13 | .otherwise({
14 | redirectTo: '/'
15 | });
16 | }]);
17 |
--------------------------------------------------------------------------------
/account/step07/style.css:
--------------------------------------------------------------------------------
1 | /* example.css より一部抜粋 */
2 | .ng-invalid {
3 | border-color: #b94a48;
4 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
5 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
6 | }
7 |
8 | .ng-invalid:focus {
9 | border-color: #953b39;
10 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #d59392;
11 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #d59392;
12 | }
13 |
--------------------------------------------------------------------------------
/assets/fonts/LICENSE_E:
--------------------------------------------------------------------------------
1 | M+ FONTS Copyright (C) 2002-2013 M+ FONTS PROJECT
2 |
3 | -
4 |
5 | LICENSE_E
6 |
7 |
8 |
9 |
10 | These fonts are free software.
11 | Unlimited permission is granted to use, copy, and distribute them, with
12 | or without modification, either commercially or noncommercially.
13 | THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
14 |
15 |
16 | http://mplus-fonts.sourceforge.jp/mplus-outline-fonts/
17 |
--------------------------------------------------------------------------------
/components/angular/.bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular",
3 | "version": "1.2.0-rc.3",
4 | "main": "./angular.js",
5 | "dependencies": {},
6 | "homepage": "https://github.com/angular/bower-angular",
7 | "_release": "1.2.0-rc.3",
8 | "_resolution": {
9 | "type": "version",
10 | "tag": "v1.2.0-rc.3",
11 | "commit": "ffab5c44ee8fd4f46e13e8ebf96c19e67383bd7b"
12 | },
13 | "_source": "git://github.com/angular/bower-angular.git",
14 | "_target": "~1.2.0",
15 | "_originalSource": "angular"
16 | }
--------------------------------------------------------------------------------
/todo/step00/article.md:
--------------------------------------------------------------------------------
1 | このチュートリアルは AngularJS を使って帳票アプリをつくる過程を {[ group.articles.length - 1 ]} のステップにわけ、段階的に AngularJS の使い方を学習します。
2 |
3 | ## アプリケーションの概要
4 | 作成する ToDo アプリの概要は以下のとおりです。
5 |
6 | ### 扱うデータ
7 | * **ToDo** - 任意の要件と、完了もしくは未完了の状態を持つデータ
8 | * **ToDo リスト** - 複数のToDoを持つデータ
9 |
10 | ### アプリが行うこと
11 | * 新しい ToDo を作成しリストに追加
12 | * ToDo リストの表示
13 | * 作成済みの ToDo の要件を編集
14 | * 任意の状態の ToDo を絞り込み表示
15 | * 完了状態の ToDo を一括削除
16 | * リスト内の ToDo の状態を一括編集
17 |
18 | ### アプリが行わないこと
19 | * データの永続化
20 |
21 | 完成予定のアプリのイメージ
22 |
--------------------------------------------------------------------------------
/components/angular-route/.bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-route",
3 | "version": "1.2.0-rc.3",
4 | "main": "./angular-route.js",
5 | "dependencies": {
6 | "angular": "1.2.0-rc.3"
7 | },
8 | "homepage": "https://github.com/angular/bower-angular-route",
9 | "_release": "1.2.0-rc.3",
10 | "_resolution": {
11 | "type": "version",
12 | "tag": "v1.2.0-rc.3",
13 | "commit": "e548b5bf0b8d5113f675e0ee30cd3c4a001adb86"
14 | },
15 | "_source": "git://github.com/angular/bower-angular-route.git",
16 | "_target": "~1.2.0",
17 | "_originalSource": "angular-route"
18 | }
--------------------------------------------------------------------------------
/todo/step05/app.js:
--------------------------------------------------------------------------------
1 | angular.module('App', ['LocationBar'])
2 | .controller('MainController', ['$scope', function ($scope) {
3 | $scope.todos = [];
4 |
5 | $scope.newTitle = '';
6 |
7 | $scope.addTodo = function () {
8 | $scope.todos.push({
9 | title: $scope.newTitle,
10 | done: false
11 | });
12 |
13 | $scope.newTitle = '';
14 | };
15 |
16 | $scope.filter = {
17 | done: { done: true },
18 | remaining: { done: false }
19 | };
20 | $scope.currentFilter = null;
21 |
22 | $scope.changeFilter = function (filter) {
23 | $scope.currentFilter = filter;
24 | };
25 | }]);
26 |
--------------------------------------------------------------------------------
/guide/controller/script.js:
--------------------------------------------------------------------------------
1 | angular.module('Example', ['LocationBar'])
2 | .controller('UserController', ['$scope', function ($scope) {
3 | // users model initial value
4 | $scope.users = [
5 | {name: 'foo', email: 'bar@example.com'}
6 | ];
7 |
8 | // add a new user, and then run the reset.
9 | $scope.addUser = function () {
10 | $scope.users.push({
11 | name: $scope.name,
12 | email: $scope.email
13 | });
14 | reset();
15 | };
16 |
17 | // Initialize name and email model.
18 | function reset() {
19 | $scope.name = null;
20 | $scope.email = null;
21 | }
22 |
23 | // Not required.
24 | reset();
25 | }]);
26 |
--------------------------------------------------------------------------------
/account/step00/article.md:
--------------------------------------------------------------------------------
1 | このチュートリアルは AngularJS を使って帳票アプリをつくる過程を {[ group.articles.length - 1 ]} のステップにわけ、段階的に AngularJS の使い方を学習します。
2 |
3 | ## アプリケーションの概要
4 | 作成する帳票アプリの概要は以下のとおりです。
5 |
6 | ### 扱うデータ
7 | * **注文明細行** - 任意の商品名、単価、注文個数を持つデータ
8 | * **帳票** - 通し番号、作成日時、注文明細行のコレクションを持つデータ
9 | * **帳票リスト** - 複数の帳票をもつコレクション
10 |
11 | ### アプリが行うこと
12 | * 帳票リスト内の帳票を一覧表示する
13 | * 任意の注文明細行を持った新しい帳票を作成し帳票リストに加える
14 | * 作成済みの帳票の詳細を表示する
15 |
16 | ### アプリが行わないこと
17 | * 帳票の編集
18 | * データの永続化
19 |
20 | ### 必要なビュー
21 | * **帳票一覧ビュー** - 帳票リストを表示するビュー
22 | * **帳票作成ビュー** - 新しい帳票を作成するビュー
23 | * **帳票詳細ビュー** - 作成済みの帳票を表示するビュー
24 |
25 | 完成予定のアプリのイメージ
26 |
--------------------------------------------------------------------------------
/guide/template/article.md:
--------------------------------------------------------------------------------
1 | ## 役割
2 | ビューは以下の役割を果たすために使用されるものです。
3 |
4 | * ディレクティブを解析し DOM を構築する。
5 | * テンプレート構文(`{{ }}`)を解析し式を実行、結果を DOM に反映する。
6 | * モデルの変更を受け取り(必要なら)式を再評価します。
7 |
8 | ## 構文
9 | AngularJS のビューは `{{ }}` で括られた部分を式として評価します。
10 | 式はその要素自身の $scope のコンテキストで評価されます。
11 | 要素に $scope がない場合、親 $scope をたどり評価されます。
12 | もし $rootScope までたどってもプロパティが発見できなければ何も出力しません。
13 |
14 | テンプレート内では未定義のプロパティを参照したり未定義の関数を呼びだそうとしてもエラーを送出しません。
15 | この仕組みは `x || ''` や `y && y()` のような記述をしなくてよいという利便性をもたらします。
16 |
17 |
18 |
19 | ## オートバインディング
20 | テンプレート構文の式は $scope のプロパティが変更されるたび再評価されます。
21 | 開発者は基本的にモデルの変更を手動でビューに通知する必要はありません。
22 |
23 |
24 |
--------------------------------------------------------------------------------
/todo/step00/article.html:
--------------------------------------------------------------------------------
1 | このチュートリアルは AngularJS を使って帳票アプリをつくる過程を {[ group.articles.length - 1 ]} のステップにわけ、段階的に AngularJS の使い方を学習します。
2 | アプリケーションの概要
3 | 作成する ToDo アプリの概要は以下のとおりです。
4 | 扱うデータ
5 |
6 | ToDo - 任意の要件と、完了もしくは未完了の状態を持つデータ
7 | ToDo リスト - 複数のToDoを持つデータ
8 |
9 | アプリが行うこと
10 |
11 | 新しい ToDo を作成しリストに追加
12 | ToDo リストの表示
13 | 作成済みの ToDo の要件を編集
14 | 任意の状態の ToDo を絞り込み表示
15 | 完了状態の ToDo を一括削除
16 | リスト内の ToDo の状態を一括編集
17 |
18 | アプリが行わないこと
19 |
22 | 完成予定のアプリのイメージ
23 |
24 |
--------------------------------------------------------------------------------
/guide/scope/demo1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Hello!!
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/guide/filter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
{{ '1234567890abcdefghijk' | omit:10:'・・・・・・' }}
13 |
{{ [0,1,2,3,4,5,6,7,8,9] | odd }}
14 |
{{ [0,1,2,3,4,5,6,7,8,9] | sum }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/guide/scope/demo1.js:
--------------------------------------------------------------------------------
1 | angular.module('Example', ['LocationBar'])
2 | .directive('countup', [function () {
3 | function up(scope) {
4 | scope.count++;
5 | if (scope.count === scope.max) {
6 | scope.count = 0;
7 | scope.$emit('complate');
8 | }
9 | }
10 |
11 | return {
12 | template: 'click {{ count }} ',
13 | scope: true,
14 | link: function (scope, $el, attrs) {
15 | scope.count = 0;
16 | scope.max = +attrs.countup;
17 |
18 | $el.on('click', function () {
19 | scope.$apply(up);
20 | });
21 | }
22 | };
23 | }])
24 | .controller('Ctrl', ['$scope', function Ctrl($scope) {
25 | $scope.toggle = false;
26 |
27 | $scope.$on('complate', function () {
28 | $scope.toggle = !$scope.toggle;
29 | });
30 | }]);
31 |
--------------------------------------------------------------------------------
/guide/template/article.html:
--------------------------------------------------------------------------------
1 | 役割
2 | ビューは以下の役割を果たすために使用されるものです。
3 |
4 | ディレクティブを解析し DOM を構築する。
5 | テンプレート構文({{ }})を解析し式を実行、結果を DOM に反映する。
6 | モデルの変更を受け取り(必要なら)式を再評価します。
7 |
8 | 構文
9 | AngularJS のビューは {{ }} で括られた部分を式として評価します。
10 | 式はその要素自身の $scope のコンテキストで評価されます。
11 | 要素に $scope がない場合、親 $scope をたどり評価されます。
12 | もし $rootScope までたどってもプロパティが発見できなければ何も出力しません。
13 | テンプレート内では未定義のプロパティを参照したり未定義の関数を呼びだそうとしてもエラーを送出しません。
14 | この仕組みは x || '' や y && y() のような記述をしなくてよいという利便性をもたらします。
15 |
16 |
17 | オートバインディング
18 | テンプレート構文の式は $scope のプロパティが変更されるたび再評価されます。
19 | 開発者は基本的にモデルの変更を手動でビューに通知する必要はありません。
20 |
21 |
22 |
--------------------------------------------------------------------------------
/todo/step08/article.md:
--------------------------------------------------------------------------------
1 | 残っているビジネスロジックは全て今まで使ってきた機能だけで実装できます。
2 |
3 | ```javascript
4 | // 全て完了/未了
5 | $scope.checkAll = function () {
6 | var state = !!$scope.remainingCount; // 未了にするのか完了にするのかの判定
7 |
8 | angular.forEach($scope.todos, function (todo) {
9 | todo.done = state;
10 | });
11 | };
12 |
13 | // 完了した ToDo を全て削除
14 | $scope.removeDoneTodo = function () {
15 | $scope.todos = where($scope.todos, $scope.filter.remaining);
16 | };
17 |
18 | // 任意の ToDo を削除
19 | $scope.removeTodo = function (currentTodo) {
20 | $scope.todos = where($scope.todos, function (todo) {
21 | return currentTodo !== todo;
22 | });
23 | };
24 | ```
25 |
26 | それぞれの関数を ngClick ディレクティブでボタンと結びつければ完了です。
27 |
28 | removeTodo メソッド内で where 関数に対して関数を渡している点に注意してください。
29 | これは単純に関数の戻り値が true の要素だけを抽出する処理です。
30 |
31 |
32 |
--------------------------------------------------------------------------------
/guide/template/demo2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
{{ count }}
13 |
14 | +1
15 | 2秒後に更新されます
16 |
17 |
18 |
19 |
{{ string }}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/guide/filter/script.js:
--------------------------------------------------------------------------------
1 | angular.module('Example', ['LocationBar'])
2 | .filter('omit', [function () {
3 | var defaultLength = 100;
4 | var defaultChar = '…';
5 |
6 | return function (arg, len, char) {
7 | len = len || defaultLength;
8 |
9 | if (angular.isString(arg) && arg.length > len) {
10 | return arg.slice(0, len) + (char || defaultChar);
11 | }
12 | return arg;
13 | };
14 | }])
15 | .filter('odd', [function () {
16 | return function (arg) {
17 | if (!angular.isArray(arg)) {
18 | return arg;
19 | }
20 |
21 | return arg.filter(function (n) {
22 | return n % 2;
23 | });
24 | };
25 | }])
26 | .filter('sum', [function () {
27 | return function (arg) {
28 | if (!angular.isArray(arg)) {
29 | return arg;
30 | }
31 |
32 | return arg.reduce(function (sum, n) {
33 | n = +n;
34 | return sum + (!isNaN(n) ? n : 0);
35 | }, 0);
36 | };
37 | }]);
38 |
--------------------------------------------------------------------------------
/account/step00/article.html:
--------------------------------------------------------------------------------
1 | このチュートリアルは AngularJS を使って帳票アプリをつくる過程を {[ group.articles.length - 1 ]} のステップにわけ、段階的に AngularJS の使い方を学習します。
2 | アプリケーションの概要
3 | 作成する帳票アプリの概要は以下のとおりです。
4 | 扱うデータ
5 |
6 | 注文明細行 - 任意の商品名、単価、注文個数を持つデータ
7 | 帳票 - 通し番号、作成日時、注文明細行のコレクションを持つデータ
8 | 帳票リスト - 複数の帳票をもつコレクション
9 |
10 | アプリが行うこと
11 |
12 | 帳票リスト内の帳票を一覧表示する
13 | 任意の注文明細行を持った新しい帳票を作成し帳票リストに加える
14 | 作成済みの帳票の詳細を表示する
15 |
16 | アプリが行わないこと
17 |
18 | 帳票の編集
19 | データの永続化
20 |
21 | 必要なビュー
22 |
23 | 帳票一覧ビュー - 帳票リストを表示するビュー
24 | 帳票作成ビュー - 新しい帳票を作成するビュー
25 | 帳票詳細ビュー - 作成済みの帳票を表示するビュー
26 |
27 | 完成予定のアプリのイメージ
28 |
29 |
--------------------------------------------------------------------------------
/todo/step06/app.js:
--------------------------------------------------------------------------------
1 | angular.module('App', ['LocationBar'])
2 | .controller('MainController', ['$scope', '$filter', function ($scope, $filter) {
3 | $scope.todos = [];
4 |
5 | $scope.newTitle = '';
6 |
7 | $scope.addTodo = function () {
8 | $scope.todos.push({
9 | title: $scope.newTitle,
10 | done: false
11 | });
12 |
13 | $scope.newTitle = '';
14 | };
15 |
16 | $scope.filter = {
17 | done: { done: true },
18 | remaining: { done: false }
19 | };
20 | $scope.currentFilter = null;
21 |
22 | $scope.changeFilter = function (filter) {
23 | $scope.currentFilter = filter;
24 | };
25 |
26 | var where = $filter('filter');
27 | $scope.$watch('todos', function (todos) {
28 | var length = todos.length;
29 |
30 | $scope.allCount = length;
31 | $scope.doneCount = where(todos, $scope.filter.done).length;
32 | $scope.remainingCount = length - $scope.doneCount;
33 | }, true);
34 | }]);
35 |
--------------------------------------------------------------------------------
/todo/step01/article.md:
--------------------------------------------------------------------------------
1 | アプリの作成にあたって最低限必要なものを準備します。
2 |
3 | まず index.html と app.js を用意します。
4 | 作業はこの2つのファイルを編集して行います。
5 |
6 | まず index.html を以下のように編集します。
7 | ```html
8 |
9 |
10 |
11 |
12 | Todo アプリ
13 |
14 |
15 |
16 |
17 |
18 |
19 | ```
20 |
21 | ## フレームワークを読み込む
22 | 今回は安定版(v1.0.8)ではなく **RC 版(v1.2.0-rc.3)**を使用します。
23 | ソースマップに対応しているので圧縮版で開発しても問題はありません。
24 |
25 | 本来は古い Internet Explorer の為にもう少し色々書く必要がありますが今回は省きます。
26 | 詳しくは[公式サイト](http://docs.angularjs.org/guide/ie)を読みましょう。
27 |
28 | ## モジュールを読み込む
29 | body 要素に `ng-app="App"` を追加します。
30 | この記述で AngularJS はこのページに App モジュールをロードするようになります。
31 | しかしこの時点では App モジュールはまだ存在していないのでコンソールにエラーが出ます。
32 |
33 | app.js を編集し App モジュールを作成しましょう。
34 |
35 | ```javascript
36 | angular.module('App', []);
37 | ```
38 |
--------------------------------------------------------------------------------
/assets/js/location.js:
--------------------------------------------------------------------------------
1 | angular.module('LocationBar', [])
2 | .directive('locationBar', ['$location', function ($location) {
3 | return {
4 | restrict: 'C',
5 | replace: true,
6 | template:
7 | '',
18 | scope: {},
19 | link: function (scope, $el) {
20 | if (!window.frameElement) {
21 | $el.css('display', 'none');
22 | return;
23 | }
24 |
25 | scope.$watch(function () {
26 | return $location.absUrl();
27 | }, function (url) {
28 | scope.url = url;
29 | });
30 | }
31 | };
32 | }]);
33 |
--------------------------------------------------------------------------------
/account/step04/article.md:
--------------------------------------------------------------------------------
1 | それぞれのビューに対するコントローラを準備しましょう。
2 |
3 | ## ルートにコントローラをセットする
4 | テンプレートはまだ仮値を表示しているだけの状態です。
5 | テンプレートはモデルを参照しデータを表示しなければなりません。
6 |
7 | 各ルートにコントローラをセットし任意のルート訪問時にそのルートのみで扱うモデルを定義できるようにしましょう。
8 |
9 | ### コントローラを準備する
10 | ルート設定は3つなのでコントローラも3つ用意します。
11 |
12 | ```javascript
13 | angular.module('App', ['ngRoute'])
14 | .config([/* 省略 */])
15 | .controller('SheetListController', [function SheetListController() {/* 一覧用 */}])
16 | .controller('CreationController', [function CreationController() {/* 作成用 */}])
17 | .controller('SheetController', [function SheetController() {/* 詳細用 */}]);
18 | ```
19 |
20 | ### ルートにコントローラをセットする
21 | when メソッドに渡す設定オブジェクトを編集してルートで使用するコントローラを使用します。
22 |
23 | ```javascript
24 | $routeProvider
25 | .when('/', {
26 | templateUrl: 'index-tmpl',
27 | controller: 'SheetListController'
28 | })
29 | .when('/new', {
30 | templateUrl: 'new-tmpl',
31 | controller: 'CreationController'
32 | })
33 | .when('/sheet/:id', {
34 | templateUrl: 'sheet-tmpl',
35 | controller: 'SheetController'
36 | })
37 | ```
38 |
39 | 設定オブジェクトの controller プロパティにルートで使用するコントローラの名前をセットします。
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 8th713
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/assets/css/example.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 60px;
3 | }
4 |
5 | .location-bar {
6 | position: fixed;
7 | top: 0;
8 | left: 0;
9 | z-index: 10000;
10 | width: 100%;
11 | background-color: #fff;
12 | }
13 |
14 | .location-bar .input-group-addon,
15 | .location-bar .btn {
16 | border-radius: 0;
17 | }
18 |
19 | .location-bar .form-control {
20 | cursor: default;
21 | background-color: #fff;
22 | }
23 |
24 | .ng-invalid {
25 | border-color: #b94a48;
26 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
27 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
28 | }
29 |
30 | .ng-invalid:focus {
31 | border-color: #953b39;
32 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #d59392;
33 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #d59392;
34 | }
35 |
36 | /* ToDo app */
37 | .todo-item .todo-title {
38 | color: #888;
39 | }
40 |
41 | .done .todo-title {
42 | text-decoration: line-through;
43 | }
44 |
45 | /* Account app */
46 | .table tbody > tr > td {
47 | vertical-align: middle;
48 | }
49 |
50 | .col-md {
51 | width: 25%;
52 | }
53 |
54 | .col-sm {
55 | width: 20%;
56 | }
57 |
--------------------------------------------------------------------------------
/guide/service/script.js:
--------------------------------------------------------------------------------
1 | // サービスはこんなことに使えますという説明のためのコードです。
2 | // 悪い実装なので真似しないでください。
3 |
4 | angular.module('Example', ['LocationBar'])
5 | .value('lines', [])
6 | // `getSubtotal` is common business logic.
7 | .factory('getSubtotal', [function () {
8 | return function (line) {
9 | return line.unitPrice * line.count;
10 | };
11 | }])
12 | .controller('MainCtrl', ['$scope', 'lines', 'getSubtotal',
13 | function (scope, lines, getSubtotal) {
14 | var line = this;
15 |
16 | function reset() {
17 | line.productName = null;
18 | line.unitPrice = 0;
19 | line.count = 0;
20 | }
21 |
22 | scope.add = function () {
23 | lines.push({
24 | productName: line.productName,
25 | unitPrice: line.unitPrice,
26 | count: line.count
27 | });
28 | reset();
29 | };
30 |
31 | scope.getSubtotal = getSubtotal;
32 |
33 | reset();
34 | }])
35 | .controller('SubCtrl', ['lines', 'getSubtotal',
36 | function (lines, getSubtotal) {
37 | this.lines = lines;
38 |
39 | this.getTotalAmount = function () {
40 | return this.lines.reduce(function (totalAmount, line) {
41 | return totalAmount + getSubtotal(line);
42 | }, 0);
43 | };
44 |
45 | this.getSubtotal = getSubtotal;
46 | }]);
47 |
--------------------------------------------------------------------------------
/guide/directive/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
Click me!
13 |
14 |
15 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
16 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
17 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
18 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
19 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
20 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/assets/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "vars": {
3 | "@font-size-base": "16px",
4 | "@line-height-base": "1.6"
5 | },
6 | "css": [
7 | "print.less",
8 | "type.less",
9 | "code.less",
10 | "grid.less",
11 | "tables.less",
12 | "forms.less",
13 | "buttons.less",
14 | "glyphicons.less",
15 | "button-groups.less",
16 | "input-groups.less",
17 | "navs.less",
18 | "navbar.less",
19 | "breadcrumbs.less",
20 | "pagination.less",
21 | "pager.less",
22 | "labels.less",
23 | "badges.less",
24 | "jumbotron.less",
25 | "thumbnails.less",
26 | "alerts.less",
27 | "progress-bars.less",
28 | "media.less",
29 | "list-group.less",
30 | "panels.less",
31 | "wells.less",
32 | "close.less",
33 | "dropdowns.less",
34 | "tooltip.less",
35 | "popovers.less",
36 | "modals.less",
37 | "carousel.less",
38 | "utilities.less",
39 | "responsive-utilities.less",
40 | "component-animations.less"
41 | ],
42 | "js": [
43 | "alert.js",
44 | "button.js",
45 | "carousel.js",
46 | "dropdown.js",
47 | "modal.js",
48 | "tooltip.js",
49 | "popover.js",
50 | "tab.js",
51 | "affix.js",
52 | "collapse.js",
53 | "scrollspy.js",
54 | "transition.js"
55 | ]
56 | }
--------------------------------------------------------------------------------
/guide/filter/article.md:
--------------------------------------------------------------------------------
1 | ## 役割
2 | フィルタは以下の役割を果たすために使用されるものです。
3 |
4 | * ビューにバインドされたデータを加工し表示する。
5 |
6 | ## 使う
7 | ビューのテンプレート構文内で `| filterName` のようにして呼び出します。
8 |
9 | ```html
10 | {{ name | uppercase }}
11 | ```
12 |
13 | 変数 name は大文字に変換され出力されます。
14 | 出力が変換されるだけであり変数が書き換わるわけではありません。
15 |
16 | フィルタの中には引数を受け取るれるものも存在します。
17 | `:` に続く部分が引数です。
18 |
19 | ```html
20 | {{ 0 | date:'yyyy-MM-dd' }}
21 | ```
22 |
23 | 複数のフィルタを通すこともできます。
24 |
25 | ```html
26 | {{ [1, 2, 3, 4, 5, 6, 7, 8, 9] | filter:odd | limitTo:2 }}
27 | ```
28 |
29 |
30 | ## 作る
31 | module オブジェクトの filter メソッドでモジュールに新しいフィルタを定義できます。
32 |
33 | ```javascript
34 | module.filter('omit', [function () {
35 | var defaultLength = 100;
36 | var defaultChar = '…';
37 |
38 | return function (arg, len, char) {
39 | len = len || defaultLength;
40 |
41 | if (angular.isString(arg) && arg.length > len) {
42 | return arg.slice(0, len) + (char || defaultChar);
43 | }
44 | return arg;
45 | };
46 | }]);
47 | ```
48 |
49 | 第一引数がフィルタの名前になります。
50 | 第二引数はアノテーション配列です。
51 |
52 | アノテーション配列の最後にはファクトリ関数を含めます。
53 | ファクトリ関数が返すべきものは第一引数にフィルタリングしたい値、第二引数以降に実行時に渡された引数を受け取り新しい値を返す関数です。
54 |
55 | ファクトリ関数はフィルタがはじめて呼び出された時に一度だけ実行されます。
56 | 2回目以降のフィルタ呼び出し時にはファクトリ関数は実行されません。
57 |
58 |
59 |
--------------------------------------------------------------------------------
/guide/terminology/article.md:
--------------------------------------------------------------------------------
1 | ## モデル
2 | アプリケーションが扱うデータとビジネスロジック。
3 |
4 | AngularJS では JavaScript のオブジェクト(配列やプリミティブ値も含む)を $scope オブジェクトのプロパティに代入することでモデルを表現する。
5 | ビューへの通知は $scope オブジェクトが自動でやってくれる。
6 |
7 | ## ビュー
8 | モデルを取り出してユーザーに表示する部分。
9 |
10 | AngularJS では ngApp ディレクティブ以下の DOM がビューになる。
11 | HTML ファイルにテンプレート構文やディレクティブを書いてデータにアクセスする。
12 |
13 | ## コントローラ
14 | モデルを生成する部分。
15 |
16 | AngularJS では コントローラが初期化される時、新しい $scope オブジェクトが作成される。
17 | それを拡張してモデルを作成するのがコントローラの役目。
18 |
19 | サーバーサイド MVC のコントローラとは全くの別物。
20 |
21 | ## $scope
22 | ビューにモデルの変更を伝える機能を持ったオブジェクト。
23 | AngularJS では $scope オブジェクト拡張することでモデルを表現する。
24 |
25 | ## ディレクティブ
26 | HTML に新しい振る舞いを拡張する仕組み。
27 | プレゼンテーションロジック(DOM 操作)を担当する部分。
28 |
29 | AngularJS のコアモジュールには ngBind, ngClass, ngRepeat のような
30 | いろいろな種類のディレクティブが存在する。
31 | もちろん新たなディレクティブを自分で定義することもできる。
32 |
33 | ## フィルタ
34 | ビューにバインドされたデータモデルの表示をフォーマットする機能。
35 |
36 | ## サービス
37 | アプリケーション内で共通の機能を提供する仕組み。
38 | $http(XMLHttpRequest ラッパー)や $location(アプリケーション内のロケーションを取得、変更)など複数のコントローラやディレクティブで使用されるような機能が提供されている。
39 |
40 | 複数のコントローラで使用したいモデルの保存場所として使う場合もある。
41 |
42 | ## アノテーション配列
43 | あるコントローラやサービスが依存しているサービスの名前を列挙した配列。
44 | 配列の最後の要素には関数を含む。
45 | 基本的には module オブジェクトのメソッドに渡すもの。
46 | アノテーション配列を受け取り可能なメソッドは関数を直接受け取ることもできる。
47 | このサイト内での呼び方です。
48 |
49 | ## ファクトリ関数
50 | 何らかの生成物を返す関数。
51 | アノテーション配列の最後に含める関数はたいてい
52 | コンストラクタかファクトリ関数になる。
53 |
--------------------------------------------------------------------------------
/account/step01/article.md:
--------------------------------------------------------------------------------
1 | アプリの作成にあたって最低限必要なものを準備します。
2 |
3 | まず index.html と app.js を用意します。
4 | 作業はこの2つのファイルを編集して行います。
5 |
6 | まず index.html を以下のように編集します。
7 | ```html
8 |
9 |
10 |
11 |
12 | 帳票アプリ
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | ```
21 |
22 | ## フレームワークを読み込む
23 | 今回は安定版(v1.0.8)ではなく **RC 版(v1.2.0-rc.3)**を使用します。
24 | ソースマップに対応しているので圧縮版で開発しても問題はありません。
25 |
26 | アプリケーションのルーティングに必要な [angular-route.js](http://docs.angularjs.org/api/ngRoute) も読み込みます。
27 | ルーティング機能は v1.0.8 ではコアモジュールに実装されていました。
28 | v1.2.0 では本体から分離され独立したモジュールになったため追加で読み込みます。
29 |
30 | 本来は古い Internet Explorer の為にもう少し色々書く必要がありますが今回は省きます。
31 | 詳しくは[公式サイト](http://docs.angularjs.org/guide/ie)を読みましょう。
32 |
33 | ## モジュールを読み込む
34 | body 要素に `ng-app="App"` を追加します。
35 | この記述で AngularJS はこのページに App モジュールをロードするようになります。
36 | しかしこの時点では App モジュールはまだ存在していないのでコンソールにエラーが出ます。
37 |
38 | app.js を編集し App モジュールを作成しましょう。
39 |
40 | ```javascript
41 | angular.module('App', ['ngRoute']);
42 | ```
43 |
44 | App モジュール内でルーティング機能が使えるように [ngRoute](http://docs.angularjs.org/api/ngRoute) モジュールに依存することを明記します。
45 |
--------------------------------------------------------------------------------
/guide/terminology/article.html:
--------------------------------------------------------------------------------
1 | モデル
2 | アプリケーションが扱うデータとビジネスロジック。
3 | AngularJS では JavaScript のオブジェクト(配列やプリミティブ値も含む)を $scope オブジェクトのプロパティに代入することでモデルを表現する。
4 | ビューへの通知は $scope オブジェクトが自動でやってくれる。
5 | ビュー
6 | モデルを取り出してユーザーに表示する部分。
7 | AngularJS では ngApp ディレクティブ以下の DOM がビューになる。
8 | HTML ファイルにテンプレート構文やディレクティブを書いてデータにアクセスする。
9 | コントローラ
10 | モデルを生成する部分。
11 | AngularJS では コントローラが初期化される時、新しい $scope オブジェクトが作成される。
12 | それを拡張してモデルを作成するのがコントローラの役目。
13 | サーバーサイド MVC のコントローラとは全くの別物。
14 | $scope
15 | ビューにモデルの変更を伝える機能を持ったオブジェクト。
16 | AngularJS では $scope オブジェクト拡張することでモデルを表現する。
17 | ディレクティブ
18 | HTML に新しい振る舞いを拡張する仕組み。
19 | プレゼンテーションロジック(DOM 操作)を担当する部分。
20 | AngularJS のコアモジュールには ngBind, ngClass, ngRepeat のような
21 | いろいろな種類のディレクティブが存在する。
22 | もちろん新たなディレクティブを自分で定義することもできる。
23 | フィルタ
24 | ビューにバインドされたデータモデルの表示をフォーマットする機能。
25 | サービス
26 | アプリケーション内で共通の機能を提供する仕組み。
27 | $http(XMLHttpRequest ラッパー)や $location(アプリケーション内のロケーションを取得、変更)など複数のコントローラやディレクティブで使用されるような機能が提供されている。
28 | 複数のコントローラで使用したいモデルの保存場所として使う場合もある。
29 | アノテーション配列
30 | あるコントローラやサービスが依存しているサービスの名前を列挙した配列。
31 | 配列の最後の要素には関数を含む。
32 | 基本的には module オブジェクトのメソッドに渡すもの。
33 | アノテーション配列を受け取り可能なメソッドは関数を直接受け取ることもできる。
34 | このサイト内での呼び方です。
35 | ファクトリ関数
36 | 何らかの生成物を返す関数。
37 | アノテーション配列の最後に含める関数はたいてい
38 | コンストラクタかファクトリ関数になる。
39 |
40 |
--------------------------------------------------------------------------------
/todo/step08/article.html:
--------------------------------------------------------------------------------
1 | 残っているビジネスロジックは全て今まで使ってきた機能だけで実装できます。
2 |
3 | $scope.checkAll = function () {
4 | var state = !!$scope.remainingCount;
5 |
6 | angular.forEach($scope.todos, function (todo) {
7 | todo.done = state;
8 | });
9 | };
10 |
11 |
12 | $scope.removeDoneTodo = function () {
13 | $scope.todos = where($scope.todos, $scope.filter.remaining);
14 | };
15 |
16 |
17 | $scope.removeTodo = function (currentTodo) {
18 | $scope.todos = where($scope.todos, function (todo) {
19 | return currentTodo !== todo;
20 | });
21 | };
22 | それぞれの関数を ngClick ディレクティブでボタンと結びつければ完了です。
23 | removeTodo メソッド内で where 関数に対して関数を渡している点に注意してください。
24 | これは単純に関数の戻り値が true の要素だけを抽出する処理です。
25 |
26 |
27 |
--------------------------------------------------------------------------------
/todo/step07/app.js:
--------------------------------------------------------------------------------
1 | angular.module('App', ['LocationBar'])
2 | .controller('MainController', ['$scope', '$filter', function ($scope, $filter) {
3 | $scope.todos = [];
4 |
5 | $scope.newTitle = '';
6 |
7 | $scope.addTodo = function () {
8 | $scope.todos.push({
9 | title: $scope.newTitle,
10 | done: false
11 | });
12 |
13 | $scope.newTitle = '';
14 | };
15 |
16 | $scope.filter = {
17 | done: { done: true },
18 | remaining: { done: false }
19 | };
20 | $scope.currentFilter = null;
21 |
22 | $scope.changeFilter = function (filter) {
23 | $scope.currentFilter = filter;
24 | };
25 |
26 | var where = $filter('filter');
27 | $scope.$watch('todos', function (todos) {
28 | var length = todos.length;
29 |
30 | $scope.allCount = length;
31 | $scope.doneCount = where(todos, $scope.filter.done).length;
32 | $scope.remainingCount = length - $scope.doneCount;
33 | }, true);
34 |
35 | var originalTitle;
36 | $scope.editing = null;
37 |
38 | $scope.editTodo = function (todo) {
39 | originalTitle = todo.title;
40 | $scope.editing = todo;
41 | };
42 |
43 | $scope.doneEdit = function (todoForm) {
44 | if (todoForm.$invalid) {
45 | $scope.editing.title = originalTitle;
46 | }
47 | $scope.editing = originalTitle = null;
48 | };
49 | }])
50 | .directive('mySelect', [function () {
51 | return function (scope, $el, attrs) {
52 | scope.$watch(attrs.mySelect, function (val) {
53 | if (val) {
54 | $el[0].select();
55 | }
56 | });
57 | };
58 | }]);
59 |
--------------------------------------------------------------------------------
/guide/directive/script.js:
--------------------------------------------------------------------------------
1 | angular.module('Example', ['LocationBar'])
2 | .directive('myDialog', [function () {
3 | return function (scope, $el, attrs) {
4 | $el.on('click', function () {
5 | window.alert('Hello ' + attrs.myDialog);
6 | });
7 | };
8 | }])
9 | .directive('myDetails', [function () {
10 | return {
11 | template: '' +
12 | '
' +
17 | '
' +
18 | '
',
19 | transclude: true,
20 | scope: {
21 | title: '@myDetails'
22 | },
23 | link: function (scope) {
24 | function open() {
25 | scope.hidden = false;
26 | scope.icon = 'glyphicon-chevron-down';
27 | scope.borderWidth = {
28 | 'border-bottom-width' : 1
29 | };
30 | }
31 |
32 | function close() {
33 | scope.hidden = true;
34 | scope.icon = 'glyphicon-chevron-right';
35 | scope.borderWidth = {
36 | 'border-bottom-width' : 0
37 | };
38 | }
39 |
40 | open();
41 | scope.toggle = function () {
42 | if (scope.hidden) {
43 | open();
44 | } else {
45 | close();
46 | }
47 | };
48 | }
49 | };
50 | }]);
51 |
--------------------------------------------------------------------------------
/todo/step05/article.md:
--------------------------------------------------------------------------------
1 | フィルタを使用しリストの表示を絞り込みましょう。
2 |
3 | ## 表示を加工する
4 | ToDo モデルの状態が変更可能になったので、指定した状態の ToDo だけを表示できるようにしましょう。
5 |
6 | データを変更せずに表示だけを加工する必要があるので li 要素に対してコアモジュールの [filter フィルタ](http://docs.angularjs.org/api/ng.filter:filter)を使用します。
7 |
8 | ```html
9 |
12 | ```
13 |
14 | filter フィルタはフィルタリング条件となる引数を受け取り、合致する要素のみの配列を返してくれます。
15 | フィルタリング条件引数は currentFilter モデルに代入することにしました。
16 | コントローラ内でフィルタリング条件を格納する $scope.filter モデルとフィルタリングの現在の状態を表す currentFilter モデル、フィルタリングの状態を変更する changeFilter メソッドを作成しましょう。
17 |
18 | ```javascript
19 | // フィルタリング条件モデル
20 | $scope.filter = {
21 | done: { done: true }, // 完了のみ
22 | remaining: { done: false } // 未了のみ
23 | };
24 |
25 | // 現在のフィルタの状態モデル
26 | $scope.currentFilter = null;
27 |
28 | // フィルタリング条件を変更するメソッド
29 | $scope.changeFilter = function (filter) {
30 | $scope.currentFilter = filter;
31 | };
32 | ```
33 |
34 | 初期状態では絞り込みはしないので currentFilter の初期値は null にします。
35 |
36 | 続いてボタンクリックでフィルタリングが実行できるように html を編集しましょう。
37 |
38 | ```html
39 | 全部 {{ todos.length }}
41 | 未了 n
43 | 完了 n
45 | ```
46 |
47 | ngClick ディレクティブを使い使用したいフィルタリング条件モデルを指定して changeFilter を実行するようにします。
48 | またボタンはクリックされた時 `.active` になる必要があります。
49 | ngClass ディレクティブで currentFilter とフィルタリング条件モデルを比較するようにしましょう。
50 |
51 |
52 |
--------------------------------------------------------------------------------
/guide/controller/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
35 |
36 |
37 | {{ user.name }} - {{ user.email }}
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/account/step05/app.js:
--------------------------------------------------------------------------------
1 | angular.module('App', ['ngRoute', 'LocationBar'])
2 | .config(['$routeProvider', function ($routeProvider) {
3 | $routeProvider
4 | .when('/', {
5 | templateUrl: 'index-tmpl',
6 | controller: 'SheetListController'
7 | })
8 | .when('/new', {
9 | templateUrl: 'new-tmpl',
10 | controller: 'CreationController'
11 | })
12 | .when('/sheet/:id', {
13 | templateUrl: 'sheet-tmpl',
14 | controller: 'SheetController'
15 | })
16 | .otherwise({
17 | redirectTo: '/'
18 | });
19 | }])
20 | .controller('SheetListController', [function SheetListController() {/* 一覧用 */}])
21 | .controller('CreationController', ['$scope', function CreationController($scope) {
22 | function createOrderLine() {
23 | return {
24 | productName: '',
25 | unitPrice: 0,
26 | count: 0
27 | };
28 | }
29 |
30 | $scope.initialize = function () {
31 | $scope.lines = [createOrderLine()];
32 | };
33 |
34 | $scope.addLine = function () {
35 | $scope.lines.push(createOrderLine());
36 | };
37 |
38 | $scope.removeLine = function (target) {
39 | var lines = $scope.lines;
40 | var index = lines.indexOf(target);
41 |
42 | if (index !== -1) {
43 | lines.splice(index, 1);
44 | }
45 | };
46 |
47 | $scope.save = function () {};
48 |
49 | $scope.getSubtotal = function (orderLine) {
50 | return orderLine.unitPrice * orderLine.count;
51 | };
52 |
53 | $scope.getTotalAmount = function (lines) {
54 | var totalAmount = 0;
55 |
56 | angular.forEach(lines, function (orderLine) {
57 | totalAmount += $scope.getSubtotal(orderLine);
58 | });
59 |
60 | return totalAmount;
61 | };
62 |
63 | $scope.initialize();
64 | }])
65 | .controller('SheetController', [function SheetController() {/* 詳細用 */}]);
66 |
--------------------------------------------------------------------------------
/components/angular/README.md:
--------------------------------------------------------------------------------
1 | # bower-angular
2 |
3 | This repo is for distribution on `bower`. The source for this module is in the
4 | [main AngularJS repo](https://github.com/angular/angular.js).
5 | Please file issues and pull requests against that repo.
6 |
7 | ## Install
8 |
9 | Install with `bower`:
10 |
11 | ```shell
12 | bower install angular
13 | ```
14 |
15 | Add a `
19 | ```
20 |
21 | ## Documentation
22 |
23 | Documentation is available on the
24 | [AngularJS docs site](http://docs.angularjs.org/).
25 |
26 | ## License
27 |
28 | The MIT License
29 |
30 | Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
31 |
32 | Permission is hereby granted, free of charge, to any person obtaining a copy
33 | of this software and associated documentation files (the "Software"), to deal
34 | in the Software without restriction, including without limitation the rights
35 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
36 | copies of the Software, and to permit persons to whom the Software is
37 | furnished to do so, subject to the following conditions:
38 |
39 | The above copyright notice and this permission notice shall be included in
40 | all copies or substantial portions of the Software.
41 |
42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
45 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
47 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
48 | THE SOFTWARE.
49 |
--------------------------------------------------------------------------------
/guide/template/demo1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 式評価
16 | 1 + 2 = {{ 1 + 2 }}
17 |
18 |
19 | 変数の参照
20 | example = {{ example }}
21 |
22 |
23 | 属性値内での式評価
24 | example
25 |
26 |
27 |
28 |
29 | 変数の参照(MainCtrl)
30 | example = {{ example }}
31 |
32 |
33 | 継承された変数の参照
34 | example2 = {{ example2 }}
35 |
36 |
37 | メソッド呼び出し
38 | square(10) = {{ square(10) }}
39 |
40 |
41 |
42 |
43 | 未定義変数の参照
44 | x = {{ x }}
45 |
46 |
47 | 未定義関数の呼び出し
48 | y() = {{ y() }}
49 |
50 |
51 | 未定義変数の関数の呼び出し
52 | x.y() = {{ x.y() }}
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/guide/scope/demo2.js:
--------------------------------------------------------------------------------
1 | angular.module('Example', ['LocationBar'])
2 | .value('random', function (n) {
3 | n = n || 9;
4 | return Math.round(Math.random() * n);
5 | })
6 | .controller('Ctrl1', ['$scope', 'random', function (scope, random) {
7 | scope.value = 0;
8 | scope.$watch('value', function (val, old) {
9 | scope.square = val * val;
10 | scope.old = old;
11 | });
12 | scope.change = function () {
13 | scope.value = random();
14 | };
15 | }])
16 | .controller('Ctrl2', ['$scope', 'random', function (scope, random) {
17 | var n = 0;
18 | scope.$watch(function() { return n; }, function (val, old) {
19 | scope.value = val;
20 | scope.square = val * val;
21 | scope.old = old;
22 | });
23 | scope.change = function () {
24 | n = random();
25 | };
26 | }])
27 | .controller('Ctrl3', ['$scope', 'random', function (scope, random) {
28 | scope.original = {};
29 | scope.$watch('original', function (val) {
30 | scope.clone1 = angular.copy(val);
31 | });
32 | scope.$watch('original', function (val) {
33 | scope.clone2 = angular.copy(val);
34 | }, true);
35 | scope.$watchCollection('original', function (val) {
36 | scope.clone3 = angular.copy(val);
37 | });
38 | scope.change = function () {
39 | var num = random();
40 |
41 | scope.original[random(2)] = num * num;
42 | };
43 | }])
44 | .controller('Ctrl4', ['$scope', 'random', function (scope, random) {
45 | scope.original = [
46 | {value: random()}
47 | ];
48 | scope.$watch('original', function (val) {
49 | scope.clone1 = angular.copy(val);
50 | });
51 | scope.$watch('original', function (val) {
52 | scope.clone2 = angular.copy(val);
53 | }, true);
54 | scope.$watchCollection('original', function (val) {
55 | scope.clone3 = angular.copy(val);
56 | });
57 | scope.change = function () {
58 | scope.original[0].value = random();
59 | };
60 | }]);
61 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(grunt) {
4 | grunt.initConfig({
5 | markdown: {
6 | article: {
7 | files: [
8 | {
9 | expand: true,
10 | src: [
11 | 'guide/**/*.md',
12 | 'account/**/*.md',
13 | 'todo/**/*.md'
14 | ],
15 | dest: './',
16 | ext: '.html'
17 | }
18 | ],
19 | options: {
20 | template: '_template.html',
21 | markdownOptions: {
22 | sanitize: false,
23 | highlight: 'manual'
24 | }
25 | }
26 | }
27 | },
28 | esteWatch: {
29 | options: {
30 | dirs: [
31 | './',
32 | 'assets/**/',
33 | 'guide/**/',
34 | 'account/**/',
35 | 'todo/**/',
36 | '!components/**/',
37 | '!node_modules/**/',
38 | ],
39 | ignoredFiles: [
40 | 'README.md'
41 | ],
42 | livereload: {
43 | enabled: true,
44 | port: 35729,
45 | extensions: ['js', 'css', 'html']
46 | }
47 | },
48 | md: function (filepath) {
49 | var files = [{
50 | expand: true,
51 | src: filepath,
52 | ext: '.html'
53 | }];
54 | grunt.config.set('markdown.article.files', files);
55 | return 'markdown:article';
56 | }
57 | },
58 | connect: {
59 | app: {
60 | options: {
61 | port: 9000,
62 | livereload: true,
63 | open: 'http://localhost:9000/'
64 | }
65 | }
66 | }
67 | });
68 |
69 | grunt.loadNpmTasks('grunt-markdown');
70 | grunt.loadNpmTasks('grunt-contrib-connect');
71 | grunt.loadNpmTasks('grunt-este-watch');
72 |
73 | grunt.registerTask('default', ['connect', 'esteWatch']);
74 | };
75 |
--------------------------------------------------------------------------------
/account/step08/article.md:
--------------------------------------------------------------------------------
1 | パスパラメータから任意の帳票を選択し帳票の詳細を表示しましょう。
2 |
3 | ## パス内の変数を取得する
4 | 帳票詳細ページのパスは取得したい帳票の id を持っています。
5 | パスのパラメータは [$routeParams サービス](http://docs.angularjs.org/api/ngRoute.$routeParams) が保持しています。
6 |
7 | ルートの設定で `/sheet/:id` と設定したので条件に一致したパスに訪問した時 $routeParams サービスは `{id: 'パスの :id 部分の文字列'}` を保持しています。
8 |
9 | これを利用して sheets サービスの get メソッドを実行し任意の帳票を取得しましょう。
10 |
11 | ```javascript
12 | .controller('SheetController', ['$scope', '$routeParams', 'sheets',
13 | function SheetController($scope, $params, sheets) {
14 | var sheet = sheets.get($params.id); // 帳票を取得
15 |
16 | angular.extend($scope, sheet); // $scope オブジェクトを sheet で拡張
17 |
18 | $scope.getSubtotal = function (orderLine) {/* 省略 */};
19 |
20 | $scope.getTotalAmount = function (lines) {/* 省略 */};
21 | }]);
22 | ```
23 |
24 | 詳細ページにも小計と合計金額を表示する必要があるので各ビジネスロジックも準備します。
25 |
26 | モデルの準備が完了したらテンプレートを編集します。
27 |
28 | ```html
29 |
30 |
帳票詳細 #{{ id }}
31 |
作成日時: {{ createdAt | date:'yyyy/MM/dd HH:mm' }}
32 |
33 |
34 |
35 | 商品名
36 | 単価
37 | 個数
38 | 小計
39 |
40 |
41 |
42 |
43 | {{ orderLine.productName }}
44 | {{ orderLine.unitPrice | number }}
45 | {{ orderLine.count | number }}
46 | {{ getSubtotal(orderLine) | number }}
47 |
48 |
49 |
50 |
51 | 合計:
52 | {{ getTotalAmount(lines) | number }}
53 |
54 |
55 |
56 |
57 |
60 | ```
61 |
62 | ngShow/ngHide ディレクティブを使用して帳票が正常に取得できなかった時用の表示も用意しましょう。
63 |
64 |
65 |
--------------------------------------------------------------------------------
/account/step04/article.html:
--------------------------------------------------------------------------------
1 | それぞれのビューに対するコントローラを準備しましょう。
2 | ルートにコントローラをセットする
3 | テンプレートはまだ仮値を表示しているだけの状態です。
4 | テンプレートはモデルを参照しデータを表示しなければなりません。
5 | 各ルートにコントローラをセットし任意のルート訪問時にそのルートのみで扱うモデルを定義できるようにしましょう。
6 | コントローラを準備する
7 | ルート設定は3つなのでコントローラも3つ用意します。
8 | angular.module('App' , ['ngRoute' ])
9 | .config([])
10 | .controller('SheetListController' , [function SheetListController () { }])
11 | .controller('CreationController' , [function CreationController () { }])
12 | .controller('SheetController' , [function SheetController () { }]);
13 | ルートにコントローラをセットする
14 | when メソッドに渡す設定オブジェクトを編集してルートで使用するコントローラを使用します。
15 | $routeProvider
16 | .when('/' , {
17 | templateUrl: 'index-tmpl' ,
18 | controller: 'SheetListController'
19 | })
20 | .when('/new' , {
21 | templateUrl: 'new-tmpl' ,
22 | controller: 'CreationController'
23 | })
24 | .when('/sheet/:id' , {
25 | templateUrl: 'sheet-tmpl' ,
26 | controller: 'SheetController'
27 | })
28 | 設定オブジェクトの controller プロパティにルートで使用するコントローラの名前をセットします。
29 |
30 |
--------------------------------------------------------------------------------
/components/angular-route/README.md:
--------------------------------------------------------------------------------
1 | # bower-angular-route
2 |
3 | This repo is for distribution on `bower`. The source for this module is in the
4 | [main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngRoute).
5 | Please file issues and pull requests against that repo.
6 |
7 | ## Install
8 |
9 | Install with `bower`:
10 |
11 | ```shell
12 | bower install angular-route
13 | ```
14 |
15 | Add a `
19 | ```
20 |
21 | And add `ngRoute` as a dependency for your app:
22 |
23 | ```javascript
24 | angular.module('myApp', ['ngRoute']);
25 | ```
26 |
27 | ## Documentation
28 |
29 | Documentation is available on the
30 | [AngularJS docs site](http://docs.angularjs.org/api/ngRoute).
31 |
32 | ## License
33 |
34 | The MIT License
35 |
36 | Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
37 |
38 | Permission is hereby granted, free of charge, to any person obtaining a copy
39 | of this software and associated documentation files (the "Software"), to deal
40 | in the Software without restriction, including without limitation the rights
41 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
42 | copies of the Software, and to permit persons to whom the Software is
43 | furnished to do so, subject to the following conditions:
44 |
45 | The above copyright notice and this permission notice shall be included in
46 | all copies or substantial portions of the Software.
47 |
48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
54 | THE SOFTWARE.
55 |
--------------------------------------------------------------------------------
/guide/controller/article.md:
--------------------------------------------------------------------------------
1 | ## 役割
2 | AngularJS のコントローラは以下の役割を果たすために使用されるものです。
3 |
4 | * $scope オブジェクトにプロパティを追加しモデルを作成する。
5 | * $scope オブジェクトにメソッドを追加しビューから参照できるようにする。
6 |
7 | コントローラはビューに一切依存せずにモデルの操作のみに集中する場所です。
8 |
9 | ## 作る
10 | module インスタンスの controller メソッドでモジュールに新しいコントローラを定義できます。
11 |
12 | ```javascript
13 | module.controller('UserController', ['$scope', function UserController($scope) {
14 | // users model initial value
15 | $scope.users = [
16 | {name: 'foo', email: 'bar@example.com'}
17 | ];
18 |
19 | // submit event listener
20 | $scope.addUser = function () {
21 | $scope.users.push({
22 | name: $scope.name,
23 | email: $scope.email
24 | });
25 | reset();
26 | };
27 |
28 | // reset name and email model
29 | function reset() {
30 | $scope.name = null;
31 | $scope.email = null;
32 | }
33 |
34 | // initialize name and email model
35 | reset();
36 | }]);
37 | ```
38 |
39 | 第一引数がコントローラの名前になります。
40 | 第二引数はアノテーション配列です。
41 |
42 | アノテーション配列の最後にはコンストラクタを含めます。
43 |
44 | AngularJS ではグローバルスコープに定義した関数も暗黙的にコントローラとして扱います。
45 | ただしこの方法は推奨されていません。
46 | 必ず module.controller メソッドを使うようにしてください。
47 |
48 | ## 使う
49 | `ng-controller` ディレクティブを使用することでコントローラとビューを関連付けます。
50 | 属性値にコントローラの名前を指定することでディレクティブが解決される時そのコントローラはインスタンス化されます。
51 |
52 | ```html
53 |
54 |
59 |
60 | {{ user.name }} - {{ user.email }}
61 |
62 |
63 | ```
64 |
65 |
66 | **Tip:**
67 | 他にも `$route` サービスから初期化する方法もあります。
68 |
69 |
70 |
71 | **Tip:**
72 | v1.2.0 から `as propertyName` 構文がサポートされています。
73 | `as` の後に続くキーワードを $scope のプロパティとして作成しコンストラクタの `this` を代入します。
74 | この構文によりビューからコントローラのコンテキストに速やかにアクセスできます。
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/todo/step08/app.js:
--------------------------------------------------------------------------------
1 | angular.module('App', ['LocationBar'])
2 | .controller('MainController', ['$scope', '$filter', function ($scope, $filter) {
3 | $scope.todos = [];
4 |
5 | $scope.newTitle = '';
6 |
7 | $scope.addTodo = function () {
8 | $scope.todos.push({
9 | title: $scope.newTitle,
10 | done: false
11 | });
12 |
13 | $scope.newTitle = '';
14 | };
15 |
16 | $scope.filter = {
17 | done: { done: true },
18 | remaining: { done: false }
19 | };
20 | $scope.currentFilter = null;
21 |
22 | $scope.changeFilter = function (filter) {
23 | $scope.currentFilter = filter;
24 | };
25 |
26 | var where = $filter('filter');
27 | $scope.$watch('todos', function (todos) {
28 | var length = todos.length;
29 |
30 | $scope.allCount = length;
31 | $scope.doneCount = where(todos, $scope.filter.done).length;
32 | $scope.remainingCount = length - $scope.doneCount;
33 | }, true);
34 |
35 | var originalTitle;
36 | $scope.editing = null;
37 |
38 | $scope.editTodo = function (todo) {
39 | originalTitle = todo.title;
40 | $scope.editing = todo;
41 | };
42 |
43 | $scope.doneEdit = function (todoForm) {
44 | if (todoForm.$invalid) {
45 | $scope.editing.title = originalTitle;
46 | }
47 | $scope.editing = originalTitle = null;
48 | };
49 |
50 | $scope.checkAll = function () {
51 | var state = !!$scope.remainingCount;
52 |
53 | angular.forEach($scope.todos, function (todo) {
54 | todo.done = state;
55 | });
56 | };
57 |
58 | $scope.removeDoneTodo = function () {
59 | $scope.todos = where($scope.todos, $scope.filter.remaining);
60 | };
61 |
62 | $scope.removeTodo = function (currentTodo) {
63 | $scope.todos = where($scope.todos, function (todo) {
64 | return currentTodo !== todo;
65 | });
66 | };
67 | }])
68 | .directive('mySelect', [function () {
69 | return function (scope, $el, attrs) {
70 | scope.$watch(attrs.mySelect, function (val) {
71 | if (val) {
72 | $el[0].select();
73 | }
74 | });
75 | };
76 | }]);
77 |
--------------------------------------------------------------------------------
/todo/step02/article.md:
--------------------------------------------------------------------------------
1 | ユーザインタフェースのモックアップを作成しましょう。
2 |
3 | ## 新しい ToDo を作成するフォーム
4 | まずは新しい ToDo を作成するためのフォームが必要です。
5 |
6 | ```html
7 |
11 | ```
12 |
13 | 要件を入力するテキストフィールドと追加を実行するボタンです。
14 | input 要素には required 属性を付けて要件が空の時はフォームを送信できないようにしておきましょう。
15 |
16 |
17 | ## ボタン類
18 | アプリケーションの機能を実行するためのボタンも必要ですね。
19 |
20 | ```html
21 |
22 | 全て完了/未了
23 | 全部 3
24 | 未了 2
25 | 完了 1
26 | 完了アイテムを全て削除
27 |
28 | ```
29 |
30 | 2 ~ 4 個目のボタンは ToDo の絞り込みボタンです。
31 | 選択中のボタンは active クラスがつくことにしましょう。
32 | span 要素内にはそれぞれの件数を表示しましょう。
33 |
34 | ## ToDo リスト
35 | ToDo を表示するリストも当然必要です。
36 | 未完了の ToDo、完了した ToDo、編集中の ToDo と3種類の状態のモックアップを作成しましょう。
37 |
38 | ### 未完了の ToDo
39 | ```html
40 |
41 |
46 |
47 | ```
48 |
49 | 状態を示すチェックボックスと削除ボタンを持っています。
50 |
51 | 削除ボタンの type 属性は reset にしておきます。
52 | 用途にあっていませんが他のタイプにしてしまうと submit イベントが発生してしまうのでやむを得ない処置です。
53 |
54 | ### 完了した ToDo
55 | ```html
56 |
57 |
62 |
63 | ```
64 |
65 | 完了した ToDo は `.todo-title` に打ち消し線を引きたいので li 要素に done クラスをつけることにしましょう。
66 |
67 | ### 編集中の ToDo
68 | ```html
69 |
70 |
74 |
75 | ```
76 |
77 | 編集中の ToDo は editing クラスがつくことにします。
78 | span 要素と button 要素が削除され input 要素が追加されます。
79 |
80 |
81 |
82 |
83 | **Tip:**
84 | プレビューは見栄えのために
Bootstrap を使用しています。
85 | その都合上マークアップが多少違います。
86 |
87 |
--------------------------------------------------------------------------------
/assets/js/learn.js:
--------------------------------------------------------------------------------
1 | angular.module('Learn', ['ngRoute', 'Learn.directives', 'Data'])
2 | .config(['$interpolateProvider', '$routeProvider',
3 | function ($interpolateProvider, $routeProvider) {
4 | $interpolateProvider
5 | .startSymbol('{[')
6 | .endSymbol(']}');
7 |
8 | $routeProvider
9 | .when('/', {
10 | templateUrl: 'start-tmpl'
11 | })
12 | .when('/:group', {
13 | templateUrl: 'index-tmpl',
14 | controller: 'ListController'
15 | })
16 | .when('/:group/:id', {
17 | templateUrl: 'article-tmpl',
18 | controller: 'ArticleController'
19 | })
20 | .otherwise({
21 | redirectTo: '/'
22 | });
23 | }])
24 | .service('finder', ['$routeParams', 'models',
25 | function (params, models) {
26 | function find(arr, id) {
27 | var i = arr.length;
28 | var el;
29 |
30 | while (i--) {
31 | el = arr[i];
32 | if (el.id === id) {
33 | return el;
34 | }
35 | }
36 | return;
37 | }
38 |
39 | function findGroup() {
40 | return find(models, params.group);
41 | }
42 |
43 | function findArticle() {
44 | var groupName = params.group;
45 | var id = params.id;
46 | var group = findGroup();
47 |
48 | return {
49 | group: group,
50 | article: find(group.articles, params.id),
51 | url: groupName + '/' + id + '/article.html'
52 | };
53 | }
54 |
55 | this.findGroup = findGroup;
56 | this.findArticle = findArticle;
57 | }])
58 | .run(['$rootScope', '$location', 'models',
59 | function MainController(scope, $location, models) {
60 | scope.models = models;
61 | scope.currentGroup = null;
62 | scope.currentPath = $location.path();
63 |
64 | scope.$on('$routeChangeStart', function (evt, route) {
65 | scope.currentGroup = route.params.group;
66 | scope.currentPath = $location.path();
67 | });
68 | }])
69 | .controller('ListController', ['$scope', 'finder',
70 | function ListController(scope, finder) {
71 | angular.extend(scope, finder.findGroup());
72 | }])
73 | .controller('ArticleController', ['$scope', 'finder',
74 | function ArticleController(scope, finder) {
75 | angular.extend(scope, finder.findArticle());
76 | }]);
77 |
--------------------------------------------------------------------------------
/todo/step04/article.md:
--------------------------------------------------------------------------------
1 | 双方向バインディングを理解しユーザーが入力した値を受け取りましょう。
2 |
3 | ## input 要素の値を受け取る
4 | 先ほどのステップで ToDo を作成することが出来るようになりました。
5 | しかし肝心の要件が仮値のままです。
6 | 本来なら要件はフォームの input 要素から取得せねばなりません。
7 |
8 | input 要素の値を表すモデルが存在すれば addTodo メソッドはユーザーが入力した値を参照できそうです。
9 | まず input 要素の値を表すモデルを作成します。
10 |
11 | ```javascript
12 | $scope.newTitle = '';
13 | ```
14 |
15 | input 要素ははじめ値を持っている必要がないので初期値は空文字列です。
16 | newTitle モデルは input 要素の値と結びついてユーザーの入力に合わせて値を更新する必要があります。
17 | [ngModel ディレクティブ](http://docs.angularjs.org/api/ng.directive:ngModel)を使用してモデルと要素を結びつけましょう。
18 |
19 | ```html
20 |
25 | ```
26 |
27 | input 要素に対して ngModel ディレクティブの使用を宣言します。
28 | ngModel ディレクティブは要素の値に紐付けたいモデルを受け取ります。
29 | これで input 要素の値が変化した時 newTitle モデルの値も合わせて更新されるようになります。
30 |
31 | 最後に addTodo メソッド内で newTitle モデルを参照し新しく作成する ToDo の要件に代入されるようにしましょう。
32 |
33 | ```javascript
34 | $scope.addTodo = function () {
35 | $scope.todos.push({
36 | title: $scope.newTitle,
37 | done: false
38 | });
39 |
40 | $scope.newTitle = '';
41 | };
42 | ```
43 |
44 | 新しい Todo が作成されたら input 要素の値はもう必要ないので newTitle モデルは初期値に戻しておきましょう。
45 | newTitle モデルに代入を行えば input 要素の値もそれに追従します。
46 |
47 | ついでなのでリスト内の ToDo の状態にも ngModel ディレクティブを使用して ToDo モデルの done プロパティと結びつけておきましょう。
48 |
49 | ```html
50 |
51 |
56 |
57 | ```
58 |
59 | これでリスト内の ToDo モデルもチェックボックスの状態に合わせて更新されます。
60 |
61 | 完了状態の ToDo には done クラスがつくという仕様なので todo.done プロパティの値に合わせてクラスが付与されるようにしましょう。
62 |
63 | ```html
64 |
66 | ```
67 |
68 | li 要素に対して [ngClass ディレクティブ](http://docs.angularjs.org/api/ng.directive:ngClass)の使用を宣言します。
69 | ngClass ディレクティブは様々な形式の値を受けとれますが今回はマップオブジェクトを使用します。
70 | マップオブジェクトは `{'クラス名': クラスを付与する条件式}` の形式です。
71 | 上記の場合 todo.done プロパティが true なら done クラスが付与されるという意味になります。
72 |
73 |
74 |
--------------------------------------------------------------------------------
/guide/directive/article.md:
--------------------------------------------------------------------------------
1 | ## 役割
2 | ディレクティブは以下の役割を果たすために使用されるものです。
3 |
4 | * 宣言的な HTML の拡張(つまり HTML への機能追加)
5 |
6 | ディレクティブは HTML に新たな振る舞いを与えたり DOM を変更したりします。
7 |
8 | ## 使う
9 | HTML 内で普通にマークアップするだけです。
10 | ディレクティブはタグや属性、クラス、コメントなどの方法で呼び出します。
11 | ただし全てのディレクティブで全ての呼び出し方が使えるわけではありません。
12 | 実際には多くのディレクティブが属性としてしか呼び出せません。
13 |
14 | ```html
15 |
16 |
17 |
18 | Click me!!
19 |
20 |
21 |
22 | ```
23 |
24 | `ng-` を含むタグ名、属性名、データ属性名、クラス名は全て AngularJS のコアモジュールが提供しているディレクティブです。
25 |
26 | 各ディレクティブがどんな働きを HTML に追加したのか軽く触れます。
27 |
28 | * ngApp
29 | * App モジュールを起動し子孫要素のテンプレートとディレクティブを解析します。
30 | * ngController
31 | * ビューに MainCtrl コントローラをアタッチし子孫要素の式のコンテキストを MainCtrl の $scope と結びつけます。
32 | * ngBind
33 | * $scope.title プロパティの値を自身の textContent に代入します。
34 | * ngClick
35 | * 自身がクリックされると $scope.say 関数が実行されます。
36 | * ngInclude
37 | * $scope.template プロパティの値の外部ファイルを読み込み自身の innerHTML に代入します。
38 |
39 | HTML バリデータをパスするようにしたい場合 `data-` 接頭辞を付けて汎用データ属性としてマークアップすることが可能です。
40 |
41 | ## 作る
42 | module インスタンスの directive メソッドでモジュールに新しいディレクティブを定義できます。
43 |
44 | ```javascript
45 | module.directive('myDialog', [function () {
46 | return function (scope, $el, attrs) {
47 | $el.on('click', function () {
48 | window.alert('Hello ' + attrs.myDialog);
49 | });
50 | };
51 | }]);
52 | ```
53 |
54 | 第一引数がディレクティブの名前になります。
55 | 第二引数はアノテーション配列です。
56 |
57 | アノテーション配列の最後にはファクトリ関数を含めます。
58 | ファクトリ関数が返すべきものはディレクティブの振る舞いを定義した関数(リンク関数)です。
59 |
60 | ファクトリ関数はディレクティブがはじめて呼び出された時に一度だけ実行されます。
61 | 2回目以降のディレクティブ呼び出し時にはファクトリ関数は実行されません。
62 |
63 | ディレクティブの名前はキャメルケースで指定します。
64 | HTML 内ではハイフンケースで呼び出します。
65 | 上記の例では `Click me!!
` のように呼び出します。
66 | 要素をクリックすると属性値を受け取って アラートに 「Hallo world」と表示します。
67 |
68 | 上記は最も単純化された定義法でありより高度なディレクティブを作成する場合、ファクトリ関数はディレクティブ定義オブジェクトを返すようにします。
69 |
70 | ディレクティブ定義オブジェクトについては [AngularJS: Directives](http://docs.angularjs.org/guide/directive) や [JavaScript - AngularJSのdirectiveとは - Qiita [キータ]](http://qiita.com/grapswiz@github/items/878432cb7e04039b06fb) を参考してください。
71 |
72 |
73 |
--------------------------------------------------------------------------------
/todo/step01/article.html:
--------------------------------------------------------------------------------
1 | アプリの作成にあたって最低限必要なものを準備します。
2 | まず index.html と app.js を用意します。
3 | 作業はこの2つのファイルを編集して行います。
4 | まず index.html を以下のように編集します。
5 | <!doctype html>
6 | <html lang ="ja" >
7 | <head >
8 | <meta charset ="UTF-8" >
9 | <title > Todo アプリ</title >
10 | </head >
11 | <body ng-app ="App" >
12 | <script src ="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.3/angular.min.js" > </script >
13 | <script src ="app.js" > </script >
14 | </body >
15 | </html >
16 | フレームワークを読み込む
17 | 今回は安定版(v1.0.8)ではなく RC 版(v1.2.0-rc.3) を使用します。
18 | ソースマップに対応しているので圧縮版で開発しても問題はありません。
19 | 本来は古い Internet Explorer の為にもう少し色々書く必要がありますが今回は省きます。
20 | 詳しくは公式サイト を読みましょう。
21 | モジュールを読み込む
22 | body 要素に ng-app="App" を追加します。
23 | この記述で AngularJS はこのページに App モジュールをロードするようになります。
24 | しかしこの時点では App モジュールはまだ存在していないのでコンソールにエラーが出ます。
25 | app.js を編集し App モジュールを作成しましょう。
26 | angular.module('App' , []);
27 |
28 |
--------------------------------------------------------------------------------
/guide/filter/article.html:
--------------------------------------------------------------------------------
1 | 役割
2 | フィルタは以下の役割を果たすために使用されるものです。
3 |
4 | ビューにバインドされたデータを加工し表示する。
5 |
6 | 使う
7 | ビューのテンプレート構文内で | filterName のようにして呼び出します。
8 | <p > {{ name | uppercase }} </p >
9 | 変数 name は大文字に変換され出力されます。
10 | 出力が変換されるだけであり変数が書き換わるわけではありません。
11 | フィルタの中には引数を受け取るれるものも存在します。
12 | : に続く部分が引数です。
13 | <p > {{ 0 | date:'yyyy-MM-dd' }} </p >
14 | 複数のフィルタを通すこともできます。
15 | <p > {{ [1, 2, 3, 4, 5, 6, 7, 8, 9] | filter:odd | limitTo:2 }} </p >
16 | 作る
17 | module オブジェクトの filter メソッドでモジュールに新しいフィルタを定義できます。
18 | module.filter('omit' , [function () {
19 | var defaultLength = 100 ;
20 | var defaultChar = '…' ;
21 |
22 | return function (arg, len, char) {
23 | len = len || defaultLength;
24 |
25 | if (angular.isString(arg) && arg.length > len) {
26 | return arg.slice(0 , len) + (char || defaultChar);
27 | }
28 | return arg;
29 | };
30 | }]);
31 | 第一引数がフィルタの名前になります。
32 | 第二引数はアノテーション配列です。
33 | アノテーション配列の最後にはファクトリ関数を含めます。
34 | ファクトリ関数が返すべきものは第一引数にフィルタリングしたい値、第二引数以降に実行時に渡された引数を受け取り新しい値を返す関数です。
35 | ファクトリ関数はフィルタがはじめて呼び出された時に一度だけ実行されます。
36 | 2回目以降のフィルタ呼び出し時にはファクトリ関数は実行されません。
37 |
38 |
39 |
--------------------------------------------------------------------------------
/assets/fonts/README_J:
--------------------------------------------------------------------------------
1 | M+ FONTS Copyright (C) 2002-2013 M+ FONTS PROJECT
2 |
3 | -
4 |
5 | README_J
6 |
7 |
8 |
9 |
10 | M+ TESTFLIGHT
11 |
12 | M+ OUTLINE FONTS は 2 種類のかな文字を持つ固定幅和文フォントと 4 種類の
13 | プロポーショナル欧文フォント、3 種類の半角固定幅欧文フォントの組み合わせから
14 | 構成され、それぞれ Thin から Black まで 7 種類(半角固定幅フォントは Bold
15 | まで 5 種類)のウエイトバリエーションがあります。
16 |
17 |
18 | プロポーショナルフォント(和文は全角固定幅、欧文・数字はプロポーショナル)
19 |
20 | M+ 1P mplus-1p-thin.ttf
21 | mplus-1p-light.ttf
22 | mplus-1p-regular.ttf
23 | mplus-1p-medium.ttf
24 | mplus-1p-bold.ttf
25 | mplus-1p-heavy.ttf
26 | mplus-1p-black.ttf
27 |
28 | M+ 2P mplus-2p-thin.ttf
29 | mplus-2p-light.ttf
30 | mplus-2p-regular.ttf
31 | mplus-2p-medium.ttf
32 | mplus-2p-bold.ttf
33 | mplus-2p-heavy.ttf
34 | mplus-2p-black.ttf
35 |
36 | M+ 1C mplus-1c-thin.ttf
37 | mplus-1c-light.ttf
38 | mplus-1c-regular.ttf
39 | mplus-1c-medium.ttf
40 | mplus-1c-bold.ttf
41 | mplus-1c-heavy.ttf
42 | mplus-1c-black.ttf
43 |
44 | M+ 2C mplus-2c-thin.ttf
45 | mplus-2c-light.ttf
46 | mplus-2c-regular.ttf
47 | mplus-2c-medium.ttf
48 | mplus-2c-bold.ttf
49 | mplus-2c-heavy.ttf
50 | mplus-2c-black.ttf
51 |
52 |
53 | 固定幅フォント(和文は全角、欧文・数字は半角の固定幅)
54 |
55 | M+ 1M mplus-1m-thin.ttf
56 | mplus-1m-light.ttf
57 | mplus-1m-regular.ttf
58 | mplus-1m-medium.ttf
59 | mplus-1m-bold.ttf
60 |
61 | M+ 2M mplus-2m-thin.ttf
62 | mplus-2m-light.ttf
63 | mplus-2m-regular.ttf
64 | mplus-2m-medium.ttf
65 | mplus-2m-bold.ttf
66 |
67 | M+ 1MN mplus-1mn-thin.ttf
68 | mplus-1mn-light.ttf
69 | mplus-1mn-regular.ttf
70 | mplus-1mn-medium.ttf
71 | mplus-1mn-bold.ttf
72 |
73 |
74 |
75 |
76 | かな文字と常用漢字、基本的な欧文グリフが揃い、一般的な文章の大半の文字を表示
77 | できるようになりました。不足している文字は他のフォントからの自動補完、
78 | IPAG フォントとの合成などで補うことができます。
79 | M+ OUTLINE FONTS の詳細、お問い合わせなどは以下の URL からお願いします。
80 |
81 | -
82 |
83 | M+ OUTLINE FONTS
84 | http://mplus-fonts.sourceforge.jp/mplus-outline-fonts/
85 |
86 | mplus-fonts-dev ML
87 | http://lists.sourceforge.jp/mailman/listinfo/mplus-fonts-dev
88 |
89 | M+ FONTS open forum
90 | http://sourceforge.jp/forum/forum.php?forum_id=3403
91 |
--------------------------------------------------------------------------------
/assets/fonts/README_E:
--------------------------------------------------------------------------------
1 | M+ FONTS Copyright (C) 2002-2013 M+ FONTS PROJECT
2 |
3 | -
4 |
5 | README_E
6 |
7 |
8 |
9 |
10 | M+ TESTFLIGHT
11 |
12 | The M+ OUTLINE FONTS are distributed with proportional Latin (4 variations), fixed-halfwidth Latin (3 variations) and fixed-fullwidth Japanese (2 Kana variations) character set. 7 weights from Thin to Black are included, but fixed-halfwidth Latin with 5 weights from Thin to Bold.
13 |
14 |
15 | PROPORTIONAL FONTS (proportional Latin and fixed-fullwidth Japanese)
16 |
17 | M+ 1P mplus-1p-thin.ttf
18 | mplus-1p-light.ttf
19 | mplus-1p-regular.ttf
20 | mplus-1p-medium.ttf
21 | mplus-1p-bold.ttf
22 | mplus-1p-heavy.ttf
23 | mplus-1p-black.ttf
24 |
25 | M+ 2P mplus-2p-thin.ttf
26 | mplus-2p-light.ttf
27 | mplus-2p-regular.ttf
28 | mplus-2p-medium.ttf
29 | mplus-2p-bold.ttf
30 | mplus-2p-heavy.ttf
31 | mplus-2p-black.ttf
32 |
33 | M+ 1C mplus-1c-thin.ttf
34 | mplus-1c-light.ttf
35 | mplus-1c-regular.ttf
36 | mplus-1c-medium.ttf
37 | mplus-1c-bold.ttf
38 | mplus-1c-heavy.ttf
39 | mplus-1c-black.ttf
40 |
41 | M+ 2C mplus-2c-thin.ttf
42 | mplus-2c-light.ttf
43 | mplus-2c-regular.ttf
44 | mplus-2c-medium.ttf
45 | mplus-2c-bold.ttf
46 | mplus-2c-heavy.ttf
47 | mplus-2c-black.ttf
48 |
49 |
50 | FIXED-WIDTH FONTS (fixed-halfwidth Latin and fixed-fullwidth Japanese)
51 |
52 | M+ 1M mplus-1m-thin.ttf
53 | mplus-1m-light.ttf
54 | mplus-1m-regular.ttf
55 | mplus-1m-medium.ttf
56 | mplus-1m-bold.ttf
57 |
58 | M+ 2M mplus-2m-thin.ttf
59 | mplus-2m-light.ttf
60 | mplus-2m-regular.ttf
61 | mplus-2m-medium.ttf
62 | mplus-2m-bold.ttf
63 |
64 | M+ 1MN mplus-1mn-thin.ttf
65 | mplus-1mn-light.ttf
66 | mplus-1mn-regular.ttf
67 | mplus-1mn-medium.ttf
68 | mplus-1mn-bold.ttf
69 |
70 |
71 |
72 |
73 | -
74 |
75 | M+ OUTLINE FONTS
76 | http://mplus-fonts.sourceforge.jp/mplus-outline-fonts/
77 |
78 | mplus-fonts-dev ML
79 | http://lists.sourceforge.jp/mailman/listinfo/mplus-fonts-dev
80 |
81 | M+ FONTS open forum
82 | http://sourceforge.jp/forum/forum.php?forum_id=3403
83 |
--------------------------------------------------------------------------------
/guide/service/article.md:
--------------------------------------------------------------------------------
1 | ## 役割
2 | サービスは以下の役割を果たすために使用されるものです。
3 |
4 | * アプリケーション共通のオブジェクトを定義する。
5 |
6 | コントローラやディレクティブ、フィルタなどで共通して使われる値や関数を定義する場所です。
7 |
8 | ## 使う
9 | サービスを使用したいコントローラなどのアノテーション配列にサービス名を列挙します。
10 |
11 | ```javascript
12 | module.controller('Ctrl', ['$scope', '$http', '$timeout', function (scope, $http, $timeout) {
13 | $http.get('/data').success(function (data) {
14 | scope.data = data;
15 | });
16 |
17 | $timeout(function () {
18 | scope.elapsed = true;
19 | }, 1000);
20 | }]);
21 | ```
22 |
23 | アノテーション配列の最後の関数がサービスを受け取ります。
24 |
25 | ## 作る
26 | module インスタンスの factory, service, value などのメソッドで新しいサービスを定義できます。
27 |
28 | いずれのメソッドも第一引数にサービスの名前を受け取ります。
29 | factory と service は第二引数にアノテーション配列を受け取ります。
30 |
31 | factory メソッドのアノテーション配列の最後はファクトリ関数です。
32 | ファクトリ関数が返すべきものはサービスとなるオブジェクトです。
33 | ファクトリ関数は作成したサービスがはじめて依存注入された時に一度だけ実行されます。
34 | 2回目以降の依存注入では初回で作成されたサービスが渡されます。
35 |
36 | ```javascript
37 | module.factory('delayOneSecond', ['$timeout', function ($timeout) {
38 | return function (callback) {
39 | return $timeout(callbak, 1000);
40 | };
41 | }]);
42 | ```
43 |
44 | service メソッドのアノテーション配列の最後はコンストラクタです。
45 | コンストラクタは作成したサービスがはじめて依存注入された時に一度だけインスタンス化されます。
46 | 2回目以降の依存注入では初回で作成されたインスタンスが渡されます。
47 |
48 | ```javascript
49 | module.service('storage', [function () {
50 | var storage = {};
51 |
52 | function getLange() {
53 | return Object.keys(storage).length;
54 | }
55 |
56 | this.length = 0;
57 |
58 | this.get = function (key) {
59 | return storage[key];
60 | };
61 |
62 | this.getAll = function () {
63 | return angular.copy(storage);
64 | };
65 |
66 | this.set = function (key, value) {
67 | storage[key] = value;
68 | this.length = getLange();
69 | };
70 |
71 | this.remove = function (key) {
72 | delete storage[key];
73 | this.length = getLange();
74 | };
75 | }]);
76 | ```
77 |
78 | value メソッドの第二引数はサービスそのものを受け取ります。
79 |
80 | ```javascript
81 | module.value('data', [
82 | {id: 1, name: 'Alice'},
83 | {id: 2, name: 'Bob'},
84 | {id: 3, name: 'Charlie'}
85 | ]);
86 | ```
87 |
88 |
89 | **Tip:**
90 | config ブロックで設定を変更できるような高度なサービスを作成する方法として provider メソッドも提供されています。
91 |
92 |
93 |
94 | **Tip:**
95 | アプリケーション共通の定数定義を作成する手段として constant も提供されています。
96 | constant で作成されたサービスは config ブロックでも取得できます。
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/account/step06/app.js:
--------------------------------------------------------------------------------
1 | angular.module('App', ['ngRoute', 'LocationBar'])
2 | .config(['$routeProvider', function ($routeProvider) {
3 | $routeProvider
4 | .when('/', {
5 | templateUrl: 'index-tmpl',
6 | controller: 'SheetListController'
7 | })
8 | .when('/new', {
9 | templateUrl: 'new-tmpl',
10 | controller: 'CreationController'
11 | })
12 | .when('/sheet/:id', {
13 | templateUrl: 'sheet-tmpl',
14 | controller: 'SheetController'
15 | })
16 | .otherwise({
17 | redirectTo: '/'
18 | });
19 | }])
20 | .service('sheets', [function () {
21 | this.list = [];
22 |
23 | this.add = function (lines) {
24 | this.list.push({
25 | id: String(this.list.length + 1),
26 | createdAt: Date.now(),
27 | lines: lines
28 | });
29 | };
30 |
31 | this.get = function (id) {
32 | var list = this.list;
33 | var index = list.length;
34 | var sheet;
35 |
36 | while (index--) {
37 | sheet = list[index];
38 | if (sheet.id === id) {
39 | return sheet;
40 | }
41 | }
42 | return null;
43 | };
44 | }])
45 | .controller('SheetListController', ['$scope', 'sheets',
46 | function SheetListController($scope, sheets) {
47 | $scope.list = sheets.list;
48 | }])
49 | .controller('CreationController', ['$scope', '$location', 'sheets',
50 | function CreationController($scope, $location, sheets) {
51 | function createOrderLine() {
52 | return {
53 | productName: '',
54 | unitPrice: 0,
55 | count: 0
56 | };
57 | }
58 |
59 | $scope.initialize = function () {
60 | $scope.lines = [createOrderLine()];
61 | };
62 |
63 | $scope.addLine = function () {
64 | $scope.lines.push(createOrderLine());
65 | };
66 |
67 | $scope.removeLine = function (target) {
68 | var lines = $scope.lines;
69 | var index = lines.indexOf(target);
70 |
71 | if (index !== -1) {
72 | lines.splice(index, 1);
73 | }
74 | };
75 |
76 | $scope.save = function () {
77 | sheets.add($scope.lines);
78 | $location.path('/');
79 | };
80 |
81 | $scope.getSubtotal = function (orderLine) {
82 | return orderLine.unitPrice * orderLine.count;
83 | };
84 |
85 | $scope.getTotalAmount = function (lines) {
86 | var totalAmount = 0;
87 |
88 | angular.forEach(lines, function (orderLine) {
89 | totalAmount += $scope.getSubtotal(orderLine);
90 | });
91 |
92 | return totalAmount;
93 | };
94 |
95 | $scope.initialize();
96 | }])
97 | .controller('SheetController', [function SheetController() {/* 詳細用 */}]);
98 |
--------------------------------------------------------------------------------
/account/step07/app.js:
--------------------------------------------------------------------------------
1 | angular.module('App', ['ngRoute', 'LocationBar'])
2 | .config(['$routeProvider', function ($routeProvider) {
3 | $routeProvider
4 | .when('/', {
5 | templateUrl: 'index-tmpl',
6 | controller: 'SheetListController'
7 | })
8 | .when('/new', {
9 | templateUrl: 'new-tmpl',
10 | controller: 'CreationController'
11 | })
12 | .when('/sheet/:id', {
13 | templateUrl: 'sheet-tmpl',
14 | controller: 'SheetController'
15 | })
16 | .otherwise({
17 | redirectTo: '/'
18 | });
19 | }])
20 | .service('sheets', [function () {
21 | this.list = [];
22 |
23 | this.add = function (lines) {
24 | this.list.push({
25 | id: String(this.list.length + 1),
26 | createdAt: Date.now(),
27 | lines: lines
28 | });
29 | };
30 |
31 | this.get = function (id) {
32 | var list = this.list;
33 | var index = list.length;
34 | var sheet;
35 |
36 | while (index--) {
37 | sheet = list[index];
38 | if (sheet.id === id) {
39 | return sheet;
40 | }
41 | }
42 | return null;
43 | };
44 | }])
45 | .controller('SheetListController', ['$scope', 'sheets',
46 | function SheetListController($scope, sheets) {
47 | $scope.list = sheets.list;
48 | }])
49 | .controller('CreationController', ['$scope', '$location', 'sheets',
50 | function CreationController($scope, $location, sheets) {
51 | function createOrderLine() {
52 | return {
53 | productName: '',
54 | unitPrice: 0,
55 | count: 0
56 | };
57 | }
58 |
59 | $scope.initialize = function () {
60 | $scope.lines = [createOrderLine()];
61 | };
62 |
63 | $scope.addLine = function () {
64 | $scope.lines.push(createOrderLine());
65 | };
66 |
67 | $scope.removeLine = function (target) {
68 | var lines = $scope.lines;
69 | var index = lines.indexOf(target);
70 |
71 | if (index !== -1) {
72 | lines.splice(index, 1);
73 | }
74 | };
75 |
76 | $scope.save = function () {
77 | sheets.add($scope.lines);
78 | $location.path('/');
79 | };
80 |
81 | $scope.getSubtotal = function (orderLine) {
82 | return orderLine.unitPrice * orderLine.count;
83 | };
84 |
85 | $scope.getTotalAmount = function (lines) {
86 | var totalAmount = 0;
87 |
88 | angular.forEach(lines, function (orderLine) {
89 | totalAmount += $scope.getSubtotal(orderLine);
90 | });
91 |
92 | return totalAmount;
93 | };
94 |
95 | $scope.initialize();
96 | }])
97 | .controller('SheetController', [function SheetController() {/* 詳細用 */}]);
98 |
--------------------------------------------------------------------------------
/guide/scope/demo2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | #
16 | value
17 | old
18 | square
19 |
20 |
21 |
22 |
23 |
24 | Ctrl1 (string)
25 | {{ value }}
26 | {{ old }}
27 | {{ square }}
28 | 変更
29 |
30 |
31 | Ctrl2 (function)
32 | {{ value }}
33 | {{ old }}
34 | {{ square }}
35 | 変更
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | #
44 | original
45 | $watch
46 | $watch (objectEquality)
47 | $watchCollection
48 |
49 |
50 |
51 |
52 |
53 | Ctrl3
54 | {{ original }}
55 | {{ clone1 }}
56 | {{ clone2 }}
57 | {{ clone3 }}
58 | 変更
59 |
60 |
61 | Ctrl4
62 | {{ original }}
63 | {{ clone1 }}
64 | {{ clone2 }}
65 | {{ clone3 }}
66 | 変更
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/todo/step06/article.md:
--------------------------------------------------------------------------------
1 | $watch メソッドを使ってモデルの変更時の振る舞いを追加しましょう。
2 |
3 | ## ToDo リストモデルの変更を監視する
4 | 絞り込み表示ができるようになりましたがボタンに表示する件数が仮値のままです。
5 | 正しい件数を表示出来るようにしましょう。
6 |
7 | 未了、完了の件数はリスト内の Todo モデルの総数や状態の変更に合わせて更新する必要があります。
8 | [$scope.$watch](http://docs.angularjs.org/api/ng.$rootScope.Scope#$watch) メソッドを使ってリストが変更された時に正しい件数に更新されるモデルを作成しましょう。
9 |
10 | ```javascript
11 | $scope.$watch('todos', function (todos) {
12 | // todos が増減したり各要素のプロパティが変更された時に実行される
13 | }, true);
14 | ```
15 |
16 | 配列の増減と配列内の ToDo の done プロパティを監視するため第三引数(objectEquality フラグ)を true に設定します。
17 | これを忘れると関数が実行されるのが todos に新しい値が代入された時だけになってしまいますので注意してください。
18 |
19 | ### コントローラからフィルタを使用する
20 | 未了、完了の件数を得るためにはリストから合致する要素だけを抽出する必要があります。
21 | この処理は先ほどのステップで使用したコアモジュールの filter フィルタが実現していたものと同じです。
22 |
23 | コントローラで特定のフィルタを使用したい場合 [$filter サービス](http://docs.angularjs.org/api/ng.$filter)を使ってフィルタを取得することができます。
24 | アノテーション配列を編集しコンストラクタが $filter サービスを受け取れるようにしましょう。
25 |
26 | ```javascript
27 | .controller('MainController', ['$scope', '$filter', function ($scope, $filter) {
28 | // 省略
29 |
30 | var where = $filter('filter'); // filter フィルタ関数の取得
31 | $scope.$watch('todos', function (todos) {
32 | var length = todos.length;
33 |
34 | $scope.allCount = length; // 総件数モデル
35 | $scope.doneCount = where(todos, $scope.filter.done).length; // 完了件数モデル
36 | $scope.remainingCount = length - $scope.doneCount; // 未了件数モデル
37 | }, true);
38 | }]);
39 | ```
40 |
41 | filter 関数(変数 where)を利用して完了した ToDo だけの配列を取得しその長さを完了件数モデルとしました。
42 | さらに、総件数から完了件数を引いた値を利用して未了件数モデルに代入します。
43 |
44 | $scope.$watch に登録された関数はコントローラがインスタンス化された時初期化処理として一度実行されるので関数外で各モデルの初期値を定義しておく必要はありません。
45 |
46 | 最後にモックアップを編集して各モデルを表示しましょう。
47 |
48 | ```html
49 | 全部 {{ allCount }}
50 | 未了 {{ remainingCount }}
51 | 完了 {{ doneCount }}
52 | ```
53 |
54 |
55 |
56 | ### $watch メソッドを使用しない実装方法
57 | 慣れないうちは $watch は少々扱いづらいので別のアプローチでの実装法も紹介します。
58 |
59 | ```javascript
60 | $scope.getDoneCount = function () {
61 | return where($scope.todos, $scope.filter.done).length;
62 | };
63 | ```
64 |
65 | ```html
66 | 全部 {{ todos.length }}
67 | 未了 {{ todos.length - getDoneCount() }}
68 | 完了 {{ getDoneCount() }}
69 | ```
70 |
71 | ビューは $scope からの更新通知を受け取ると式を再評価するためこの実装は結果的に $watch メソッドを使った実装と
72 | 同じことになります。
73 |
74 | ただし $watch を使った実装ならば再評価時の動作は単純なプロパティ参照で済むのに対し、この実装は再評価時に必ず関数を実行するため多用するとパフォーマンスが低下する可能性があります。
75 |
--------------------------------------------------------------------------------
/guide/module/article.md:
--------------------------------------------------------------------------------
1 | AngularJS でアプリケーションを構築する時最初に必要になるのがモジュールです。
2 |
3 | モジュールはアプリケーションが必要としているパーツの情報を持ったオブジェクトです。
4 |
5 | AngularJS ではアプリケーションに必要な機能は全てモジュールのメソッドを使って定義します。
6 |
7 | ## 作る
8 | モジュールを作成するには angular.module メソッドを使用します。
9 |
10 | ```javascript
11 | var app = angular.module('App', []);
12 | ```
13 |
14 | 第一引数はアプリケーションの名前です。
15 | 第二引数の配列には作成するモジュールが依存している外部モジュールの名前を列挙します。
16 | 依存するモジュールがひとつも存在しない場合でも第二引数を省略することは出来ません。
17 | module メソッドは第二引数が省略されると作成済みモジュールを取得するゲッターとして機能します。
18 |
19 | ## 使う
20 | AngularJS は DOM の準備が終わる(DOMContentLoaded)と ng-app 属性を持った要素を探し出し属性値で指定されたモジュールをロードしてアプリケーションを開始します。
21 |
22 | ```html
23 |
24 |
25 | ```
26 |
27 | ## モジュールの依存解決
28 | モジュールは別のモジュールに定義された機能を利用することが出来ます。
29 | 以下の例では App モジュールは自身に定義された機能以外に Sub モジュールに定義された機能も使用することが出来ます。
30 |
31 | ```javascript
32 | // sub.js
33 | angular.module('Sub', []);
34 | ```
35 | ```javascript
36 | // app.js
37 | angular.module('App', ['Sub']);
38 | ```
39 |
40 | 上記のコードではモジュールは別々のファイルに記述されています。
41 | この時 sub.js は app.js より先に読み込まれている**必要がありません**。
42 | どちらが先に読み込まれても正しく動作します。
43 |
44 | モジュールの依存解決はファイルがロードされた時ではなく、モジュールがロードされるときに行われます。
45 |
46 | ## モジュールの設定を変更する
47 | モジュールで使用するアプリケーション共通の機能(サービス)の設定を変更するためには config メソッドを使用します。
48 |
49 | ```javascript
50 | angular.module('App', [])
51 | .config(['$interpolateProvider', '$locationProvider', function ($interpolateProvider, $locationProvider) {
52 | // テンプレート構文を // ~ // に変更
53 | $interpolateProvider.startSymbol('//').endSymbol('//');
54 |
55 | // $location サービスの振る舞いを History API を使用するものに変更
56 | $locationProvider.html5Mode(true);
57 | }]);
58 | ```
59 |
60 | config メソッドはアノテーション配列を受け取ります。
61 | 設定関数はモジュールがロードされたときに実行されます。実行タイミングはサービスが初期化されるよりも前であることに注意してください。
62 |
63 |
64 | **Tip:**
65 | アノテーション配列については
依存関係の注釈付け で説明しています。
66 |
67 |
68 |
69 | ## モジュールにスタートアップ処理を与える
70 | モジュールのロード完了直後に実行する必要がある処理を作成するには run メソッドを使用します。
71 |
72 | ```javascript
73 | angular.module('App', [])
74 | .run(['$rootScope', function (scope) {
75 | scope.$on('notification', function (evt, text) {
76 | // ...
77 | });
78 | }]);
79 | ```
80 | run メソッドはアノテーション配列を受け取ります。
81 | 関数はモジュールがロードされ設定処理が終わった直後に実行されます。
82 | config メソッドと違い関数はサービスを受け取ることができます。
83 |
84 | ## モジュールに機能を定義する
85 | モジュールにコントローラやサービスを定義する方法は各ガイドを参照してください。
86 |
87 | ## モジュールの作成からアプリケーション開始までの流れ
88 | 1. js ファイルがロードされます。
89 | 2. angular.module でモジュールが作成されます。
90 | 3. module オブジェクトのメソッドで機能が定義されます。
91 | この時点では引数として渡した関数は全て実行されません。
92 | 4. DOMContentLoaded のタイミングで フレームワークが ng-app 属性を持った要素を探します。
93 | 5. 要素が見つかったら指定モジュールの依存を解決します。
94 | 6. モジュールの設定関数が(あれば)実行されます。
95 | 7. モジュールのスタートアップ関数が(あれば)実行されます。
96 | 8. モジュールの情報を元にディレクティブが呼び出されます。
97 |
--------------------------------------------------------------------------------
/guide/service/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
46 |
47 |
48 |
49 |
50 | 商品名
51 | 単価
52 | 個数
53 | 小計
54 |
55 |
56 |
57 |
58 | {{ line.productName }}
59 | {{ line.unitPrice | number}}
60 | {{ line.count | number }}
61 | {{ sheet.getSubtotal(line) | number }}
62 |
63 |
64 |
65 |
合計: {{ sheet.getTotalAmount() }}
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/account/step01/article.html:
--------------------------------------------------------------------------------
1 | アプリの作成にあたって最低限必要なものを準備します。
2 | まず index.html と app.js を用意します。
3 | 作業はこの2つのファイルを編集して行います。
4 | まず index.html を以下のように編集します。
5 | <!doctype html>
6 | <html lang ="ja" >
7 | <head >
8 | <meta charset ="UTF-8" >
9 | <title > 帳票アプリ</title >
10 | </head >
11 | <body ng-app ="App" >
12 | <script src ="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.3/angular.min.js" > </script >
13 | <script src ="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.3/angular-route.min.js" > </script >
14 | <script src ="app.js" > </script >
15 | </body >
16 | </html >
17 | フレームワークを読み込む
18 | 今回は安定版(v1.0.8)ではなく RC 版(v1.2.0-rc.3) を使用します。
19 | ソースマップに対応しているので圧縮版で開発しても問題はありません。
20 | アプリケーションのルーティングに必要な angular-route.js も読み込みます。
21 | ルーティング機能は v1.0.8 ではコアモジュールに実装されていました。
22 | v1.2.0 では本体から分離され独立したモジュールになったため追加で読み込みます。
23 | 本来は古い Internet Explorer の為にもう少し色々書く必要がありますが今回は省きます。
24 | 詳しくは公式サイト を読みましょう。
25 | モジュールを読み込む
26 | body 要素に ng-app="App" を追加します。
27 | この記述で AngularJS はこのページに App モジュールをロードするようになります。
28 | しかしこの時点では App モジュールはまだ存在していないのでコンソールにエラーが出ます。
29 | app.js を編集し App モジュールを作成しましょう。
30 | angular.module('App' , ['ngRoute' ]);
31 | App モジュール内でルーティング機能が使えるように ngRoute モジュールに依存することを明記します。
32 |
33 |
--------------------------------------------------------------------------------
/todo/step04/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Todo アプリ
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
15 |
18 |
19 |
31 |
32 |
33 |
34 |
56 |
57 |
58 |
59 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/account/step07/article.md:
--------------------------------------------------------------------------------
1 | 帳票を保存し一覧を表示できるようになりました。
2 | しかしこの保存機能には明細行に不正な入力がなされていた場合でも保存できてしまうという問題があります。
3 |
4 | 入力の検証を行い不正な帳票は保存できないようにしましょう。
5 |
6 | ## 適切な帳票の条件
7 | 検証機能を実装する前に適切な帳票の条件を決めます。
8 |
9 | * 明細行リストは必ず1つ以上の明細行を持たくてはならない。
10 | * 明細行の各項目はすべて必須項目である。
11 | * 明細行の単価、個数は n >= 0 でなければならない。
12 | * 明細行の単価、個数は 整数でなければならない。
13 |
14 | ## 削除ボタンを無効化する
15 | 条件のうち明細行の数については明細行が1つしか存在しない時は削除ボタンが機能しないようにすることで実装することにします。
16 |
17 | ```html
18 | 削除
20 | ```
21 |
22 | [ngDisabled ディレクティブ](http://docs.angularjs.org/api/ng.directive:ngDisabled) を使用し明細行リストの長さが2つ未満の時要素を無効化します。
23 | これでクリックイベントが発行されなくなるので明細行リストが空になることを防げます。
24 |
25 | ## 入力を検証する
26 | ユーザーの入力が適切かどうかを検証し不正な入力が存在していた時フォームの送信を禁止しましょう。
27 |
28 | まずフォームパーツに制約条件を加えます。
29 |
30 | AngularJS ではいくつかの制約条件については HTML5 で標準化された制約属性がそのまま使用できます。
31 | 使用可能な制約属性は required, min, max の三種類のみで step, pattern などは使用できません。
32 | 使用できない制約属性の代替として ngPattern, ngMaxlength などの独自の属性が用意されています。
33 |
34 | ```html
35 |
37 |
39 |
41 | ```
42 |
43 | input 要素はすべて入力必須なので required 属性をセットします。
44 | input[number] 要素は 0 未満を許容しないので min 属性をセットします。
45 | pattern 属性は使用できなので ngPattern 属性を代わりに使用して入力を整数に限定します。
46 |
47 | これらの制約条件は ngModel ディレクティブが検証を行うために使用されます。
48 | 検証を行うのは ngModel ディレクティブなので標準化された検証機能が未実装の古いブラウザでも検証を行うことができます。
49 | 逆に検証機能を備えたモダンブラウザでは互いの検証機能が衝突してしまうため form 要素に novalidate 属性をセットしてブラウザサイドの検証機能を無効化する必要があります。
50 |
51 | 各制約は ngModel ディレクティブによってリアルタイムで検証されます。
52 | 検証結果は form 要素に紐付いた [FormController](http://docs.angularjs.org/api/ng.directive:form.FormController) オブジェクトに格納されています。
53 |
54 | 検証結果を参照する必要がある場合フォームパーツが所属している form 要素に name 属性をつけモデル化する必要があります。
55 |
56 | ```html
57 |
72 |
73 | 追加
74 |
75 | ```
76 |
77 | form 要素に対して [ngSubmit ディレクティブ](http://docs.angularjs.org/api/ng.directive:ngSubmit)の使用を宣言します。
78 | ngSubmit ディレクティブはサブミットイベント発生時に属性値の式を直近の $scope オブジェクトで評価してくれます。
79 |
80 | これでフォームが送信された時 addTodo が実行され新しい ToDo がリストに追加されます。
81 |
82 | 続いて ToDo リストを表示してみましょう。
83 | とりあえず未完了状態の ToDo のモックアップを編集して表示に使いましょう。
84 |
85 | ```html
86 |
87 |
88 |
89 | {{ todo.title }}
90 | 削除
91 |
92 |
93 | ```
94 |
95 | li 要素に対して [ngRepeat ディレクティブ](http://docs.angularjs.org/api/ng.directive:ngRepeat)の使用を宣言します。
96 | ngRepeat ディレクティブは JavaScript の for...in のような、コレクションを列挙する特別な構文を受け取り DOM を複製するディレクティブです。 また、このディレクティブは複製された DOM に新たな $scope オブジェクトを結びつけます。
97 |
98 | $scope オブジェクトはキーワードとなった名前のプロパティ(上記例では todo)を持ちそこには配列の各要素が代入されます。
99 | つまり li 要素の以下の要素は個々の ToDo モデルにアクセス可能ということです。
100 |
101 | 本当に ToDo モデルにアクセス可能か確認するためテンプレート構文 `{{ }}` を使って ToDo モデルの要件を出力してみましょう。
102 |
103 | ついでなので ToDo の総数も表示できるよう該当箇所を編集しましょう。
104 |
105 | ```html
106 | 全部 {{ todos.length }}
107 | ```
108 |
109 | プレビューで実際に動く様子を確認してみましょう。
110 |
111 |
112 |
113 |
114 | **Tip:**
115 | プレビューで使用している LocationBar モジュールは iframe 内でもルーティングの状態が見えるようにアドレスバーを表示するためのモジュールです。
116 | 本チュートリアルとは直接関係しないものです。
117 |
118 |
--------------------------------------------------------------------------------
/todo/step05/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Todo アプリ
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
15 |
18 |
19 |
21 |
26 |
27 | 追加
29 |
30 |
31 |
32 |
33 |
34 |
62 |
63 |
64 |
65 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/todo/step03/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Todo アプリ
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
15 |
18 |
19 |
21 |
25 |
26 | 追加
28 |
29 |
30 |
31 |
32 |
33 |
55 |
56 |
57 |
58 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/todo/step06/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Todo アプリ
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
15 |
18 |
19 |
21 |
26 |
27 | 追加
29 |
30 |
31 |
32 |
33 |
34 |
62 |
63 |
64 |
65 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/todo/step07/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Todo アプリ
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
15 |
18 |
19 |
21 |
26 |
27 | 追加
29 |
30 |
31 |
32 |
33 |
34 |
62 |
63 |
64 |
65 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/components/angular-route/angular-route.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.2.0-rc.3
3 | (c) 2010-2012 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(u,c,A){'use strict';function w(c,s,g,b,d){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",compile:function(l,m,y){return function(p,l,m){function k(){h&&(h.$destroy(),h=null);q&&(d.leave(q),q=null)}function x(){var a=c.current&&c.current.locals,e=a&&a.$template;if(e){var r=p.$new();y(r,function(v){k();v.html(e);d.enter(v,null,l);var f=g(v.contents()),n=c.current;h=n.scope=r;q=v;if(n.controller){a.$scope=h;var p=b(n.controller,a);n.controllerAs&&(h[n.controllerAs]=p);
7 | v.data("$ngControllerController",p);v.children().data("$ngControllerController",p)}f(h);h.$emit("$viewContentLoaded");h.$eval(t);s()})}else k()}var h,q,t=m.onload||"";p.$on("$routeChangeSuccess",x);x()}}}}u=c.module("ngRoute",["ng"]).provider("$route",function(){function u(b,d){return c.extend(new (c.extend(function(){},{prototype:b})),d)}function s(b,c){var l=c.caseInsensitiveMatch,m={originalPath:b,regexp:b},g=m.keys=[];b=b.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?|\*])?/g,function(b,
8 | c,d,k){b="?"===k?k:null;k="*"===k?k:null;g.push({name:d,optional:!!b});c=c||"";return""+(b?"":c)+"(?:"+(b?c:"")+(k&&"(.+?)"||"([^/]+)")+(b||"")+")"+(b||"")}).replace(/([\/$\*])/g,"\\$1");m.regexp=RegExp("^"+b+"$",l?"i":"");return m}var g={};this.when=function(b,d){g[b]=c.extend({reloadOnSearch:!0},d,b&&s(b,d));if(b){var l="/"==b[b.length-1]?b.substr(0,b.length-1):b+"/";g[l]=c.extend({redirectTo:b},s(l,d))}return this};this.otherwise=function(b){this.when(null,b);return this};this.$get=["$rootScope",
9 | "$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(b,d,l,m,s,p,w,z){function k(){var a=x(),e=t.current;if(a&&e&&a.$$route===e.$$route&&c.equals(a.pathParams,e.pathParams)&&!a.reloadOnSearch&&!q)e.params=a.params,c.copy(e.params,l),b.$broadcast("$routeUpdate",e);else if(a||e)q=!1,b.$broadcast("$routeChangeStart",a,e),(t.current=a)&&a.redirectTo&&(c.isString(a.redirectTo)?d.path(h(a.redirectTo,a.params)).search(a.params).replace():d.url(a.redirectTo(a.pathParams,d.path(),
10 | d.search())).replace()),m.when(a).then(function(){if(a){var b=c.extend({},a.resolve),e,f;c.forEach(b,function(a,e){b[e]=c.isString(a)?s.get(a):s.invoke(a)});c.isDefined(e=a.template)?c.isFunction(e)&&(e=e(a.params)):c.isDefined(f=a.templateUrl)&&(c.isFunction(f)&&(f=f(a.params)),f=z.getTrustedResourceUrl(f),c.isDefined(f)&&(a.loadedTemplateUrl=f,e=p.get(f,{cache:w}).then(function(a){return a.data})));c.isDefined(e)&&(b.$template=e);return m.all(b)}}).then(function(d){a==t.current&&(a&&(a.locals=d,
11 | c.copy(a.params,l)),b.$broadcast("$routeChangeSuccess",a,e))},function(c){a==t.current&&b.$broadcast("$routeChangeError",a,e,c)})}function x(){var a,b;c.forEach(g,function(r,k){var f;if(f=!b){var n=d.path();f=r.keys;var h={};if(r.regexp)if(n=r.regexp.exec(n)){for(var g=1,l=n.length;g役割
2 | ディレクティブは以下の役割を果たすために使用されるものです。
3 |
4 | 宣言的な HTML の拡張(つまり HTML への機能追加)
5 |
6 | ディレクティブは HTML に新たな振る舞いを与えたり DOM を変更したりします。
7 | 使う
8 | HTML 内で普通にマークアップするだけです。
9 | ディレクティブはタグや属性、クラス、コメントなどの方法で呼び出します。
10 | ただし全てのディレクティブで全ての呼び出し方が使えるわけではありません。
11 | 実際には多くのディレクティブが属性としてしか呼び出せません。
12 | <div ng-app ="App" >
13 | <div ng-controller ="MainCtrl" >
14 | <h1 class ="ng-bind: title" > </h1 >
15 | <button data-ng-click ="say()" > Click me!!</button >
16 | <ng-include="template" > </ng-include >
17 | </div >
18 | </div >
19 | ng- を含むタグ名、属性名、データ属性名、クラス名は全て AngularJS のコアモジュールが提供しているディレクティブです。
20 | 各ディレクティブがどんな働きを HTML に追加したのか軽く触れます。
21 |
22 | ngApp
23 | App モジュールを起動し子孫要素のテンプレートとディレクティブを解析します。
24 |
25 |
26 | ngController
27 | ビューに MainCtrl コントローラをアタッチし子孫要素の式のコンテキストを MainCtrl の $scope と結びつけます。
28 |
29 |
30 | ngBind
31 | $scope.title プロパティの値を自身の textContent に代入します。
32 |
33 |
34 | ngClick
35 | 自身がクリックされると $scope.say 関数が実行されます。
36 |
37 |
38 | ngInclude
39 | $scope.template プロパティの値の外部ファイルを読み込み自身の innerHTML に代入します。
40 |
41 |
42 |
43 | HTML バリデータをパスするようにしたい場合 data- 接頭辞を付けて汎用データ属性としてマークアップすることが可能です。
44 | 作る
45 | module インスタンスの directive メソッドでモジュールに新しいディレクティブを定義できます。
46 | module.directive('myDialog' , [function () {
47 | return function (scope, $el, attrs) {
48 | $el.on('click' , function () {
49 | window.alert('Hello ' + attrs.myDialog);
50 | });
51 | };
52 | }]);
53 | 第一引数がディレクティブの名前になります。
54 | 第二引数はアノテーション配列です。
55 | アノテーション配列の最後にはファクトリ関数を含めます。
56 | ファクトリ関数が返すべきものはディレクティブの振る舞いを定義した関数(リンク関数)です。
57 | ファクトリ関数はディレクティブがはじめて呼び出された時に一度だけ実行されます。
58 | 2回目以降のディレクティブ呼び出し時にはファクトリ関数は実行されません。
59 | ディレクティブの名前はキャメルケースで指定します。
60 | HTML 内ではハイフンケースで呼び出します。
61 | 上記の例では <p my-dialog="world">Click me!!</p> のように呼び出します。
62 | 要素をクリックすると属性値を受け取って アラートに 「Hallo world」と表示します。
63 | 上記は最も単純化された定義法でありより高度なディレクティブを作成する場合、ファクトリ関数はディレクティブ定義オブジェクトを返すようにします。
64 | ディレクティブ定義オブジェクトについては AngularJS: Directives や JavaScript - AngularJSのdirectiveとは - Qiita [キータ] を参考してください。
65 |
66 |
67 |
--------------------------------------------------------------------------------
/account/step03/article.md:
--------------------------------------------------------------------------------
1 | 特定のパスを訪問した時、先ほど作成したテンプレートが表示されるようにしましょう。
2 |
3 | ## AngularJS でのルーティングの基礎
4 | AngularJS のデフォルトのルーティングはハッシュフラグメントでビューを指し示します。
5 | $location サービスの設定を変更することで History API を使用するモードに変更することも可能です。
6 |
7 | 今回はサーバーサイドの実装がありませんのでデフォルトのハッシュフラグメント方式を採用します。
8 | アプリのページ(`./index.html`)のフラグメント部分(`#` 以降の部分)が App モジュールが管理すべきパスになります。
9 |
10 | ## ルートにテンプレートを割り当てる
11 | App モジュールはルーティングに関する設定をまだ持っていません。
12 | config メソッドを使ってアプリケーションの設定を行いましょう。
13 |
14 | ```javascript
15 | angular.module('App', ['ngRoute'])
16 | .config(['$routeProvider', function ($routeProvider) {}]);
17 | ```
18 |
19 | アプリケーションにルーティングの設定を加えるには ngRoute モジュールが提供する [$routeProvider](http://docs.angularjs.org/api/ngRoute.$routeProvider) が必要になります。
20 | コールバックが $routeProvider を受けとれるように依存の注釈を行います。
21 |
22 | ルートを設定するには $routeProvider.when メソッドを使用します。
23 |
24 | ```javascript
25 | $routeProvider
26 | .when('/', {
27 | templateUrl: 'index-tmpl'
28 | })
29 | .when('/new', {
30 | templateUrl: 'new-tmpl'
31 | })
32 | .when('/sheet/:id', {
33 | templateUrl: 'sheet-tmpl'
34 | });
35 | ```
36 |
37 | when メソッドは第一引数にルートを割り当てたいパスを、第二引数にルートの設定オブジェクトを受け取ります。
38 |
39 | ### 帳票一覧ビュー
40 | 手始めに `/` に index-tmpl テンプレートを割り当てます。
41 |
42 | オブジェクトの templateUrl プロパティに index-tmpl を設定します。
43 | templateUrl は本来外部ファイルの URL を受け取りますが前回のステップでも説明したとおりテンプレート化した script 要素の id 属性は URL として扱われるので属性値を与えてやれば OK です。
44 |
45 | これで `index.html#/` にアクセスすれば index-tmpl テンプレートが使用されるようになりました。
46 |
47 |
48 | **Tip:**
49 | フラグメント無しでページにアクセスした時、`/` にルート設定が存在していたら自動的に `#/` が付与されルート設定が適用されます。
50 |
51 |
52 | ### 帳票作成ビュー
53 | 同様の手順で `/new` に new-tmpl テンプレートを割り当てます。
54 | `index.html#/new` のとき new-tmpl テンプレートが使用されます。
55 |
56 |
57 | **Tip:**
58 | `/new/` に別の割り当てが無い限り `index/html#/new/` にアクセスしても new-tmpl テンプレートが使用されます(`index/html#/new` にリダイレクトされます)。
59 |
60 |
61 | ### 帳票詳細ビュー
62 | 帳票詳細ビューは `/sheet/帳票の ID` で表示するようにしましょう。
63 | 帳票の ID 部分は不定の値になります。
64 |
65 | パスに不定の値を含む場合は `:` キーワードを使用します。
66 | `:` キーワードの後に続く語句は $routeParams サービスオブジェクトのプロパティの名前として使用されます。
67 |
68 | 受け取りたい値は帳票の ID なのでそのまま `/sheet/:id` とし sheet-tmpl テンプレートを設定しましょう。
69 |
70 | ## ルートが設定されていないパスにアクセスした時の振る舞いを設定する
71 | 3つのルート設定が完了しました。
72 | しかし、このままではルートが設定されていないパスにアクセスがあった時どのテンプレートも表示されず白紙のページになってしまいます。
73 |
74 | ルートが設定されていないパスにアクセスした時、帳票一覧にリダイレクトされるようにしましょう。
75 |
76 | ```javascript
77 | $routeProvider
78 | .otherwise({
79 | redirectTo: '/'
80 | });
81 | ```
82 |
83 | 個別ルート外のパスにルート設定を行うには otherwise メソッドを使用します。
84 | otherwise メソッドは when メソッドと同じ形式の設定オブジェクトを受け取ります。
85 |
86 | 今回はリダイレクト先を指し示す redirectTo プロパティを設定して `index.html#/` にリダイレクトされるようにします。
87 |
88 | ## テンプレートが注入される要素を用意する
89 | ルート設定が完了したのでモジュールはどのパスの時どのテンプレートを使用すればいいのかがわかるようになりました。
90 | しかし、モジュールは DOM のどの部分にテンプレートを流し込めばいいのかを知りません。
91 |
92 | ngView ディレクティブを使ってテンプレートが注入されるべき要素を指定しましょう。
93 |
94 | ```html
95 |
96 | ```
97 |
98 | 入れ物となる要素を作成し `ng-view` 属性を追加します。
99 | ルート設定されたパスにユーザーがアクセスするとこの要素の中身が自動的に指定されたテンプレートに置き換えられます。
100 |
101 | ## グローバルナビゲーションを用意する
102 | 実際にパスが変更された時テンプレートが注入されるかを簡単に確認するためグローバルナビゲーションもマークアップしておきましょう。
103 |
104 | ```javascript
105 |
109 | ```
110 |
111 | 帳票一覧と帳票作成へのリンクを持っていれば場所やマークアップの仕方はお好みで構いません。
112 | 帳票詳細へのリンクは帳票一覧にすでに含まれているので必要ありません。
113 |
114 |
115 |
116 |
117 | **Tip:**
118 | プレビューは見栄えのために
Bootstrap を使用しています。
119 | その都合上マークアップが多少違います。
120 |
121 |
122 |
123 | **Tip:**
124 | プレビューで使用している LocationBar モジュールは iframe 内でもルーティングの状態が見えるようにアドレスバーを表示するためのモジュールです。
125 | 本チュートリアルとは直接関係しないものです。
126 |
127 |
--------------------------------------------------------------------------------
/todo/step08/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Todo アプリ
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
15 |
18 |
19 |
21 |
26 |
27 | 追加
29 |
30 |
31 |
32 |
33 |
34 |
64 |
65 |
66 |
67 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/account/step02/article.md:
--------------------------------------------------------------------------------
1 | 今回作成するのは3つのビューを持ったアプリケーションです。
2 | それぞれのビューで使用するテンプレートを準備しましょう。
3 |
4 | ## テンプレートの基礎
5 | テンプレートは専用の html ファイルを用意するか script 要素内に記述します。
6 |
7 | script 要素をテンプレートとして使用する場合 type 属性に `text/ng-template` を指定します。
8 | 適切な属性を付与することでその要素内の記述を、外部ファイルのように扱ってくれます。
9 |
10 | 1つの script 要素が外部ファイルの1つ分に相当するため script 要素には外部ファイルの url に相当する情報も持たせる必要があります。
11 | URL に相当する情報は要素の `id` 要素に指定します。
12 |
13 |
14 | **Warning:**
15 | テンプレートとして使用される `script` 要素の `id` は URL に見立てられるものなので `/templates/index.html` のような URL 風の値にしても構いません(公式リファレンスはそういう使い方をしています)。
16 | ただし HTML 4.01 で `id` 要素の値として使用できる記号は `-` `_` `:` `.` だけですので注意が必要です。
17 | HTML 5 では空白文字を禁止する制限以外に言及がないので `/` も使えるはずです。
18 | 互換性などを考慮する場合 `-` `_` 以外の記号を使うのはやめたほうがいいかもしれません。
19 |
20 |
21 | ## 帳票一覧のテンプレート
22 | id 要素の値は `index-tmpl` にしました。
23 |
24 | アプリケーションはまだデータを持っていないのでデータ部分は仮の値を入れておくことにします。
25 |
26 | リスト項目は詳細画面へのリンクを持っています。
27 | 詳細画面に割り当てる予定の URL に設定しておきましょう。
28 |
29 | ```html
30 |
38 | ```
39 |
40 | ## 帳票作成のテンプレート
41 | id 要素の値は `new-tmpl` にしました。
42 |
43 | 仮値として注文明細行を2つ持っていることにしておきます。
44 |
45 | 注文明細行は商品名、単価、個数を入力できるようにします。
46 | さらに小計の項目と明細行を削除するボタンも持っていることにしましょう。
47 |
48 | 合計金額を表示する場所も確保しておきましょう。
49 |
50 | そのほかにも「明細行を追加」などいくつかのボタンとリンクがあります。
51 |
52 | ```html
53 |
96 | ```
97 |
98 | ## 帳票詳細のテンプレート
99 | id 要素の値は `sheet-tmpl` にしました。
100 |
101 | ここでも仮値として注文明細行を2つ持っていることにしておきます。
102 | 表示する項目は作成画面とほぼ同じです。
103 |
104 | このページは作成済みの帳票を閲覧するだけなのでインタラクティブな要素は帳票一覧へ戻るリンクのみです。
105 |
106 | ```html
107 |
141 | ```
142 |
--------------------------------------------------------------------------------
/guide/module/article.html:
--------------------------------------------------------------------------------
1 | AngularJS でアプリケーションを構築する時最初に必要になるのがモジュールです。
2 | モジュールはアプリケーションが必要としているパーツの情報を持ったオブジェクトです。
3 | AngularJS ではアプリケーションに必要な機能は全てモジュールのメソッドを使って定義します。
4 | 作る
5 | モジュールを作成するには angular.module メソッドを使用します。
6 | var app = angular.module('App' , []);
7 | 第一引数はアプリケーションの名前です。
8 | 第二引数の配列には作成するモジュールが依存している外部モジュールの名前を列挙します。
9 | 依存するモジュールがひとつも存在しない場合でも第二引数を省略することは出来ません。
10 | module メソッドは第二引数が省略されると作成済みモジュールを取得するゲッターとして機能します。
11 | 使う
12 | AngularJS は DOM の準備が終わる(DOMContentLoaded)と ng-app 属性を持った要素を探し出し属性値で指定されたモジュールをロードしてアプリケーションを開始します。
13 | <body ng-app ="App" >
14 | </body >
15 | モジュールの依存解決
16 | モジュールは別のモジュールに定義された機能を利用することが出来ます。
17 | 以下の例では App モジュールは自身に定義された機能以外に Sub モジュールに定義された機能も使用することが出来ます。
18 |
19 | angular.module('Sub' , []);
20 |
21 | angular.module('App' , ['Sub' ]);
22 | 上記のコードではモジュールは別々のファイルに記述されています。
23 | この時 sub.js は app.js より先に読み込まれている必要がありません 。
24 | どちらが先に読み込まれても正しく動作します。
25 | モジュールの依存解決はファイルがロードされた時ではなく、モジュールがロードされるときに行われます。
26 | モジュールの設定を変更する
27 | モジュールで使用するアプリケーション共通の機能(サービス)の設定を変更するためには config メソッドを使用します。
28 | angular.module('App' , [])
29 | .config(['$interpolateProvider' , '$locationProvider' , function ($interpolateProvider, $locationProvider) {
30 |
31 | $interpolateProvider.startSymbol('//' ).endSymbol('//' );
32 |
33 |
34 | $locationProvider.html5Mode(true );
35 | }]);
36 | config メソッドはアノテーション配列を受け取ります。
37 | 設定関数はモジュールがロードされたときに実行されます。実行タイミングはサービスが初期化されるよりも前であることに注意してください。
38 |
39 |
Tip:
40 | アノテーション配列については
依存関係の注釈付け で説明しています。
41 |
42 |
43 |
44 | モジュールにスタートアップ処理を与える
45 | モジュールのロード完了直後に実行する必要がある処理を作成するには run メソッドを使用します。
46 | angular.module('App' , [])
47 | .run(['$rootScope' , function (scope) {
48 | scope.$on('notification' , function (evt, text) {
49 |
50 | });
51 | }]);
52 | run メソッドはアノテーション配列を受け取ります。
53 | 関数はモジュールがロードされ設定処理が終わった直後に実行されます。
54 | config メソッドと違い関数はサービスを受け取ることができます。
55 | モジュールに機能を定義する
56 | モジュールにコントローラやサービスを定義する方法は各ガイドを参照してください。
57 | モジュールの作成からアプリケーション開始までの流れ
58 |
59 | js ファイルがロードされます。
60 | angular.module でモジュールが作成されます。
61 | module オブジェクトのメソッドで機能が定義されます。
62 | この時点では引数として渡した関数は全て実行されません。
63 | DOMContentLoaded のタイミングで フレームワークが ng-app 属性を持った要素を探します。
64 | 要素が見つかったら指定モジュールの依存を解決します。
65 | モジュールの設定関数が(あれば)実行されます。
66 | モジュールのスタートアップ関数が(あれば)実行されます。
67 | モジュールの情報を元にディレクティブが呼び出されます。
68 |
69 |
70 |
--------------------------------------------------------------------------------
/account/step05/article.md:
--------------------------------------------------------------------------------
1 | 前回のステップまででルート訪問時に使用されるテンプレートとコントローラの準備が整いました。
2 |
3 | 次は CreationController を編集して帳票作成ページで必要なモデルを定義していきましょう。
4 |
5 | ## 帳票作成ページで必要なものをおさらいする
6 | 実装を始める前に必要なものを再確認しましょう。
7 |
8 | * データ
9 | * 注文明細行のリスト
10 | * 個別の注文明細行 - リストの要素
11 | * ビジネスロジック
12 | * 任意の明細行から小計を計算する機能
13 | * リストモデル内の明細行の合計金額を計算する機能
14 | * リストモデルに新しい明細行を追加する機能
15 | * 任意の明細行をリストモデルから取り除く機能
16 | * リストモデルを初期化する機能
17 | * リストモデルから帳票モデルを作成して保存する機能
18 |
19 | ## データを準備する
20 | まずデータの準備から始めましょう。
21 |
22 | ```javascript
23 | .controller('CreationController', ['$scope', function CreationController($scope) {
24 | // 新しい明細行を作成する
25 | function createOrderLine() {
26 | return {
27 | productName: '',
28 | unitPrice: 0,
29 | count: 0
30 | };
31 | }
32 |
33 | $scope.lines = [createOrderLine()]; // 明細行リスト
34 | }])
35 | ```
36 |
37 | モデルは $scope オブジェクトのプロパティとして定義する必要があるので依存の注釈付けを行いコンストラクタが $scope オブジェクトを受け取れるようにします。
38 |
39 | 明細行は行を追加する処理などで作成される必要があるので createOrderLine 関数を準備しておきます。
40 | 新しい明細行が必要なときはこの関数を実行して取得することにします。
41 |
42 | 明細行リストは初期状態で明細行を一つ持っているようにしたいので createOrderLine 関数を使って配列にひとつ要素を加えておきます。
43 |
44 | ## データを表示する
45 | テンプレートを編集して lines モデルが表示されるようにしましょう。
46 |
47 | ```html
48 |
49 |
50 |
51 |
52 | n,nnn
53 | 削除
54 |
55 | ```
56 |
57 | [ngRepeat ディレクティブ](http://docs.angularjs.org/api/ng.directive:ngRepeat)を使い tr 要素を lines モデルの長さ分だけ反復させます。
58 | 各 input 要素と明細行のプロパティが双方向に結びつくよう [ngModel ディレクティブ](http://docs.angularjs.org/api/ng.directive:ngModel)を使用します。
59 |
60 | ## 振る舞いを加える
61 | リストに行を追加する機能や任意の行を削除する機能などの振る舞いを実装しビューから呼び出してみましょう。
62 |
63 | ```javascript
64 | // リストモデルに新しい明細行を追加する
65 | $scope.addLine = function () {
66 | $scope.lines.push(createOrderLine());
67 | };
68 |
69 | // リストモデルを初期化する
70 | $scope.initialize = function () {
71 | $scope.lines = [createOrderLine()];
72 | };
73 |
74 | // リストモデルから帳票モデルを作成して保存
75 | $scope.save = function () {};
76 |
77 | // 任意の明細行をリストモデルから取り除く
78 | $scope.removeLine = function (target) {
79 | var lines = $scope.lines;
80 | var index = lines.indexOf(target);
81 |
82 | if (index !== -1) {
83 | lines.splice(index, 1);
84 | }
85 | };
86 | ```
87 |
88 | save メソッドについてはまだ帳票の保存先を用意していないため、後回しにしておきます。
89 |
90 | テンプレートも合わせて編集しましょう。
91 |
92 | ```html
93 | 明細行を追加
94 | 帳票を初期化
95 | ```
96 | ```html
97 | 削除
98 | ```
99 |
100 | button 要素に対して [ngClick ディレクティブ](http://docs.angularjs.org/api/ng.directive:ngClick)を使い振る舞いを実行するようにします。
101 |
102 | ```html
103 |
104 | ```
105 |
106 | form 要素には [ngSubmit ディレクティブ](http://docs.angularjs.org/api/ng.directive:ngSubmit)を使用します。
107 |
108 | ## 小計、合計を表示する
109 | 小計と合計を算出し出力されるようにしましょう。
110 |
111 | ```javascript
112 | // 引数から小計を計算して返す
113 | $scope.getSubtotal = function (orderLine) {
114 | return orderLine.unitPrice * orderLine.count;
115 | };
116 |
117 | // リストから合計金額を計算して返す
118 | $scope.getTotalAmount = function (lines) {
119 | var totalAmount = 0;
120 |
121 | angular.forEach(lines, function (orderLine) {
122 | totalAmount += $scope.getSubtotal(orderLine);
123 | });
124 |
125 | return totalAmount;
126 | };
127 | ```
128 |
129 | テンプレートは以下のとおりです。
130 |
131 | ```html
132 | {{ getSubtotal(orderLine) | number }}
133 | ```
134 |
135 | ```html
136 | {{ getTotalAmount(lines) | number }}
137 | ```
138 |
139 | [number フィルタ](http://docs.angularjs.org/api/ng.filter:number)を使用して桁区切りで表示されるようにします。
140 |
141 |
142 |
--------------------------------------------------------------------------------
/guide/controller/article.html:
--------------------------------------------------------------------------------
1 | 役割
2 | AngularJS のコントローラは以下の役割を果たすために使用されるものです。
3 |
4 | $scope オブジェクトにプロパティを追加しモデルを作成する。
5 | $scope オブジェクトにメソッドを追加しビューから参照できるようにする。
6 |
7 | コントローラはビューに一切依存せずにモデルの操作のみに集中する場所です。
8 | 作る
9 | module インスタンスの controller メソッドでモジュールに新しいコントローラを定義できます。
10 | module.controller('UserController' , ['$scope' , function UserController ($scope) {
11 |
12 | $scope.users = [
13 | {name: 'foo' , email: 'bar@example.com' }
14 | ];
15 |
16 |
17 | $scope.addUser = function () {
18 | $scope.users.push({
19 | name: $scope.name,
20 | email: $scope.email
21 | });
22 | reset();
23 | };
24 |
25 |
26 | function reset () {
27 | $scope.name = null ;
28 | $scope.email = null ;
29 | }
30 |
31 |
32 | reset();
33 | }]);
34 | 第一引数がコントローラの名前になります。
35 | 第二引数はアノテーション配列です。
36 | アノテーション配列の最後にはコンストラクタを含めます。
37 | AngularJS ではグローバルスコープに定義した関数も暗黙的にコントローラとして扱います。
38 | ただしこの方法は推奨されていません。
39 | 必ず module.controller メソッドを使うようにしてください。
40 | 使う
41 | ng-controller ディレクティブを使用することでコントローラとビューを関連付けます。
42 | 属性値にコントローラの名前を指定することでディレクティブが解決される時そのコントローラはインスタンス化されます。
43 | <div ng-controller ="UserController" >
44 | <form ng-submit ="addUser()" >
45 | <input type ="text" ng-model ="name" >
46 | <input type ="email" ng-model ="email" >
47 | <input type ="submit" valuse ="追加" >
48 | </form >
49 | <ul >
50 | <li ng-repeat ="user in users" > {{ user.name }} - {{ user.email }} </li >
51 | </ul >
52 | </div >
53 |
54 | Tip:
55 | 他にも $route サービスから初期化する方法もあります。
56 |
57 |
58 |
59 | Tip:
60 | v1.2.0 から as propertyName 構文がサポートされています。
61 | as の後に続くキーワードを $scope のプロパティとして作成しコンストラクタの this を代入します。
62 | この構文によりビューからコントローラのコンテキストに速やかにアクセスできます。
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/todo/step09/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Todo アプリ
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
15 |
18 |
19 |
22 |
27 |
28 | 追加
30 |
31 |
32 |
33 |
34 |
35 |
66 |
67 |
68 |
69 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/todo/step04/article.html:
--------------------------------------------------------------------------------
1 | 双方向バインディングを理解しユーザーが入力した値を受け取りましょう。
2 | input 要素の値を受け取る
3 | 先ほどのステップで ToDo を作成することが出来るようになりました。
4 | しかし肝心の要件が仮値のままです。
5 | 本来なら要件はフォームの input 要素から取得せねばなりません。
6 | input 要素の値を表すモデルが存在すれば addTodo メソッドはユーザーが入力した値を参照できそうです。
7 | まず input 要素の値を表すモデルを作成します。
8 | $scope.newTitle = '' ;
9 | input 要素ははじめ値を持っている必要がないので初期値は空文字列です。
10 | newTitle モデルは input 要素の値と結びついてユーザーの入力に合わせて値を更新する必要があります。
11 | ngModel ディレクティブ を使用してモデルと要素を結びつけましょう。
12 | <form ng-submit ="addTodo()" >
13 | <input type ="text" required placeholder ="新しい要件を入力" .
14 | ng-model ="newTitle" >
15 | <button type ="submit" > 追加</button >
16 | </form >
17 | input 要素に対して ngModel ディレクティブの使用を宣言します。
18 | ngModel ディレクティブは要素の値に紐付けたいモデルを受け取ります。
19 | これで input 要素の値が変化した時 newTitle モデルの値も合わせて更新されるようになります。
20 | 最後に addTodo メソッド内で newTitle モデルを参照し新しく作成する ToDo の要件に代入されるようにしましょう。
21 | $scope.addTodo = function () {
22 | $scope.todos.push({
23 | title: $scope.newTitle,
24 | done: false
25 | });
26 |
27 | $scope.newTitle = '' ;
28 | };
29 | 新しい Todo が作成されたら input 要素の値はもう必要ないので newTitle モデルは初期値に戻しておきましょう。
30 | newTitle モデルに代入を行えば input 要素の値もそれに追従します。
31 | ついでなのでリスト内の ToDo の状態にも ngModel ディレクティブを使用して ToDo モデルの done プロパティと結びつけておきましょう。
32 | <li class ="todo-item" ng-repeat ="todo in todos" >
33 | <form >
34 | <input type ="checkbox" ng-model ="todo.done" >
35 | <span class ="todo-title" > {{ todo.title }} </span >
36 | <button type ="reset" > 削除</button >
37 | </form >
38 | </li >
39 | これでリスト内の ToDo モデルもチェックボックスの状態に合わせて更新されます。
40 | 完了状態の ToDo には done クラスがつくという仕様なので todo.done プロパティの値に合わせてクラスが付与されるようにしましょう。
41 | <li class ="todo-item" ng-repeat ="todo in todos"
42 | ng-class ="{done: todo.done}" >
43 | li 要素に対して ngClass ディレクティブ の使用を宣言します。
44 | ngClass ディレクティブは様々な形式の値を受けとれますが今回はマップオブジェクトを使用します。
45 | マップオブジェクトは {'クラス名': クラスを付与する条件式} の形式です。
46 | 上記の場合 todo.done プロパティが true なら done クラスが付与されるという意味になります。
47 |
48 |
49 |
--------------------------------------------------------------------------------
/account/step03/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 帳票アプリ
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
27 |
28 |
29 |
30 |
31 |
32 |
40 |
41 |
87 |
88 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/guide/service/article.html:
--------------------------------------------------------------------------------
1 | 役割
2 | サービスは以下の役割を果たすために使用されるものです。
3 |
4 | アプリケーション共通のオブジェクトを定義する。
5 |
6 | コントローラやディレクティブ、フィルタなどで共通して使われる値や関数を定義する場所です。
7 | 使う
8 | サービスを使用したいコントローラなどのアノテーション配列にサービス名を列挙します。
9 | module.controller('Ctrl' , ['$scope' , '$http' , '$timeout' , function (scope, $http, $timeout) {
10 | $http.get('/data' ).success(function (data) {
11 | scope.data = data;
12 | });
13 |
14 | $timeout(function () {
15 | scope.elapsed = true ;
16 | }, 1000 );
17 | }]);
18 | アノテーション配列の最後の関数がサービスを受け取ります。
19 | 作る
20 | module インスタンスの factory, service, value などのメソッドで新しいサービスを定義できます。
21 | いずれのメソッドも第一引数にサービスの名前を受け取ります。
22 | factory と service は第二引数にアノテーション配列を受け取ります。
23 | factory メソッドのアノテーション配列の最後はファクトリ関数です。
24 | ファクトリ関数が返すべきものはサービスとなるオブジェクトです。
25 | ファクトリ関数は作成したサービスがはじめて依存注入された時に一度だけ実行されます。
26 | 2回目以降の依存注入では初回で作成されたサービスが渡されます。
27 | module.factory('delayOneSecond' , ['$timeout' , function ($timeout) {
28 | return function (callback) {
29 | return $timeout(callbak, 1000 );
30 | };
31 | }]);
32 | service メソッドのアノテーション配列の最後はコンストラクタです。
33 | コンストラクタは作成したサービスがはじめて依存注入された時に一度だけインスタンス化されます。
34 | 2回目以降の依存注入では初回で作成されたインスタンスが渡されます。
35 | module.service('storage' , [function () {
36 | var storage = {};
37 |
38 | function getLange () {
39 | return Object.keys(storage).length;
40 | }
41 |
42 | this .length = 0 ;
43 |
44 | this .get = function (key) {
45 | return storage[key];
46 | };
47 |
48 | this .getAll = function () {
49 | return angular.copy(storage);
50 | };
51 |
52 | this .set = function (key, value) {
53 | storage[key] = value;
54 | this .length = getLange();
55 | };
56 |
57 | this .remove = function (key) {
58 | delete storage[key];
59 | this .length = getLange();
60 | };
61 | }]);
62 | value メソッドの第二引数はサービスそのものを受け取ります。
63 | module.value('data' , [
64 | {id: 1 , name: 'Alice' },
65 | {id: 2 , name: 'Bob' },
66 | {id: 3 , name: 'Charlie' }
67 | ]);
68 |
69 | Tip:
70 | config ブロックで設定を変更できるような高度なサービスを作成する方法として provider メソッドも提供されています。
71 |
72 |
73 |
74 | Tip:
75 | アプリケーション共通の定数定義を作成する手段として constant も提供されています。
76 | constant で作成されたサービスは config ブロックでも取得できます。
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/account/step05/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 帳票アプリ
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
27 |
28 |
29 |
30 |
31 |
32 |
40 |
41 |
90 |
91 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/guide/scope/article.md:
--------------------------------------------------------------------------------
1 | ## 役割
2 | $scope オブジェクトは以下の役割を果たすために使用されるものです。
3 |
4 | * ビューにバインドしたいデータモデルを定義する場所。
5 | * 自身のプロパティ(つまりデータモデル)に変更があった時ビューにそれを伝える。
6 | * ビューが式を評価する際のコンテキストの提供(つまりビューのグローバルスコープ)。
7 | * イベントの発行とイベントリスナーの登録
8 |
9 | ## コントローラやビューとの関係
10 | $scope はコントローラやディレクティブとビューの橋渡しを行います。
11 | ビューは $scope を参照しデータをバインドしていきます。
12 | コントローラやディレクティブがデータモデルを変更した時 $scope はその変更をビューに伝えてくれます。
13 | 変更を受け取ったビューは自動的に表示を更新します。
14 |
15 | この仕組のためコントローラーはビューを全く意識せずにビジネスロジックのみに集中できます。
16 |
17 | ## ビューへの変更通知
18 | AngularJS ではほとんどの場合においてビューへの変更通知を手動で行う必要がありません。
19 | 手動で変更通知を行わなければいけない場合の代表例は以下のとおりです。
20 |
21 | * ディレクティブを通さずにアタッチした DOM イベントのリスナー内
22 | * タイマーや XMLHttpRequest などの非同期処理のコールバック内
23 |
24 | 手動で更新通知を行うための手段として $scope.$apply メソッドが提供されています。
25 |
26 | ```javascript
27 | $scope.foo = false;
28 |
29 | setTimeout(function () {
30 | $scope.foo = true;
31 | $scope.$apply();
32 | }, 1000);
33 | // or
34 | setTimeout(function () {
35 | // こちらのほうがベター
36 | $scope.$apply(function () {
37 | $scope.foo = true;
38 | });
39 | }, 1000);
40 | ```
41 |
42 | タイマーは $timeout サービスを、 XMLHttpRequest は $http サービスを使うことで更新を自動化できます。
43 |
44 |
45 | **Tip:**
46 | デバッガや console API でスタックトレースを調べて $scope.$apply が呼び出されているかどうかを確認することで手動更新が必要かどうかを判別することができます。
47 |
48 |
49 | ## 階層構造と継承
50 | $scope は親子関係を持っており小スコープは親スコープのプロパティをプロトタイプ継承します。
51 |
52 | アプリケーションははじめ $rootScope だけを持っています。
53 | いくつかのディレクティブは 新規に $scope を作成するのでアプリケーションは複数の $scope を持つことになります。
54 | 新規の $scope は親 $scope の子となり DOM の構造に従ったツリー構造になります。
55 |
56 | ビューが式を評価する時、直近の $scope のプロパティに値が発見できなかったら $rootScope に到達するまで親スコープのプロパティをたどります。
57 |
58 | ## イベント
59 | $scope はイベントの発行とイベントリスナーの管理も行います。
60 | イベントはいくつかのディレクティブやサービスが $scope を使って発行したりします。
61 | もちろん自分でカスタムイベントを発行することもできます。
62 |
63 | ```javascript
64 | // イベントリスナーの登録
65 | $scope.$on('eventName', eventListener);
66 |
67 | // イベント発行
68 | // 親スコープに伝搬する
69 | $scope.$emit('eventName', passingData, ...);
70 |
71 | // 小スコープに伝搬する
72 | $scope.$broadcast('eventName', passingData, ...);
73 | ```
74 |
75 |
76 |
77 | ## 変更時にコールバックを実行する
78 | ビューに変更が通知される時、特定のプロパティの値が変わっていたら追加の処理を行ないたい場合があります。
79 |
80 | $scope.$watch メソッドを使うと値が変化していた時に任意の関数を実行する事ができます。
81 |
82 | ```javascript
83 | $scope.name = 'Alice';
84 | $scope.$watch('name', function (newValue, oldValue, scope) {
85 | console.log(scope === $scope);
86 | console.log('name は %s から %s に変更されました', oldValue, newValue);
87 | });
88 |
89 | $scope.name = 'Bob';
90 | $scope.$apply(); // 更新を通知
91 | // => true
92 | // => name は Alice から Bob に変更されました
93 | ```
94 |
95 | 第一引数に文字列を渡すと $scope のそのプロパティの変更を監視します。
96 | コールバックが実行されるのはビューに変更が通知される直前で監視中のプロパティの値が変更されていたときのみです。
97 |
98 | 第一引数に関数を渡すことで $scope オブジェクトのプロパティ以外の変更も監視できます。
99 |
100 | ```javascript
101 | var bool;
102 | $scope.$watch(function() {
103 | return bool;
104 | }, function (newValue, oldValue) {
105 | $scope.value = newValue;
106 | });
107 |
108 | bool = true;
109 | $scope.$apply();
110 | ```
111 |
112 | 注意点として変更とはプロパティへの代入だということです。
113 | 以下の例は期待通りに動きません。
114 |
115 | ```javascript
116 | $scope.arr = [1, 2, 3];
117 | $scope.$watch('arr', function (newValue, oldValue) {});
118 | $scope.arr.push(4);
119 |
120 | $scope.obj = {value1: 1};
121 | $scope.$watch('obj', function (newValue, oldValue) {});
122 | $scope.obj.value2 = 2;
123 |
124 | $scope.$apply();
125 | ```
126 |
127 | 上記例ではコールバックが受け取る newValue と oldValue は参照が同じなので `newValue === oldValue` が成り立ちます。
128 | つまりプロパティは変更されていないということになります。
129 |
130 | この変更を監視したい場合は $watchCollection メソッドを使います。
131 |
132 | ```javascript
133 | $scope.arr = [1, 2, 3];
134 | $scope.$watchCollection('arr', function (newValue, oldValue) {
135 | console.log(newValue);
136 | // => [1, 2, 3, 4]
137 | });
138 | $scope.arr.push(4);
139 |
140 | $scope.obj = {value1: 1};
141 | $scope.$watchCollection('obj', function (newValue, oldValue) {
142 | console.log(newValue);
143 | // => {value1: 1, value2: 2}
144 | });
145 | $scope.obj.value2 = 2;
146 |
147 | $scope.$apply();
148 | ```
149 |
150 | 配列の要素やオブジェクトのプロパティの値に変更があった場合を監視したい時は以下のようにします。
151 |
152 | ```javascript
153 | $scope.list = [
154 | {value: false}
155 | ];
156 |
157 | $scope.$watch('list', function (newValue, oldValue) {
158 | // 実行されない
159 | });
160 |
161 | $scope.$watch('list', function (newValue, oldValue) {
162 | // 実行される
163 | }, true); // 第三引数に true を渡す
164 |
165 | $scope.$watchCollection('list', function (newValue, oldValue) {
166 | // 実行されない
167 | });
168 |
169 | $scope.list[0].value = true;
170 | $scope.$apply();
171 | ```
172 |
173 | 第三引数を設定することで比較の仕方を参照の比較から各値を個別に比較するようになります。
174 |
175 |
176 |
--------------------------------------------------------------------------------
/account/step06/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 帳票アプリ
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
27 |
28 |
29 |
30 |
31 |
32 |
47 |
48 |
97 |
98 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/account/step09/article.md:
--------------------------------------------------------------------------------
1 | 前回のステップまでで帳票アプリは完成です。
2 | しかし改めて完成したコードを眺めると無駄な箇所があることがわかります。
3 |
4 | ## 複数のコントローラが全く同じ機能を別々に実装している
5 | getSubtotal メソッドと getTotalAmount メソッドが CreationController と SheetController の2箇所に存在しています。
6 | コードの重複は無駄ですし処理に変更があった際の修正箇所も増えてしまいます。
7 |
8 | これらのメソッドはサービスとして一箇所にまとめて実装しましょう。
9 |
10 | ```javascript
11 | .service('counting', function () {
12 | this.getSubtotal = function (orderLine) {/* 省略 */};
13 | this.getTotalAmount = function (lines) {/* 省略 */};
14 | })
15 | .controller('CreationController', ['$scope', 'sheets', 'counting',
16 | function CreationController($scope, sheets, counting) {
17 | // 省略
18 |
19 | angular.extend($scope, counting); // $scope オブジェクトに counting サービスメソッドをミックスイン
20 |
21 | $scope.initialize();
22 | }])
23 | .controller('SheetController', ['$scope', '$routeParams', 'sheets', 'counting',
24 | function SheetController($scope, $params, sheets, counting) {
25 | angular.extend($scope, sheets.get($params.id));
26 | angular.extend($scope, counting); // counting サービスで $scope オブジェクトを拡張
27 | }]);
28 | ```
29 |
30 | コントローラはサービスを受け取り $scope オブジェクトにミックスインしてビューから参照できるようにします。
31 |
32 | 副次的なメリットとしてコンスタクタがインスタンス化されるときに関数を作成するコストが少なくなりました。
33 |
34 | ## コントローラのインスタンス化のコストを減らす
35 | コントローラ関数はコンストラクタです。
36 | マルチビューアプリケーションではコントローラはルートが変わるたびにインスタンス化されます。
37 |
38 | コンストラクタ内で関数定義を行うとインスタンス化のたびに新しく関数を定義することになりインスタンス化の際にかかるコストが高くなります。
39 |
40 | 一方サービスは常にシングルトンであり、サービス内の関数定義はアプリケーション全体を通して一度だけしか行われません。
41 | 上記を踏まえた上で改めてコードを見れば新たに無駄な場所を発見できると思います。
42 |
43 | CreationController 内で定義されたビジネスロジックの定義です。
44 | これらもすべてサービスにカプセル化しコントローラ内ではミックスインするだけにしましょう。
45 |
46 | ```javascript
47 | .service('sheetAction', ['$location', 'sheets',
48 | function ($location, sheets) {
49 | function createOrderLine() {/* 省略 */}
50 |
51 | this.initialize = function () {/* 省略 */};
52 | this.addLine = function () {/* 省略 */};
53 | this.removeLine = function (target) {/* 省略 */};
54 | this.save = function () {/* 省略 */};
55 | }])
56 | .controller('CreationController', ['$scope', 'counting', 'sheetAction',
57 | function CreationController($scope, counting, sheetAction) {
58 | angular.extend($scope, sheetAction); // $scope オブジェクトに sheetAction サービスメソッドをミックスイン
59 | angular.extend($scope, counting);
60 |
61 | $scope.initialize();
62 | }])
63 | ```
64 |
65 | ## テンプレート内のわかりにくい部分をコントローラに移管する
66 | テンプレート内の ngPattern ディレクティブに渡す正規表現や削除ボタンの ngDisabled ディレクティブに渡す条件式などは HTML 上では少し把握しづらいものです。
67 | とりわけこのようなものはコメントを残しておかないと、あとで困った事になるケースも有り得ます(今回のケースでは単純な条件なのでまだ大丈夫だとは思いますが)。
68 |
69 | わかりにくい部分はモデル化してビューにはモデルの参照だけさせるようにしましょう。
70 |
71 | ```javascript
72 | $scope.integer = /^\d+$/; // 整数にマッチ
73 | ```
74 | ```html
75 |
76 | ```
77 |
78 | このほうがわかりやすいですね。
79 |
80 | ngDisabled ディレクティブに渡す条件はリストが更新されるたびに評価される必要があります。
81 | もっとも単純な例は下記のやり方です。
82 |
83 | ```javascript
84 | $scope.isDisabled = function () {
85 | retrun $scope.lines.length < 2;
86 | };
87 | ```
88 | ```html
89 | 削除
90 | ```
91 |
92 | しかしこのやり方は実はあまりいい実装ではありません。
93 | 例えば100個の注文明細行をもった帳票だった場合、個別の実行結果が変わらないにもかかわらず isDisabled メソッドは100回コールされることになります。
94 | これは恐ろしく無駄ですので他の方法で実装しましょう。
95 |
96 | ```javascript
97 | $scope.$watch('lines.length < 2', function (val) {
98 | $scope.disabledDelBtn = val;
99 | });
100 | ```
101 | ```html
102 | 削除
103 | ```
104 |
105 | [$watch](http://docs.angularjs.org/api/ng.$rootScope.Scope#$watch) メソッドを使いボタンを無効化する条件を監視しその結果をモデル化します。
106 | ディレクティブは関数実行ではなくモデルを参照するだけなので要素数が増えてもパフォーマンスに与える影響が少なくてすみます。
107 |
108 |
109 | **Warning:**
110 | 最初の状態と比べた場合、パフォーマンスが低下する可能性があるのでメンテナンス性とパフォーマンスを秤にかける必要があります。
111 |
112 |
113 | lines モデルや lines モデルの length プロパティを監視してもいいのですがその場合コールバック内で条件評価を行うことになります。
114 | それは結果的に disabledDelBtn モデルの値に変化がない場合でもコールバックが実行されるケースがあることを意味するので無駄が発生してしまいます。
115 |
116 | ```javascript
117 | $scope.$watch('lines.length', function (length) {
118 | // この関数は lenes.length が 4 から 5 に変化した時も呼ばれます
119 | // その条件では length < 2 の結果には変化がありません。
120 | $scope.disabledDelBtn = length < 2;
121 | });
122 | ```
123 | ```javascript
124 | $scope.$watch('lines.length < 2', function (val) {
125 | // この関数は lines.length < 2 の結果が変わった時だけ呼ばれます
126 | $scope.disabledDelBtn = val;
127 | });
128 | ```
129 |
130 | $watch を使う場合コールバック内の処理は簡潔なものにとどめましょう。
131 | 監視対象によっては開発者が思う以上にコールバックが呼ばれたり最悪の場合無限ループに陥るケースもありえます。
132 |
133 |
134 | **Tip:**
135 | AngularJS ではこのような無限ループ(digest ループ)はデフォルトで 10回繰り返した段階で強制的にループから抜けます。
136 |
137 |
138 |
139 |
140 | ## 完成
141 | 以上でチュートリアルは終了です。
142 |
--------------------------------------------------------------------------------
/assets/css/docs.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 70px;
3 | }
4 |
5 | hr {
6 | border-color: #ddd;
7 | }
8 |
9 | pre {
10 | font-size: 14px;
11 | }
12 |
13 | .main-top {
14 | margin: -20px 0 0;
15 | }
16 |
17 | .jumbotron {
18 | margin: 0;
19 | color: #fff;
20 | font-size: 16px;
21 | }
22 |
23 | .jumbotron h1,
24 | .jumbotron h2,
25 | .jumbotron h3 {
26 | line-height: 1.1;
27 | }
28 |
29 | .jumbotron p {
30 | color: #eee;
31 | }
32 |
33 | .jumbotron section {
34 | margin-top: 32px;
35 | }
36 |
37 | @media screen and (min-width: 768px) {
38 | @font-face {
39 | font-family: mplus;
40 | font-weight: 100;
41 | src: local('M+ 1p'),
42 | url('../fonts/mplus-1p-thin.ttf') format('truetype');
43 | }
44 |
45 | .jumbotron {
46 | font-family: mplus, 'Helvetica Neue', Helvetica, Arial, sans-serif;
47 | font-size: 21px;
48 | }
49 |
50 | .jumbotron h1,
51 | .jumbotron h2,
52 | .jumbotron h3 {
53 | font-family: mplus, 'Helvetica Neue', Helvetica, Arial, sans-serif;
54 | line-height: 1.1;
55 | }
56 |
57 | .jumbotron h2 {
58 | font-size: 49px;
59 | }
60 |
61 | .jumbotron h3 {
62 | font-size: 32px;
63 | }
64 | }
65 |
66 | .jumbotron-blue {
67 | background-color: #2980b9;
68 | }
69 |
70 | .jumbotron-sky {
71 | background-color: #3498db;
72 | }
73 |
74 | .alert {
75 | font-size: 14px;
76 | }
77 |
78 | a.list-group-item:hover,
79 | a.list-group-item:focus {
80 | z-index: 2;
81 | color: #ffffff;
82 | background-color: #428bca;
83 | border-color: #428bca;
84 | }
85 |
86 | .tab-content-code {
87 | margin-top: 18px;
88 | }
89 |
90 | .aside {
91 | margin-top: 40px;
92 | }
93 |
94 | .pager li > a {
95 | border-radius: 3px;
96 | }
97 |
98 | .pager .next > a {
99 | color: #fff;
100 | background-color: #428bca;
101 | border-color: #357ebd;
102 | }
103 |
104 | .pager .next > a:hover,
105 | .pager .next > a:focus,
106 | .pager .next > a:active {
107 | color: #fff;
108 | background-color: #3276b1;
109 | border-color: #285e8e;
110 | }
111 |
112 | .preview {
113 | width: 100%;
114 | min-height: 500px;
115 | border: 1px solid #ccc;
116 | border-radius: 4px;
117 | resize: vertical;
118 | }
119 |
120 | .footer {
121 | border-top: 1px solid #e5e5e5;
122 | padding: 40px 0;
123 | color: #999;
124 | }
125 |
126 | .footer-link {
127 | padding: 0;
128 | text-align: center;
129 | }
130 |
131 | .footer-link li {
132 | display: inline;
133 | }
134 |
135 | /*! Highlight.js theme */
136 | /*! github.com style (c) Vasily Polovnyov */
137 | pre .comment,
138 | pre .template_comment,
139 | pre .diff .header,
140 | pre .javadoc {
141 | color: #998;
142 | /* fixed by 8th713 */
143 | /* disabled the font-style because Italics is hard to read in Japanese. */
144 | /* font-style: italic */
145 | }
146 |
147 | pre .keyword,
148 | pre .css .rule .keyword,
149 | pre .winutils,
150 | pre .javascript .title,
151 | pre .nginx .title,
152 | pre .subst,
153 | pre .request,
154 | pre .status {
155 | color: #333;
156 | font-weight: bold
157 | }
158 |
159 | pre .number,
160 | pre .hexcolor,
161 | pre .ruby .constant {
162 | color: #099;
163 | }
164 |
165 | pre .string,
166 | pre .tag .value,
167 | pre .phpdoc,
168 | pre .tex .formula {
169 | color: #d14
170 | }
171 |
172 | pre .title,
173 | pre .id {
174 | color: #900;
175 | font-weight: bold
176 | }
177 |
178 | pre .javascript .title,
179 | pre .lisp .title,
180 | pre .clojure .title,
181 | pre .subst {
182 | font-weight: normal
183 | }
184 |
185 | pre .class .title,
186 | pre .haskell .type,
187 | pre .vhdl .literal,
188 | pre .tex .command {
189 | color: #458;
190 | font-weight: bold
191 | }
192 |
193 | pre .tag,
194 | pre .tag .title,
195 | pre .rules .property,
196 | pre .django .tag .keyword {
197 | color: #000080;
198 | font-weight: normal
199 | }
200 |
201 | pre .attribute,
202 | pre .variable,
203 | pre .lisp .body {
204 | color: #008080
205 | }
206 |
207 | pre .regexp {
208 | color: #009926
209 | }
210 |
211 | pre .class {
212 | color: #458;
213 | font-weight: bold
214 | }
215 |
216 | pre .symbol,
217 | pre .ruby .symbol .string,
218 | pre .lisp .keyword,
219 | pre .tex .special,
220 | pre .prompt {
221 | color: #990073
222 | }
223 |
224 | pre .built_in,
225 | pre .lisp .title,
226 | pre .clojure .built_in {
227 | color: #0086b3
228 | }
229 |
230 | pre .preprocessor,
231 | pre .pi,
232 | pre .doctype,
233 | pre .shebang,
234 | pre .cdata {
235 | color: #999;
236 | font-weight: bold
237 | }
238 |
239 | pre .deletion {
240 | background: #fdd
241 | }
242 |
243 | pre .addition {
244 | background: #dfd
245 | }
246 |
247 | pre .diff .change {
248 | background: #0086b3
249 | }
250 |
251 | pre .chunk {
252 | color: #aaa
253 | }
254 |
--------------------------------------------------------------------------------
/todo/step06/article.html:
--------------------------------------------------------------------------------
1 | $watch メソッドを使ってモデルの変更時の振る舞いを追加しましょう。
2 | ToDo リストモデルの変更を監視する
3 | 絞り込み表示ができるようになりましたがボタンに表示する件数が仮値のままです。
4 | 正しい件数を表示出来るようにしましょう。
5 | 未了、完了の件数はリスト内の Todo モデルの総数や状態の変更に合わせて更新する必要があります。
6 | $scope.$watch メソッドを使ってリストが変更された時に正しい件数に更新されるモデルを作成しましょう。
7 | $scope.$watch('todos' , function (todos) {
8 |
9 | }, true );
10 | 配列の増減と配列内の ToDo の done プロパティを監視するため第三引数(objectEquality フラグ)を true に設定します。
11 | これを忘れると関数が実行されるのが todos に新しい値が代入された時だけになってしまいますので注意してください。
12 | コントローラからフィルタを使用する
13 | 未了、完了の件数を得るためにはリストから合致する要素だけを抽出する必要があります。
14 | この処理は先ほどのステップで使用したコアモジュールの filter フィルタが実現していたものと同じです。
15 | コントローラで特定のフィルタを使用したい場合 $filter サービス を使ってフィルタを取得することができます。
16 | アノテーション配列を編集しコンストラクタが $filter サービスを受け取れるようにしましょう。
17 | .controller('MainController' , ['$scope' , '$filter' , function ($scope, $filter) {
18 |
19 |
20 | var where = $filter('filter' );
21 | $scope.$watch('todos' , function (todos) {
22 | var length = todos.length;
23 |
24 | $scope.allCount = length;
25 | $scope.doneCount = where(todos, $scope.filter.done).length;
26 | $scope.remainingCount = length - $scope.doneCount;
27 | }, true );
28 | }]);
29 | filter 関数(変数 where)を利用して完了した ToDo だけの配列を取得しその長さを完了件数モデルとしました。
30 | さらに、総件数から完了件数を引いた値を利用して未了件数モデルに代入します。
31 | $scope.$watch に登録された関数はコントローラがインスタンス化された時初期化処理として一度実行されるので関数外で各モデルの初期値を定義しておく必要はありません。
32 | 最後にモックアップを編集して各モデルを表示しましょう。
33 | <button class ="active" > 全部 <span > {{ allCount }} </span > </button >
34 | <button > 未了 <span > {{ remainingCount }} </span > </button >
35 | <button > 完了 <span > {{ doneCount }} </span > </button >
36 |
37 |
38 | $watch メソッドを使用しない実装方法
39 | 慣れないうちは $watch は少々扱いづらいので別のアプローチでの実装法も紹介します。
40 | $scope.getDoneCount = function () {
41 | return where($scope.todos, $scope.filter.done).length;
42 | };
43 | <button class ="active" > 全部 <span > {{ todos.length }} </span > </button >
44 | <button > 未了 <span > {{ todos.length - getDoneCount() }} </span > </button >
45 | <button > 完了 <span > {{ getDoneCount() }} </span > </button >
46 | ビューは $scope からの更新通知を受け取ると式を再評価するためこの実装は結果的に $watch メソッドを使った実装と
47 | 同じことになります。
48 | ただし $watch を使った実装ならば再評価時の動作は単純なプロパティ参照で済むのに対し、この実装は再評価時に必ず関数を実行するため多用するとパフォーマンスが低下する可能性があります。
49 |
50 |
--------------------------------------------------------------------------------