├── Chapter_01 ├── README.md ├── pubspec.yaml ├── web │ ├── main.dart │ └── index.html └── pubspec.lock ├── Chapter_02 ├── web │ ├── style.css │ ├── main.dart │ └── index.html ├── README.md ├── pubspec.yaml ├── test │ ├── karma.conf.js │ └── main_test.dart ├── lib │ └── component │ │ ├── recipe_book.html │ │ └── recipe_book.dart └── pubspec.lock ├── Chapter_06 ├── lib │ ├── component │ │ ├── view_recipe.css │ │ ├── rating.css │ │ ├── rating.html │ │ ├── search_recipe.html │ │ ├── view_recipe.dart │ │ ├── search_recipe.dart │ │ ├── view_recipe.html │ │ ├── recipe_book.html │ │ ├── rating.dart │ │ └── recipe_book.dart │ ├── formatter │ │ └── category_filter.dart │ ├── service │ │ ├── recipe.dart │ │ └── query.dart │ ├── routing │ │ └── recipe_book_router.dart │ └── tooltip │ │ └── tooltip.dart ├── README.md ├── web │ ├── view │ │ ├── viewRecipe.html │ │ ├── addRecipe.html │ │ └── editRecipe.html │ ├── fonzie1.jpg │ ├── fonzie2.jpg │ ├── categories.json │ ├── style.css │ ├── index.html │ ├── main.dart │ └── recipes.json ├── pubspec.yaml ├── test │ ├── karma.conf.js │ └── main_test.dart ├── bin │ └── server_simple_get_and_put.dart └── pubspec.lock ├── .gitignore ├── img ├── ch01-1.png ├── ch01-2.png ├── ch02-1.png └── ch02-2.png ├── Chapter_04 ├── README.md ├── web │ ├── fonzie1.jpg │ ├── fonzie2.jpg │ ├── style.css │ ├── index.html │ └── main.dart ├── lib │ ├── component │ │ ├── rating.css │ │ ├── rating.html │ │ ├── recipe_book.html │ │ ├── rating.dart │ │ └── recipe_book.dart │ └── tooltip │ │ └── tooltip.dart ├── pubspec.yaml ├── test │ ├── karma.conf.js │ └── main_test.dart └── pubspec.lock ├── Chapter_03 ├── README.md ├── web │ ├── style.css │ ├── main.dart │ └── index.html ├── lib │ └── component │ │ ├── rating.css │ │ ├── rating.html │ │ ├── recipe_book.html │ │ ├── recipe_book.dart │ │ └── rating.dart ├── pubspec.yaml ├── test │ ├── karma.conf.js │ └── main_test.dart └── pubspec.lock ├── Chapter_05 ├── README.md ├── web │ ├── fonzie1.jpg │ ├── fonzie2.jpg │ ├── categories.json │ ├── style.css │ ├── index.html │ ├── main.dart │ └── recipes.json ├── lib │ ├── component │ │ ├── rating.css │ │ ├── rating.html │ │ ├── recipe_book.html │ │ ├── rating.dart │ │ └── recipe_book.dart │ ├── formatter │ │ └── category_filter.dart │ ├── recipe.dart │ └── tooltip │ │ └── tooltip.dart ├── pubspec.yaml ├── test │ ├── karma.conf.js │ └── main_test.dart └── pubspec.lock ├── README.md ├── package.json └── LICENSE /Chapter_01/README.md: -------------------------------------------------------------------------------- 1 | "Hello World" AngularDart application 2 | -------------------------------------------------------------------------------- /Chapter_02/web/style.css: -------------------------------------------------------------------------------- 1 | .pointer { 2 | cursor: pointer; 3 | } -------------------------------------------------------------------------------- /Chapter_06/lib/component/view_recipe.css: -------------------------------------------------------------------------------- 1 | ul { 2 | list-style-type: none; 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | packages 3 | .project 4 | .idea 5 | node_modules 6 | build 7 | .pub 8 | -------------------------------------------------------------------------------- /img/ch01-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dart-archive/angular.dart.tutorial/master/img/ch01-1.png -------------------------------------------------------------------------------- /img/ch01-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dart-archive/angular.dart.tutorial/master/img/ch01-2.png -------------------------------------------------------------------------------- /img/ch02-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dart-archive/angular.dart.tutorial/master/img/ch02-1.png -------------------------------------------------------------------------------- /img/ch02-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dart-archive/angular.dart.tutorial/master/img/ch02-2.png -------------------------------------------------------------------------------- /Chapter_04/README.md: -------------------------------------------------------------------------------- 1 | Recipe Book AngularDart application 2 | Illustrates the use of an Angular directive 3 | -------------------------------------------------------------------------------- /Chapter_03/README.md: -------------------------------------------------------------------------------- 1 | Recipe Book AngularDart application 2 | Illustrates the use of an Angular components 3 | -------------------------------------------------------------------------------- /Chapter_05/README.md: -------------------------------------------------------------------------------- 1 | Recipe Book AngularDart application 2 | Illustrates the use of formatters and services 3 | -------------------------------------------------------------------------------- /Chapter_06/README.md: -------------------------------------------------------------------------------- 1 | Recipe Book AngularDart application 2 | Illustrates the use of routing to create views 3 | -------------------------------------------------------------------------------- /Chapter_06/web/view/viewRecipe.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /Chapter_02/README.md: -------------------------------------------------------------------------------- 1 | Recipe Book AngularDart application 2 | Illustrates how to create a basic Angular component 3 | -------------------------------------------------------------------------------- /Chapter_03/web/style.css: -------------------------------------------------------------------------------- 1 | .pointer { 2 | cursor: pointer; 3 | } 4 | 5 | .extra-space { 6 | padding-left: 10px; 7 | } 8 | -------------------------------------------------------------------------------- /Chapter_04/web/fonzie1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dart-archive/angular.dart.tutorial/master/Chapter_04/web/fonzie1.jpg -------------------------------------------------------------------------------- /Chapter_04/web/fonzie2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dart-archive/angular.dart.tutorial/master/Chapter_04/web/fonzie2.jpg -------------------------------------------------------------------------------- /Chapter_04/web/style.css: -------------------------------------------------------------------------------- 1 | .pointer { 2 | cursor: pointer; 3 | } 4 | 5 | .extra-space { 6 | padding-left: 10px; 7 | } 8 | -------------------------------------------------------------------------------- /Chapter_05/web/fonzie1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dart-archive/angular.dart.tutorial/master/Chapter_05/web/fonzie1.jpg -------------------------------------------------------------------------------- /Chapter_05/web/fonzie2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dart-archive/angular.dart.tutorial/master/Chapter_05/web/fonzie2.jpg -------------------------------------------------------------------------------- /Chapter_06/web/fonzie1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dart-archive/angular.dart.tutorial/master/Chapter_06/web/fonzie1.jpg -------------------------------------------------------------------------------- /Chapter_06/web/fonzie2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dart-archive/angular.dart.tutorial/master/Chapter_06/web/fonzie2.jpg -------------------------------------------------------------------------------- /Chapter_06/web/view/addRecipe.html: -------------------------------------------------------------------------------- 1 |
2 |

Add recipe

3 | Now it's your turn. Write some code to add a new recipe. 4 |
5 | -------------------------------------------------------------------------------- /Chapter_06/web/view/editRecipe.html: -------------------------------------------------------------------------------- 1 |
2 |

Edit recipe

3 | Now it's your turn. Write some code to edit the recipe. 4 |
5 | -------------------------------------------------------------------------------- /Chapter_05/web/categories.json: -------------------------------------------------------------------------------- 1 | [ 2 | "Appetizers", 3 | "Salads", 4 | "Soups", 5 | "Main Dishes", 6 | "Side Dishes", 7 | "Desserts" 8 | ] 9 | -------------------------------------------------------------------------------- /Chapter_06/web/categories.json: -------------------------------------------------------------------------------- 1 | [ 2 | "Appetizers", 3 | "Salads", 4 | "Soups", 5 | "Main Dishes", 6 | "Side Dishes", 7 | "Desserts" 8 | ] 9 | -------------------------------------------------------------------------------- /Chapter_01/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: tutorial 2 | version: 1.0.0 3 | dependencies: 4 | angular: "1.1.0" 5 | web_components: "0.10.1" 6 | browser: ">=0.10.0+2 <0.11.0" 7 | transformers: 8 | - angular 9 | -------------------------------------------------------------------------------- /Chapter_03/lib/component/rating.css: -------------------------------------------------------------------------------- 1 | .star-off { 2 | color: #6E6E6E; 3 | } 4 | .star-on { 5 | color: #FACC2E; 6 | } 7 | .stars { 8 | letter-spacing: -2px; 9 | cursor: pointer; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter_04/lib/component/rating.css: -------------------------------------------------------------------------------- 1 | .star-off { 2 | color: #6E6E6E; 3 | } 4 | .star-on { 5 | color: #FACC2E; 6 | } 7 | .stars { 8 | letter-spacing: -2px; 9 | cursor: pointer; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter_05/lib/component/rating.css: -------------------------------------------------------------------------------- 1 | .star-off { 2 | color: #6E6E6E; 3 | } 4 | .star-on { 5 | color: #FACC2E; 6 | } 7 | .stars { 8 | letter-spacing: -2px; 9 | cursor: pointer; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter_06/lib/component/rating.css: -------------------------------------------------------------------------------- 1 | .star-off { 2 | color: #6E6E6E; 3 | } 4 | .star-on { 5 | color: #FACC2E; 6 | } 7 | .stars { 8 | letter-spacing: -2px; 9 | cursor: pointer; 10 | } 11 | -------------------------------------------------------------------------------- /Chapter_03/lib/component/rating.html: -------------------------------------------------------------------------------- 1 | 5 | {{starChar(star)}} 6 | 7 | -------------------------------------------------------------------------------- /Chapter_04/lib/component/rating.html: -------------------------------------------------------------------------------- 1 | 5 | {{starChar(star)}} 6 | 7 | -------------------------------------------------------------------------------- /Chapter_05/lib/component/rating.html: -------------------------------------------------------------------------------- 1 | 5 | {{starChar(star)}} 6 | 7 | -------------------------------------------------------------------------------- /Chapter_06/lib/component/rating.html: -------------------------------------------------------------------------------- 1 | 5 | {{starChar(star)}} 6 | 7 | -------------------------------------------------------------------------------- /Chapter_05/web/style.css: -------------------------------------------------------------------------------- 1 | .pointer { 2 | cursor: pointer; 3 | } 4 | 5 | .extra-space { 6 | padding-left: 10px; 7 | } 8 | 9 | [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { 10 | display: none !important; 11 | } 12 | 13 | ul { 14 | list-style-type: none; 15 | } -------------------------------------------------------------------------------- /Chapter_06/web/style.css: -------------------------------------------------------------------------------- 1 | a { 2 | text-decoration: none; 3 | } 4 | 5 | .extra-space { 6 | padding-left: 10px; 7 | } 8 | 9 | [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { 10 | display: none !important; 11 | } 12 | 13 | ul { 14 | list-style-type: none; 15 | } 16 | -------------------------------------------------------------------------------- /Chapter_01/web/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:angular/application_factory.dart'; 2 | import 'package:di/annotations.dart'; 3 | 4 | @Injectable() 5 | class Greeter { 6 | String name; 7 | } 8 | 9 | void main() { 10 | applicationFactory() 11 | .rootContextType(Greeter) 12 | .run(); 13 | } 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repo contains the examples from the archived [AngularDart 1 Tutorial][tut]. 2 | 3 | For the latest AngularDart examples, check out the repos under 4 | [github.com/angular-examples](https://github.com/angular-examples). 5 | 6 | [tut]: https://web.archive.org/web/20160304145052/https://angulardart.org/tutorial/ 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular.dart", 3 | "dependencies": { 4 | "karma": "^0.11.14", 5 | "karma-dart": "^0.2.6", 6 | "karma-chrome-launcher": "*", 7 | "karma-firefox-launcher": "*", 8 | "karma-junit-reporter": "*" 9 | }, 10 | "devDependencies": { 11 | "karma-chrome-launcher": "^0.1.3" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter_02/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: tutorial 2 | version: 1.0.0 3 | dependencies: 4 | angular: "1.1.0" 5 | web_components: "0.10.1" 6 | browser: ">=0.10.0+2 <0.11.0" 7 | dev_dependencies: 8 | unittest: ">=0.11.0+5 <0.12.0" 9 | mock: ">=0.11.0+2 <0.12.0" 10 | transformers: 11 | - angular: 12 | html_files: 13 | - lib/component/recipe_book.html 14 | 15 | -------------------------------------------------------------------------------- /Chapter_04/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: tutorial 2 | version: 1.0.0 3 | dependencies: 4 | angular: "1.1.0" 5 | web_components: "0.10.1" 6 | browser: ">=0.10.0+2 <0.11.0" 7 | dev_dependencies: 8 | unittest: ">=0.11.0+5 <0.12.0" 9 | mock: ">=0.11.0+2 <0.12.0" 10 | transformers: 11 | - angular: 12 | html_files: 13 | - lib/component/recipe_book.html 14 | - lib/component/rating.html 15 | -------------------------------------------------------------------------------- /Chapter_05/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: tutorial 2 | version: 1.0.0 3 | dependencies: 4 | angular: "1.1.0" 5 | web_components: "0.10.1" 6 | browser: ">=0.10.0+2 <0.11.0" 7 | dev_dependencies: 8 | unittest: ">=0.11.0+5 <0.12.0" 9 | mock: ">=0.11.0+2 <0.12.0" 10 | transformers: 11 | - angular: 12 | html_files: 13 | - lib/component/recipe_book.html 14 | - lib/component/rating.html 15 | -------------------------------------------------------------------------------- /Chapter_03/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: tutorial 2 | version: 1.0.0 3 | dependencies: 4 | angular: "1.1.0" 5 | web_components: "0.10.1" 6 | browser: ">=0.10.0+2 <0.11.0" 7 | dev_dependencies: 8 | unittest: ">=0.11.0+5 <0.12.0" 9 | mock: ">=0.11.0+2 <0.12.0" 10 | transformers: 11 | - angular: 12 | html_files: 13 | - lib/component/recipe_book.html 14 | - lib/component/rating.html 15 | 16 | -------------------------------------------------------------------------------- /Chapter_02/web/main.dart: -------------------------------------------------------------------------------- 1 | library recipe_book; 2 | 3 | import 'package:angular/angular.dart'; 4 | import 'package:angular/application_factory.dart'; 5 | import 'package:tutorial/component/recipe_book.dart'; 6 | 7 | class MyAppModule extends Module { 8 | MyAppModule() { 9 | bind(RecipeBookComponent); 10 | } 11 | } 12 | 13 | void main() { 14 | applicationFactory() 15 | .addModule(new MyAppModule()) 16 | .run(); 17 | } 18 | -------------------------------------------------------------------------------- /Chapter_06/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: tutorial 2 | version: 1.0.0 3 | dependencies: 4 | angular: "1.1.0" 5 | web_components: "0.10.1" 6 | browser: any 7 | http_server: ">=0.9.3 <0.10.0" 8 | dev_dependencies: 9 | unittest: ">=0.11.0+5 <0.12.0" 10 | mock: ">=0.11.0+2 <0.12.0" 11 | transformers: 12 | - angular: 13 | html_files: 14 | - lib/component/recipe_book.html 15 | - lib/component/rating.html 16 | - lib/component/search_recipe.html 17 | - lib/component/view_recipe.html 18 | -------------------------------------------------------------------------------- /Chapter_03/web/main.dart: -------------------------------------------------------------------------------- 1 | library recipe_book; 2 | 3 | import 'package:angular/angular.dart'; 4 | import 'package:angular/application_factory.dart'; 5 | 6 | import 'package:tutorial/component/rating.dart'; 7 | import 'package:tutorial/component/recipe_book.dart'; 8 | 9 | class MyAppModule extends Module { 10 | MyAppModule() { 11 | bind(RecipeBookComponent); 12 | bind(RatingComponent); 13 | } 14 | } 15 | 16 | void main() { 17 | applicationFactory() 18 | .addModule(new MyAppModule()) 19 | .run(); 20 | } 21 | -------------------------------------------------------------------------------- /Chapter_01/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chapter One - Hello, World! 5 | 6 | 7 | 8 | 9 |

Hello {{name}}!

10 | Name: 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Chapter_05/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chapter Five - A Simple Recipe Book 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Chapter_02/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chapter Two - A Simple Recipe Book 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Chapter_03/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chapter Three - A Simple Recipe Book 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Chapter_04/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chapter Four - A Simple Recipe Book 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Chapter_06/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chapter Six - A Simple Recipe Book 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Chapter_02/test/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | basePath: '..', 4 | frameworks: ['dart-unittest'], 5 | 6 | // list of files / patterns to load in the browser 7 | files: [ 8 | 'test/main_test.dart', 9 | {pattern: '**/*.dart', watched: false, included: false, served: true}, 10 | {pattern: 'packages/browser/dart.js', watched: false, included: true, served: true}, 11 | ], 12 | 13 | autoWatch: true, 14 | 15 | plugins: [ 16 | 'karma-dart', 17 | 'karma-chrome-launcher' 18 | ] 19 | }); 20 | }; 21 | -------------------------------------------------------------------------------- /Chapter_03/test/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | basePath: '..', 4 | frameworks: ['dart-unittest'], 5 | 6 | // list of files / patterns to load in the browser 7 | files: [ 8 | 'test/main_test.dart', 9 | {pattern: '**/*.dart', watched: false, included: false, served: true}, 10 | {pattern: 'packages/browser/dart.js', watched: false, included: true, served: true}, 11 | ], 12 | 13 | autoWatch: true, 14 | 15 | plugins: [ 16 | 'karma-dart', 17 | 'karma-chrome-launcher' 18 | ] 19 | }); 20 | }; 21 | -------------------------------------------------------------------------------- /Chapter_04/test/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | basePath: '..', 4 | frameworks: ['dart-unittest'], 5 | 6 | // list of files / patterns to load in the browser 7 | files: [ 8 | 'test/main_test.dart', 9 | {pattern: '**/*.dart', watched: false, included: false, served: true}, 10 | {pattern: 'packages/browser/dart.js', watched: false, included: true, served: true}, 11 | ], 12 | 13 | autoWatch: true, 14 | 15 | plugins: [ 16 | 'karma-dart', 17 | 'karma-chrome-launcher' 18 | ] 19 | }); 20 | }; 21 | -------------------------------------------------------------------------------- /Chapter_05/test/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | basePath: '..', 4 | frameworks: ['dart-unittest'], 5 | 6 | // list of files / patterns to load in the browser 7 | files: [ 8 | 'test/main_test.dart', 9 | {pattern: '**/*.dart', watched: false, included: false, served: true}, 10 | {pattern: 'packages/browser/dart.js', watched: false, included: true, served: true}, 11 | ], 12 | 13 | autoWatch: true, 14 | 15 | plugins: [ 16 | 'karma-dart', 17 | 'karma-chrome-launcher' 18 | ] 19 | }); 20 | }; 21 | -------------------------------------------------------------------------------- /Chapter_06/test/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | basePath: '..', 4 | frameworks: ['dart-unittest'], 5 | 6 | // list of files / patterns to load in the browser 7 | files: [ 8 | 'test/main_test.dart', 9 | {pattern: '**/*.dart', watched: false, included: false, served: true}, 10 | {pattern: 'packages/browser/dart.js', watched: false, included: true, served: true}, 11 | ], 12 | 13 | autoWatch: true, 14 | 15 | plugins: [ 16 | 'karma-dart', 17 | 'karma-chrome-launcher' 18 | ] 19 | }); 20 | }; 21 | -------------------------------------------------------------------------------- /Chapter_04/web/main.dart: -------------------------------------------------------------------------------- 1 | library recipe_book; 2 | 3 | import 'package:angular/angular.dart'; 4 | import 'package:angular/application_factory.dart'; 5 | 6 | import 'package:tutorial/component/recipe_book.dart'; 7 | import 'package:tutorial/component/rating.dart'; 8 | import 'package:tutorial/tooltip/tooltip.dart'; 9 | 10 | class MyAppModule extends Module { 11 | MyAppModule() { 12 | bind(RecipeBookComponent); 13 | bind(RatingComponent); 14 | bind(Tooltip); 15 | } 16 | } 17 | 18 | void main() { 19 | applicationFactory() 20 | .addModule(new MyAppModule()) 21 | .run(); 22 | } 23 | -------------------------------------------------------------------------------- /Chapter_06/lib/component/search_recipe.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 7 |
8 |
9 | Filter recipes by category: 10 | 11 | 14 | 15 |
16 | 17 |
18 | -------------------------------------------------------------------------------- /Chapter_06/lib/formatter/category_filter.dart: -------------------------------------------------------------------------------- 1 | library category_filter; 2 | 3 | import 'package:angular/angular.dart'; 4 | 5 | @Formatter(name: 'categoryfilter') 6 | class CategoryFilter { 7 | List call(recipeList, filterMap) { 8 | if (recipeList is Iterable && filterMap is Map) { 9 | // If there is nothing checked, treat it as "everything is checked" 10 | bool nothingChecked = filterMap.values.every((isChecked) => !isChecked); 11 | return nothingChecked 12 | ? recipeList 13 | : recipeList.where((i) => filterMap[i.category] == true).toList(); 14 | } 15 | return const []; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter_05/lib/formatter/category_filter.dart: -------------------------------------------------------------------------------- 1 | library category_filter; 2 | 3 | import 'package:angular/angular.dart'; 4 | 5 | @Formatter(name: 'categoryfilter') 6 | class CategoryFilter { 7 | List call(recipeList, filterMap) { 8 | if (recipeList is Iterable && filterMap is Map) { 9 | // If there is nothing checked, treat it as "everything is checked" 10 | bool nothingChecked = filterMap.values.every((isChecked) => !isChecked); 11 | return nothingChecked 12 | ? recipeList.toList() 13 | : recipeList.where((i) => filterMap[i.category] == true).toList(); 14 | } 15 | return const []; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter_06/lib/component/view_recipe.dart: -------------------------------------------------------------------------------- 1 | library view_recipe_component; 2 | 3 | import 'package:tutorial/service/recipe.dart'; 4 | import 'package:angular/angular.dart'; 5 | 6 | @Component( 7 | selector: 'view-recipe', 8 | templateUrl: 'view_recipe.html', 9 | cssUrl: 'view_recipe.css') 10 | class ViewRecipeComponent { 11 | @NgOneWay('recipe-map') 12 | Map recipeMap; 13 | 14 | String _recipeId; 15 | 16 | Recipe get recipe => recipeMap == null ? null : recipeMap[_recipeId]; 17 | 18 | ViewRecipeComponent(RouteProvider routeProvider) { 19 | _recipeId = routeProvider.parameters['recipeId']; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Chapter_02/lib/component/recipe_book.html: -------------------------------------------------------------------------------- 1 |

Recipe List

2 | 9 | 10 |

Recipe Details

11 |
Name: {{selectedRecipe.name}}
12 |
Category: {{selectedRecipe.category}}
13 |
Rating: {{selectedRecipe.rating}}
14 |
15 | 20 |
21 |
Directions: {{selectedRecipe.directions}}
22 | -------------------------------------------------------------------------------- /Chapter_05/web/main.dart: -------------------------------------------------------------------------------- 1 | library recipe_book; 2 | 3 | import 'package:angular/angular.dart'; 4 | import 'package:angular/application_factory.dart'; 5 | 6 | import 'package:tutorial/component/recipe_book.dart'; 7 | import 'package:tutorial/component/rating.dart'; 8 | import 'package:tutorial/formatter/category_filter.dart'; 9 | import 'package:tutorial/tooltip/tooltip.dart'; 10 | 11 | class MyAppModule extends Module { 12 | MyAppModule() { 13 | bind(RecipeBookComponent); 14 | bind(RatingComponent); 15 | bind(Tooltip); 16 | bind(CategoryFilter); 17 | } 18 | } 19 | 20 | void main() { 21 | applicationFactory() 22 | .addModule(new MyAppModule()) 23 | .run(); 24 | } 25 | -------------------------------------------------------------------------------- /Chapter_05/lib/recipe.dart: -------------------------------------------------------------------------------- 1 | library recipe; 2 | 3 | class Recipe { 4 | String id; 5 | String name; 6 | String category; 7 | List ingredients; 8 | String directions; 9 | String imgUrl; 10 | int rating; 11 | 12 | Recipe(this.id, this.name, this.category, this.ingredients, this.directions, this.rating, 13 | this.imgUrl); 14 | 15 | Map toJson() => { 16 | "id": id, 17 | "name": name, 18 | "category": category, 19 | "ingredients": ingredients, 20 | "directions": directions, 21 | "rating": rating, 22 | "imgUrl": imgUrl 23 | }; 24 | 25 | Recipe.fromJson(Map json): this(json['id'], json['name'], json['category'], 26 | json['ingredients'], json['directions'], json['rating'], json['imgUrl']); 27 | } 28 | -------------------------------------------------------------------------------- /Chapter_06/lib/service/recipe.dart: -------------------------------------------------------------------------------- 1 | library recipe; 2 | 3 | class Recipe { 4 | String id; 5 | String name; 6 | String category; 7 | List ingredients; 8 | String directions; 9 | String imgUrl; 10 | int rating; 11 | 12 | Recipe(this.id, this.name, this.category, this.ingredients, this.directions, this.rating, 13 | this.imgUrl); 14 | 15 | Map toJson() => { 16 | "id": id, 17 | "name": name, 18 | "category": category, 19 | "ingredients": ingredients, 20 | "directions": directions, 21 | "rating": rating, 22 | "imgUrl": imgUrl 23 | }; 24 | 25 | Recipe.fromJson(Map json): this(json['id'], json['name'], json['category'], 26 | json['ingredients'], json['directions'], json['rating'], json['imgUrl']); 27 | } 28 | -------------------------------------------------------------------------------- /Chapter_03/lib/component/recipe_book.html: -------------------------------------------------------------------------------- 1 |

Recipe List

2 |
    3 |
  • 6 | 7 | {{recipe.name}} 9 |
  • 10 |
11 | 12 |

Recipe Details

13 |
Name: {{selectedRecipe.name}}
14 |
Category: {{selectedRecipe.category}}
15 |
Rating:
16 |
17 |
    18 |
  • 19 | {{ingredient}} 20 |
  • 21 |
22 |
23 |
Directions: {{selectedRecipe.directions}}
24 | -------------------------------------------------------------------------------- /Chapter_06/lib/component/search_recipe.dart: -------------------------------------------------------------------------------- 1 | library search_recipe_component; 2 | 3 | import 'package:angular/angular.dart'; 4 | 5 | @Component( 6 | selector: 'search-recipe', 7 | templateUrl: 'search_recipe.html') 8 | class SearchRecipeComponent { 9 | Map _categoryFilterMap = {}; 10 | final List categories = []; 11 | 12 | 13 | @NgTwoWay('name-filter-string') 14 | String nameFilterString = ""; 15 | 16 | @NgOneWay('category-filter-map') 17 | Map get categoryFilterMap => _categoryFilterMap; 18 | void set categoryFilterMap(values) { 19 | _categoryFilterMap = values; 20 | categories.clear(); 21 | categories.addAll(categoryFilterMap.keys); 22 | } 23 | 24 | void clearFilters() { 25 | _categoryFilterMap.keys.forEach((f) => _categoryFilterMap[f] = false); 26 | nameFilterString = ""; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Chapter_02/test/main_test.dart: -------------------------------------------------------------------------------- 1 | library main_test; 2 | 3 | import 'package:unittest/unittest.dart'; 4 | import 'package:di/di.dart'; 5 | import 'package:angular/angular.dart'; 6 | import 'package:angular/mock/module.dart'; 7 | import 'package:tutorial/component/recipe_book.dart'; 8 | 9 | import '../web/main.dart'; 10 | 11 | main() { 12 | setUp(setUpInjector); 13 | tearDown(tearDownInjector); 14 | 15 | group('recipe book component', () { 16 | setUp(module((Module m) => m.install(new MyAppModule()))); 17 | 18 | test('should load recipes', inject((RecipeBookComponent recipeBook) { 19 | expect(recipeBook.recipes, isNot(isEmpty)); 20 | })); 21 | 22 | test('should select recipe', inject((RecipeBookComponent recipeBook) { 23 | var recipe = recipeBook.recipes[0]; 24 | recipeBook.selectRecipe(recipe); 25 | expect(recipeBook.selectedRecipe, same(recipe)); 26 | })); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /Chapter_04/lib/component/recipe_book.html: -------------------------------------------------------------------------------- 1 |

Recipe List

2 |
    3 |
  • 6 | 7 | 8 | {{recipe.name}} 10 | 11 |
  • 12 |
13 | 14 |

Recipe Details

15 |
Name: {{selectedRecipe.name}}
16 |
Category: {{selectedRecipe.category}}
17 |
Rating:
18 |
19 |
    20 |
  • 21 | {{ingredient}} 22 |
  • 23 |
24 |
25 |
Directions: {{selectedRecipe.directions}}
26 | -------------------------------------------------------------------------------- /Chapter_06/lib/routing/recipe_book_router.dart: -------------------------------------------------------------------------------- 1 | library recipe_book_routing; 2 | 3 | import 'package:angular/angular.dart'; 4 | 5 | void recipeBookRouteInitializer(Router router, RouteViewFactory views) { 6 | views.configure({ 7 | 'add': ngRoute( 8 | path: '/add', 9 | view: 'view/addRecipe.html'), 10 | 'recipe': ngRoute( 11 | path: '/recipe/:recipeId', 12 | mount: { 13 | 'view': ngRoute( 14 | path: '/view', 15 | view: 'view/viewRecipe.html'), 16 | 'edit': ngRoute( 17 | path: '/edit', 18 | view: 'view/editRecipe.html'), 19 | 'view_default': ngRoute( 20 | defaultRoute: true, 21 | enter: (RouteEnterEvent e) => 22 | router.go('view', {}, 23 | startingFrom: router.root.findRoute('recipe'), 24 | replace: true)) 25 | }) 26 | }); 27 | } -------------------------------------------------------------------------------- /Chapter_06/lib/component/view_recipe.html: -------------------------------------------------------------------------------- 1 |
2 |

Recipe Details

3 |
4 | 5 | 6 | 7 | 8 |
Name: {{recipe.name}}
9 |
Category: {{recipe.category}}
10 |
Rating: 11 | 12 |
13 |
14 | Ingredients: 15 |
    16 |
  • 17 | {{ingredient}} 18 |
  • 19 |
20 |
21 |
22 | Directions: 23 | {{recipe.directions}} 24 |
25 |
26 |
27 |

Sorry, we could not find the details of the recipe you requested.

28 |
29 |
30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Angular 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 | -------------------------------------------------------------------------------- /Chapter_06/lib/component/recipe_book.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{message}} 4 |
5 |
6 |

Recipe List

7 | 8 | 15 | 16 | 28 |
29 | 30 |
31 |
32 |
33 | -------------------------------------------------------------------------------- /Chapter_06/bin/server_simple_get_and_put.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:http_server/http_server.dart' show VirtualDirectory; 3 | 4 | final String PATH_TO_WEB_CONTENT = "../web"; 5 | 6 | void main() { 7 | HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 3031).then((HttpServer server) { 8 | print("Server up; try http://${server.address.address}:${server.port}/index.html"); 9 | final String root = Platform.script.resolve(PATH_TO_WEB_CONTENT).toFilePath(); 10 | final virDir = new VirtualDirectory(root) 11 | // The following are needed in dev mode to be able to access 12 | // Dart packages in the cache. 13 | ..followLinks = true 14 | ..jailRoot = false; 15 | server.listen((HttpRequest req) { 16 | print("${req.method} ${req.uri};\tcached ${req.headers.ifModifiedSince}"); 17 | (req.method == "PUT" ? processPut : virDir.serveRequest)(req); 18 | }); 19 | }); 20 | } 21 | 22 | void processPut(HttpRequest request) { 23 | final String filePath = request.uri.toFilePath(); 24 | final File file = new File(PATH_TO_WEB_CONTENT + filePath); 25 | request.pipe(file.openWrite()).then((_) { 26 | request.response 27 | ..statusCode = HttpStatus.NO_CONTENT 28 | ..close(); 29 | print("Wrote to file '${file.path}'"); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /Chapter_06/web/main.dart: -------------------------------------------------------------------------------- 1 | library recipe_book; 2 | 3 | import 'package:angular/angular.dart'; 4 | import 'package:angular/application_factory.dart'; 5 | import 'package:logging/logging.dart'; 6 | 7 | import 'package:tutorial/component/recipe_book.dart'; 8 | import 'package:tutorial/component/rating.dart'; 9 | import 'package:tutorial/component/search_recipe.dart'; 10 | import 'package:tutorial/component/view_recipe.dart'; 11 | import 'package:tutorial/formatter/category_filter.dart'; 12 | import 'package:tutorial/routing/recipe_book_router.dart'; 13 | import 'package:tutorial/service/query.dart'; 14 | import 'package:tutorial/tooltip/tooltip.dart'; 15 | 16 | class MyAppModule extends Module { 17 | MyAppModule() { 18 | bind(RecipeBookComponent); 19 | bind(RatingComponent); 20 | bind(Tooltip); 21 | bind(CategoryFilter); 22 | bind(SearchRecipeComponent); 23 | bind(ViewRecipeComponent); 24 | bind(QueryService); 25 | bind(RouteInitializerFn, toValue: recipeBookRouteInitializer); 26 | bind(NgRoutingUsePushState, toValue: new NgRoutingUsePushState.value(false)); 27 | } 28 | } 29 | 30 | void main() { 31 | Logger.root..level = Level.FINEST 32 | ..onRecord.listen((LogRecord r) { print(r.message); }); 33 | 34 | applicationFactory() 35 | .addModule(new MyAppModule()) 36 | .run(); 37 | } 38 | -------------------------------------------------------------------------------- /Chapter_03/test/main_test.dart: -------------------------------------------------------------------------------- 1 | library main_test; 2 | 3 | import 'package:unittest/unittest.dart'; 4 | import 'package:di/di.dart'; 5 | import 'package:angular/angular.dart'; 6 | import 'package:angular/mock/module.dart'; 7 | import 'package:tutorial/component/recipe_book.dart'; 8 | import 'package:tutorial/component/rating.dart'; 9 | 10 | import '../web/main.dart'; 11 | 12 | main() { 13 | setUp(() { 14 | setUpInjector(); 15 | module((Module m) => m.install(new MyAppModule())); 16 | }); 17 | 18 | tearDown(tearDownInjector); 19 | 20 | group('recipe book component', () { 21 | test('should load recipes', inject((RecipeBookComponent recipeBook) { 22 | expect(recipeBook.recipes, isNot(isEmpty)); 23 | })); 24 | 25 | test('should select recipe', inject((RecipeBookComponent recipeBook) { 26 | var recipe = recipeBook.recipes[0]; 27 | recipeBook.selectRecipe(recipe); 28 | expect(recipeBook.selectedRecipe, same(recipe)); 29 | })); 30 | }); 31 | 32 | group('rating component', () { 33 | test('should show the right number of stars', inject((RatingComponent rating) { 34 | rating.maxRating = '5'; 35 | expect(rating.stars, equals([1, 2, 3, 4, 5])); 36 | })); 37 | 38 | test('should handle click', inject((RatingComponent rating) { 39 | rating.maxRating = '5'; 40 | rating.handleClick(3); 41 | expect(rating.rating, equals(3)); 42 | 43 | rating.handleClick(1); 44 | expect(rating.rating, equals(1)); 45 | 46 | rating.handleClick(1); 47 | expect(rating.rating, equals(0)); 48 | 49 | rating.handleClick(1); 50 | expect(rating.rating, equals(1)); 51 | })); 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /Chapter_04/test/main_test.dart: -------------------------------------------------------------------------------- 1 | library main_test; 2 | 3 | import 'package:unittest/unittest.dart'; 4 | import 'package:di/di.dart'; 5 | import 'package:angular/angular.dart'; 6 | import 'package:angular/mock/module.dart'; 7 | import 'package:tutorial/component/recipe_book.dart'; 8 | import 'package:tutorial/component/rating.dart'; 9 | 10 | import '../web/main.dart'; 11 | 12 | main() { 13 | setUp(() { 14 | setUpInjector(); 15 | module((Module m) => m.install(new MyAppModule())); 16 | }); 17 | 18 | tearDown(tearDownInjector); 19 | 20 | group('recipe book component', () { 21 | test('should load recipes', inject((RecipeBookComponent recipeBook) { 22 | expect(recipeBook.recipes, isNot(isEmpty)); 23 | })); 24 | 25 | test('should select recipe', inject((RecipeBookComponent recipeBook) { 26 | var recipe = recipeBook.recipes[0]; 27 | recipeBook.selectRecipe(recipe); 28 | expect(recipeBook.selectedRecipe, same(recipe)); 29 | })); 30 | }); 31 | 32 | group('rating component', () { 33 | test('should show the right number of stars', inject((RatingComponent rating) { 34 | rating.maxRating = '5'; 35 | expect(rating.stars, equals([1, 2, 3, 4, 5])); 36 | })); 37 | 38 | test('should handle click', inject((RatingComponent rating) { 39 | rating.maxRating = '5'; 40 | rating.handleClick(3); 41 | expect(rating.rating, equals(3)); 42 | 43 | rating.handleClick(1); 44 | expect(rating.rating, equals(1)); 45 | 46 | rating.handleClick(1); 47 | expect(rating.rating, equals(0)); 48 | 49 | rating.handleClick(1); 50 | expect(rating.rating, equals(1)); 51 | })); 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /Chapter_06/lib/service/query.dart: -------------------------------------------------------------------------------- 1 | library query_service; 2 | 3 | import 'dart:async'; 4 | 5 | import 'recipe.dart'; 6 | import 'package:angular/angular.dart'; 7 | 8 | @Injectable() 9 | class QueryService { 10 | String _recipesUrl = 'recipes.json'; 11 | String _categoriesUrl = 'categories.json'; 12 | 13 | Future _loaded; 14 | 15 | Map _recipesCache; 16 | List _categoriesCache; 17 | 18 | final Http _http; 19 | 20 | QueryService(Http this._http) { 21 | _loaded = Future.wait([_loadRecipes(), _loadCategories()]); 22 | } 23 | 24 | Future _loadRecipes() { 25 | return _http.get(_recipesUrl) 26 | .then((HttpResponse response) { 27 | _recipesCache = new Map(); 28 | for (Map recipe in response.data) { 29 | Recipe r = new Recipe.fromJson(recipe); 30 | _recipesCache[r.id] = r; 31 | } 32 | }); 33 | } 34 | 35 | Future _loadCategories() { 36 | return _http.get(_categoriesUrl).then((HttpResponse response) { 37 | _categoriesCache = response.data; 38 | }); 39 | } 40 | 41 | Future getRecipeById(String id) { 42 | return _recipesCache == null 43 | ? _loaded.then((_) => _recipesCache[id]) 44 | : new Future.value(_recipesCache[id]); 45 | } 46 | 47 | Future> getAllRecipes() { 48 | return _recipesCache == null 49 | ? _loaded.then((_) => _recipesCache) 50 | : new Future.value(_recipesCache); 51 | } 52 | 53 | Future> getAllCategories() { 54 | return _categoriesCache == null 55 | ? _loaded.then((_) => _categoriesCache) 56 | : new Future.value(_categoriesCache); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Chapter_02/lib/component/recipe_book.dart: -------------------------------------------------------------------------------- 1 | library recipe_book_component; 2 | 3 | import 'package:angular/angular.dart'; 4 | 5 | /* The selector field defines the CSS selector that will trigger the component. It can be any valid 6 | * CSS selector which does not cross element boundaries. 7 | * 8 | * The component's public fields are available for data binding from the view. 9 | * Similarly, the component's public methods can be invoked from the view. 10 | */ 11 | @Component( 12 | selector: 'recipe-book', 13 | templateUrl: 'recipe_book.html') 14 | class RecipeBookComponent { 15 | Recipe selectedRecipe; 16 | List recipes; 17 | 18 | RecipeBookComponent() { 19 | recipes = _loadData(); 20 | } 21 | 22 | void selectRecipe(Recipe recipe) { 23 | selectedRecipe = recipe; 24 | } 25 | 26 | List _loadData() { 27 | return [ 28 | new Recipe('My Appetizer','Appetizers', 29 | ["Ingredient 1", "Ingredient 2"], 30 | "Some Directions", 1), 31 | new Recipe('My Salad','Salads', 32 | ["Ingredient 1", "Ingredient 2"], 33 | "Some Directions", 3), 34 | new Recipe('My Soup','Soups', 35 | ["Ingredient 1", "Ingredient 2"], 36 | "Some Directions", 4), 37 | new Recipe('My Main Dish','Main Dishes', 38 | ["Ingredient 1", "Ingredient 2"], 39 | "Some Directions", 2), 40 | new Recipe('My Side Dish','Side Dishes', 41 | ["Ingredient 1", "Ingredient 2"], 42 | "Some Directions", 3), 43 | new Recipe('My Awesome Dessert','Desserts', 44 | ["Ingredient 1", "Ingredient 2"], 45 | "Some Directions", 5), 46 | new Recipe('My So-So Dessert','Desserts', 47 | ["Ingredient 1", "Ingredient 2"], 48 | "Some Directions", 3), 49 | ]; 50 | } 51 | } 52 | 53 | class Recipe { 54 | final String name; 55 | final String category; 56 | final List ingredients; 57 | final String directions; 58 | int rating; 59 | 60 | Recipe(this.name, this.category, this.ingredients, this.directions, this.rating); 61 | } 62 | -------------------------------------------------------------------------------- /Chapter_03/lib/component/recipe_book.dart: -------------------------------------------------------------------------------- 1 | library recipe_book_component; 2 | 3 | import 'package:angular/angular.dart'; 4 | 5 | /* The selector field defines the CSS selector that will trigger the component. It can be any valid 6 | * CSS selector which does not cross element boundaries. 7 | * 8 | * The component's public fields are available for data binding from the view. 9 | * Similarly, the component's public methods can be invoked from the view. 10 | */ 11 | @Component( 12 | selector: 'recipe-book', 13 | templateUrl: 'recipe_book.html') 14 | class RecipeBookComponent { 15 | Recipe selectedRecipe; 16 | List recipes; 17 | 18 | RecipeBookComponent() { 19 | recipes = _loadData(); 20 | } 21 | 22 | void selectRecipe(Recipe recipe) { 23 | selectedRecipe = recipe; 24 | } 25 | 26 | List _loadData() { 27 | return [ 28 | new Recipe('My Appetizer','Appetizers', 29 | ["Ingredient 1", "Ingredient 2"], 30 | "Some Directions", 1), 31 | new Recipe('My Salad','Salads', 32 | ["Ingredient 1", "Ingredient 2"], 33 | "Some Directions", 3), 34 | new Recipe('My Soup','Soups', 35 | ["Ingredient 1", "Ingredient 2"], 36 | "Some Directions", 4), 37 | new Recipe('My Main Dish','Main Dishes', 38 | ["Ingredient 1", "Ingredient 2"], 39 | "Some Directions", 2), 40 | new Recipe('My Side Dish','Side Dishes', 41 | ["Ingredient 1", "Ingredient 2"], 42 | "Some Directions", 3), 43 | new Recipe('My Awesome Dessert','Desserts', 44 | ["Ingredient 1", "Ingredient 2"], 45 | "Some Directions", 5), 46 | new Recipe('My So-So Dessert','Desserts', 47 | ["Ingredient 1", "Ingredient 2"], 48 | "Some Directions", 3), 49 | ]; 50 | } 51 | } 52 | 53 | class Recipe { 54 | final String name; 55 | final String category; 56 | final List ingredients; 57 | final String directions; 58 | int rating; 59 | 60 | Recipe(this.name, this.category, this.ingredients, this.directions, this.rating); 61 | } 62 | -------------------------------------------------------------------------------- /Chapter_04/lib/tooltip/tooltip.dart: -------------------------------------------------------------------------------- 1 | library tooltip; 2 | 3 | import 'dart:html' as dom; 4 | import 'package:angular/angular.dart'; 5 | 6 | @Decorator(selector: '[tooltip]') 7 | class Tooltip { 8 | final dom.Element element; 9 | 10 | @NgOneWay('tooltip') 11 | TooltipModel displayModel; 12 | 13 | dom.Element tooltipElem; 14 | 15 | Tooltip(this.element) { 16 | element..onMouseEnter.listen((_) => _createTemplate()) 17 | ..onMouseLeave.listen((_) => _destroyTemplate()); 18 | } 19 | 20 | void _createTemplate() { 21 | assert(displayModel != null); 22 | 23 | tooltipElem = new dom.DivElement(); 24 | 25 | dom.ImageElement imgElem = new dom.ImageElement() 26 | ..width = displayModel.imgWidth 27 | ..src = displayModel.imgUrl; 28 | tooltipElem.append(imgElem); 29 | 30 | if (displayModel.text != null) { 31 | dom.DivElement textSpan = new dom.DivElement() 32 | ..appendText(displayModel.text) 33 | ..style.color = "white" 34 | ..style.fontSize = "smaller" 35 | ..style.paddingBottom = "5px"; 36 | 37 | tooltipElem.append(textSpan); 38 | } 39 | 40 | tooltipElem.style 41 | ..padding = "5px" 42 | ..paddingBottom = "0px" 43 | ..backgroundColor = "black" 44 | ..borderRadius = "5px" 45 | ..width = "${displayModel.imgWidth.toString()}px"; 46 | 47 | // position the tooltip. 48 | var elTopRight = element.offset.topRight; 49 | 50 | tooltipElem.style 51 | ..position = "absolute" 52 | ..top = "${elTopRight.y}px" 53 | ..left = "${elTopRight.x + 10}px"; 54 | 55 | // Add the tooltip to the document body. We add it here because we need to position it 56 | // absolutely, without reference to its parent element. 57 | dom.document.body.append(tooltipElem); 58 | } 59 | 60 | void _destroyTemplate() { 61 | tooltipElem.remove(); 62 | } 63 | } 64 | 65 | class TooltipModel { 66 | final String imgUrl; 67 | final String text; 68 | final int imgWidth; 69 | 70 | TooltipModel(this.imgUrl, this.text, this.imgWidth); 71 | } 72 | -------------------------------------------------------------------------------- /Chapter_05/lib/component/recipe_book.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{message}} 4 |
5 |
6 |

Recipe List

7 | 8 |
9 |
10 | 11 | 13 |
14 |
15 | Filter recipes by category: 16 | 17 | 21 | 22 |
23 | 24 |
25 | 26 |
27 |
    28 |
  • 30 | 31 | 32 | {{recipe.name}} 34 | 35 |
  • 36 |
37 |
38 | 39 |
40 |

Recipe Details

41 |
Name: {{selectedRecipe.name}}
42 |
Category: {{selectedRecipe.category}}
43 |
Rating: 44 | 45 |
46 |
47 |
    48 |
  • 49 | {{ingredient}} 50 |
  • 51 |
52 |
53 |
Directions: {{selectedRecipe.directions}}
54 |
55 |
56 |
57 | -------------------------------------------------------------------------------- /Chapter_05/lib/tooltip/tooltip.dart: -------------------------------------------------------------------------------- 1 | library tooltip; 2 | 3 | import 'dart:html' as dom; 4 | import 'package:angular/angular.dart'; 5 | 6 | @Decorator(selector: '[tooltip]') 7 | class Tooltip { 8 | final dom.Element element; 9 | 10 | @NgOneWay('tooltip') 11 | TooltipModel displayModel; 12 | 13 | dom.Element tooltipElem; 14 | 15 | Tooltip(this.element) { 16 | element..onMouseEnter.listen((_) => _createTemplate()) 17 | ..onMouseLeave.listen((_) => _destroyTemplate()); 18 | } 19 | 20 | void _createTemplate() { 21 | assert(displayModel != null); 22 | 23 | tooltipElem = new dom.DivElement(); 24 | 25 | dom.ImageElement imgElem = new dom.ImageElement() 26 | ..width = displayModel.imgWidth 27 | ..src = displayModel.imgUrl; 28 | tooltipElem.append(imgElem); 29 | 30 | if (displayModel.text != null) { 31 | dom.DivElement textSpan = new dom.DivElement() 32 | ..appendText(displayModel.text) 33 | ..style.color = "white" 34 | ..style.fontSize = "smaller" 35 | ..style.paddingBottom = "5px"; 36 | 37 | tooltipElem.append(textSpan); 38 | } 39 | 40 | tooltipElem.style 41 | ..padding = "5px" 42 | ..paddingBottom = "0px" 43 | ..backgroundColor = "black" 44 | ..borderRadius = "5px" 45 | ..width = "${displayModel.imgWidth.toString()}px"; 46 | 47 | // position the tooltip. 48 | var elTopRight = element.offset.topRight; 49 | 50 | tooltipElem.style 51 | ..position = "absolute" 52 | ..top = "${elTopRight.y}px" 53 | ..left = "${elTopRight.x + 10}px"; 54 | 55 | // Add the tooltip to the document body. We add it here because we need to position it 56 | // absolutely, without reference to its parent element. 57 | dom.document.body.append(tooltipElem); 58 | } 59 | 60 | void _destroyTemplate() { 61 | tooltipElem.remove(); 62 | } 63 | } 64 | 65 | class TooltipModel { 66 | final String imgUrl; 67 | final String text; 68 | final int imgWidth; 69 | 70 | TooltipModel(this.imgUrl, this.text, this.imgWidth); 71 | } 72 | -------------------------------------------------------------------------------- /Chapter_06/lib/tooltip/tooltip.dart: -------------------------------------------------------------------------------- 1 | library tooltip; 2 | 3 | import 'dart:html' as dom; 4 | import 'package:angular/angular.dart'; 5 | 6 | @Decorator(selector: '[tooltip]') 7 | class Tooltip { 8 | final dom.Element element; 9 | 10 | @NgOneWay('tooltip') 11 | TooltipModel displayModel; 12 | 13 | dom.Element tooltipElem; 14 | 15 | Tooltip(this.element) { 16 | element..onMouseEnter.listen((_) => _createTemplate()) 17 | ..onMouseLeave.listen((_) => _destroyTemplate()); 18 | } 19 | 20 | void _createTemplate() { 21 | assert(displayModel != null); 22 | 23 | tooltipElem = new dom.DivElement(); 24 | 25 | dom.ImageElement imgElem = new dom.ImageElement() 26 | ..width = displayModel.imgWidth 27 | ..src = displayModel.imgUrl; 28 | tooltipElem.append(imgElem); 29 | 30 | if (displayModel.text != null) { 31 | dom.DivElement textSpan = new dom.DivElement() 32 | ..appendText(displayModel.text) 33 | ..style.color = "white" 34 | ..style.fontSize = "smaller" 35 | ..style.paddingBottom = "5px"; 36 | 37 | tooltipElem.append(textSpan); 38 | } 39 | 40 | tooltipElem.style 41 | ..padding = "5px" 42 | ..paddingBottom = "0px" 43 | ..backgroundColor = "black" 44 | ..borderRadius = "5px" 45 | ..width = "${displayModel.imgWidth.toString()}px"; 46 | 47 | // position the tooltip. 48 | var elTopRight = element.offset.topRight; 49 | 50 | tooltipElem.style 51 | ..position = "absolute" 52 | ..top = "${elTopRight.y}px" 53 | ..left = "${elTopRight.x + 10}px"; 54 | 55 | // Add the tooltip to the document body. We add it here because we need to position it 56 | // absolutely, without reference to its parent element. 57 | dom.document.body.append(tooltipElem); 58 | } 59 | 60 | void _destroyTemplate() { 61 | tooltipElem.remove(); 62 | } 63 | } 64 | 65 | class TooltipModel { 66 | final String imgUrl; 67 | final String text; 68 | final int imgWidth; 69 | 70 | TooltipModel(this.imgUrl, this.text, this.imgWidth); 71 | } 72 | -------------------------------------------------------------------------------- /Chapter_04/lib/component/rating.dart: -------------------------------------------------------------------------------- 1 | library rating; 2 | 3 | import 'package:angular/angular.dart'; 4 | 5 | /* Use the @Component annotation to indicate that this class is an 6 | * Angular component. 7 | * 8 | * The selector field defines the CSS selector that will trigger the 9 | * component. Typically, the CSS selector is an element name. 10 | * 11 | * The templateUrl field tells the component which HTML template to use 12 | * for its view. 13 | * 14 | * The cssUrl field tells the component which CSS file to use. 15 | * 16 | * The publishAs field specifies that the component instance should be 17 | * assigned to the current scope under the name specified. 18 | * 19 | * The class field and setter annotated with @NgTwoWay and @NgAttr, 20 | * respectively, identify the attributes that can be set on 21 | * the component. Users of this component will specify these attributes 22 | * in the HTML tag that is used to create the component. For example: 23 | * 24 | * 25 | * 26 | * The component's public fields are available for data binding from the 27 | * component's view. Similarly, the component's public methods can be 28 | * invoked from the component's view. 29 | */ 30 | @Component( 31 | selector: 'rating', 32 | templateUrl: 'rating.html', 33 | cssUrl: 'rating.css') 34 | class RatingComponent { 35 | static const String _STAR_ON_CHAR = "\u2605"; 36 | static const String _STAR_OFF_CHAR = "\u2606"; 37 | static const String _STAR_ON_CLASS = "star-on"; 38 | static const String _STAR_OFF_CLASS = "star-off"; 39 | 40 | static final int DEFAULT_MAX = 5; 41 | 42 | List stars = []; 43 | 44 | @NgTwoWay('rating') 45 | int rating; 46 | 47 | @NgAttr('max-rating') 48 | void set maxRating(String value) { 49 | var count = value == null 50 | ? DEFAULT_MAX 51 | : int.parse(value, onError: (_) => DEFAULT_MAX); 52 | stars = new List.generate(count, (i) => i + 1); 53 | } 54 | 55 | String starClass(int star) => star > rating ? _STAR_OFF_CLASS : _STAR_ON_CLASS; 56 | 57 | String starChar(int star) => star > rating ? _STAR_OFF_CHAR : _STAR_ON_CHAR; 58 | 59 | void handleClick(int star) { 60 | rating = (star == 1 && rating == 1) ? 0 : star; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Chapter_05/lib/component/rating.dart: -------------------------------------------------------------------------------- 1 | library rating; 2 | 3 | import 'package:angular/angular.dart'; 4 | 5 | /* Use the @Component annotation to indicate that this class is an 6 | * Angular component. 7 | * 8 | * The selector field defines the CSS selector that will trigger the 9 | * component. Typically, the CSS selector is an element name. 10 | * 11 | * The templateUrl field tells the component which HTML template to use 12 | * for its view. 13 | * 14 | * The cssUrl field tells the component which CSS file to use. 15 | * 16 | * The publishAs field specifies that the component instance should be 17 | * assigned to the current scope under the name specified. 18 | * 19 | * The class field and setter annotated with @NgTwoWay and @NgAttr, 20 | * respectively, identify the attributes that can be set on 21 | * the component. Users of this component will specify these attributes 22 | * in the HTML tag that is used to create the component. For example: 23 | * 24 | * 25 | * 26 | * The component's public fields are available for data binding from the 27 | * component's view. Similarly, the component's public methods can be 28 | * invoked from the component's view. 29 | */ 30 | @Component( 31 | selector: 'rating', 32 | templateUrl: 'rating.html', 33 | cssUrl: 'rating.css') 34 | class RatingComponent { 35 | static const String _STAR_ON_CHAR = "\u2605"; 36 | static const String _STAR_OFF_CHAR = "\u2606"; 37 | static const String _STAR_ON_CLASS = "star-on"; 38 | static const String _STAR_OFF_CLASS = "star-off"; 39 | 40 | static final int DEFAULT_MAX = 5; 41 | 42 | List stars = []; 43 | 44 | @NgTwoWay('rating') 45 | int rating; 46 | 47 | @NgAttr('max-rating') 48 | void set maxRating(String value) { 49 | var count = value == null 50 | ? DEFAULT_MAX 51 | : int.parse(value, onError: (_) => DEFAULT_MAX); 52 | stars = new List.generate(count, (i) => i + 1); 53 | } 54 | 55 | String starClass(int star) => star > rating ? _STAR_OFF_CLASS : _STAR_ON_CLASS; 56 | 57 | String starChar(int star) => star > rating ? _STAR_OFF_CHAR : _STAR_ON_CHAR; 58 | 59 | void handleClick(int star) { 60 | rating = (star == 1 && rating == 1) ? 0 : star; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Chapter_06/lib/component/rating.dart: -------------------------------------------------------------------------------- 1 | library rating; 2 | 3 | import 'package:angular/angular.dart'; 4 | 5 | /* Use the @Component annotation to indicate that this class is an 6 | * Angular component. 7 | * 8 | * The selector field defines the CSS selector that will trigger the 9 | * component. Typically, the CSS selector is an element name. 10 | * 11 | * The templateUrl field tells the component which HTML template to use 12 | * for its view. 13 | * 14 | * The cssUrl field tells the component which CSS file to use. 15 | * 16 | * The publishAs field specifies that the component instance should be 17 | * assigned to the current scope under the name specified. 18 | * 19 | * The class field and setter annotated with @NgTwoWay and @NgAttr, 20 | * respectively, identify the attributes that can be set on 21 | * the component. Users of this component will specify these attributes 22 | * in the HTML tag that is used to create the component. For example: 23 | * 24 | * 25 | * 26 | * The component's public fields are available for data binding from the 27 | * component's view. Similarly, the component's public methods can be 28 | * invoked from the component's view. 29 | */ 30 | @Component( 31 | selector: 'rating', 32 | templateUrl: 'rating.html', 33 | cssUrl: 'rating.css') 34 | class RatingComponent { 35 | static const String _STAR_ON_CHAR = "\u2605"; 36 | static const String _STAR_OFF_CHAR = "\u2606"; 37 | static const String _STAR_ON_CLASS = "star-on"; 38 | static const String _STAR_OFF_CLASS = "star-off"; 39 | 40 | static final int DEFAULT_MAX = 5; 41 | 42 | List stars = []; 43 | 44 | @NgTwoWay('rating') 45 | int rating; 46 | 47 | @NgAttr('max-rating') 48 | void set maxRating(String value) { 49 | var count = value == null 50 | ? DEFAULT_MAX 51 | : int.parse(value, onError: (_) => DEFAULT_MAX); 52 | stars = new List.generate(count, (i) => i + 1); 53 | } 54 | 55 | String starClass(int star) => star > rating ? _STAR_OFF_CLASS : _STAR_ON_CLASS; 56 | 57 | String starChar(int star) => star > rating ? _STAR_OFF_CHAR : _STAR_ON_CHAR; 58 | 59 | void handleClick(int star) { 60 | rating = (star == 1 && rating == 1) ? 0 : star; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Chapter_03/lib/component/rating.dart: -------------------------------------------------------------------------------- 1 | library rating; 2 | 3 | import 'package:angular/angular.dart'; 4 | 5 | /* Use the @Component annotation to indicate that this class is an 6 | * Angular component. 7 | * 8 | * The selector field defines the CSS selector that will trigger the 9 | * component. Typically, the CSS selector is an element name. 10 | * 11 | * The templateUrl field tells the component which HTML template to use 12 | * for its view. 13 | * 14 | * The cssUrl field tells the component which CSS file to use. 15 | * 16 | * The publishAs field specifies that the component instance should be 17 | * assigned to the current scope under the name specified. 18 | * 19 | * The class field and setter annotated with @NgTwoWay and @NgAttr, 20 | * respectively, identify the attributes that can be set on 21 | * the component. Users of this component will specify these attributes 22 | * in the HTML tag that is used to create the component. For example: 23 | * 24 | * 25 | * 26 | * The component's public fields are available for data binding from the 27 | * component's view. Similarly, the component's public methods can be 28 | * invoked from the component's view. 29 | */ 30 | @Component( 31 | selector: 'rating', 32 | templateUrl: 'rating.html', 33 | cssUrl: 'rating.css') 34 | class RatingComponent { 35 | static const String _STAR_ON_CHAR = "\u2605"; 36 | static const String _STAR_OFF_CHAR = "\u2606"; 37 | static const String _STAR_ON_CLASS = "star-on"; 38 | static const String _STAR_OFF_CLASS = "star-off"; 39 | 40 | static final int DEFAULT_MAX = 5; 41 | 42 | List stars = []; 43 | 44 | @NgTwoWay('rating') 45 | int rating; 46 | 47 | @NgAttr('max-rating') 48 | void set maxRating(String value) { 49 | var count = value == null 50 | ? DEFAULT_MAX 51 | : int.parse(value, onError: (_) => DEFAULT_MAX); 52 | stars = new List.generate(count, (i) => i + 1); 53 | } 54 | 55 | String starClass(int star) => 56 | rating == null || star > rating ? _STAR_OFF_CLASS : _STAR_ON_CLASS; 57 | 58 | String starChar(int star) => 59 | rating == null || star > rating ? _STAR_OFF_CHAR : _STAR_ON_CHAR; 60 | 61 | void handleClick(int star) { 62 | rating = (star == 1 && rating == 1) ? 0 : star; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Chapter_04/lib/component/recipe_book.dart: -------------------------------------------------------------------------------- 1 | library recipe_book_component; 2 | 3 | import 'package:angular/angular.dart'; 4 | import 'package:tutorial/tooltip/tooltip.dart' show TooltipModel; 5 | 6 | /* The selector field defines the CSS selector that will trigger the component. It can be any valid 7 | * CSS selector which does not cross element boundaries. 8 | * 9 | * The component's public fields are available for data binding from the view. 10 | * Similarly, the component's public methods can be invoked from the view. 11 | */ 12 | @Component( 13 | selector: 'recipe-book', 14 | templateUrl: 'recipe_book.html') 15 | class RecipeBookComponent { 16 | Recipe selectedRecipe; 17 | List recipes; 18 | 19 | RecipeBookComponent() { 20 | recipes = _loadData(); 21 | } 22 | 23 | void selectRecipe(Recipe recipe) { 24 | selectedRecipe = recipe; 25 | } 26 | 27 | // Tooltip 28 | static final tooltip = new Expando(); 29 | 30 | TooltipModel tooltipForRecipe(Recipe recipe) { 31 | if (tooltip[recipe] == null) { 32 | tooltip[recipe] = new TooltipModel(recipe.imgUrl, 33 | "I don't have a picture of these recipes, " 34 | "so here's one of my cat instead!", 35 | 80); 36 | } 37 | return tooltip[recipe]; // recipe.tooltip 38 | } 39 | 40 | List _loadData() { 41 | return [ 42 | new Recipe('My Appetizer','Appetizers', 43 | ["Ingredient 1", "Ingredient 2"], 44 | "Some Directions", 1, 'fonzie1.jpg'), 45 | new Recipe('My Salad','Salads', 46 | ["Ingredient 1", "Ingredient 2"], 47 | "Some Directions", 3, 'fonzie2.jpg'), 48 | new Recipe('My Soup','Soups', 49 | ["Ingredient 1", "Ingredient 2"], 50 | "Some Directions", 4, 'fonzie1.jpg'), 51 | new Recipe('My Main Dish','Main Dishes', 52 | ["Ingredient 1", "Ingredient 2"], 53 | "Some Directions", 2, 'fonzie2.jpg'), 54 | new Recipe('My Side Dish','Side Dishes', 55 | ["Ingredient 1", "Ingredient 2"], 56 | "Some Directions", 3, 'fonzie1.jpg'), 57 | new Recipe('My Awesome Dessert','Desserts', 58 | ["Ingredient 1", "Ingredient 2"], 59 | "Some Directions", 5, 'fonzie2.jpg'), 60 | new Recipe('My So-So Dessert','Desserts', 61 | ["Ingredient 1", "Ingredient 2"], 62 | "Some Directions", 3, 'fonzie1.jpg'), 63 | ]; 64 | } 65 | } 66 | 67 | class Recipe { 68 | final String name; 69 | final String category; 70 | final List ingredients; 71 | final String directions; 72 | final String imgUrl; 73 | int rating; 74 | 75 | Recipe(this.name, this.category, this.ingredients, this.directions, this.rating, this.imgUrl); 76 | } 77 | -------------------------------------------------------------------------------- /Chapter_05/test/main_test.dart: -------------------------------------------------------------------------------- 1 | library main_test; 2 | 3 | import 'package:unittest/unittest.dart'; 4 | import 'package:di/di.dart'; 5 | import 'package:angular/angular.dart'; 6 | import 'package:angular/mock/module.dart'; 7 | import 'package:tutorial/component/recipe_book.dart'; 8 | import 'package:tutorial/component/rating.dart'; 9 | import 'package:tutorial/recipe.dart'; 10 | import 'package:tutorial/formatter/category_filter.dart'; 11 | 12 | import '../web/main.dart'; 13 | 14 | main() { 15 | setUp(() { 16 | setUpInjector(); 17 | module((Module m) => m.install(new MyAppModule())); 18 | }); 19 | 20 | tearDown(tearDownInjector); 21 | 22 | group('recipe book component', () { 23 | test('should load recipes', async(inject((Injector injector, MockHttpBackend backend) { 24 | 25 | backend.expectGET('recipes.json').respond('[{"name": "test1"}]'); 26 | backend.expectGET('categories.json').respond('["c1"]'); 27 | 28 | var recipeBook = injector.get(RecipeBookComponent); 29 | expect(recipeBook.recipes, isEmpty); 30 | 31 | microLeap(); 32 | backend.flush(); 33 | microLeap(); 34 | 35 | expect(recipeBook.recipes, isNot(isEmpty)); 36 | }))); 37 | 38 | test('should select recipe', async(inject((Injector injector, MockHttpBackend backend) { 39 | backend.expectGET('recipes.json').respond('[{"name": "test1"}]'); 40 | backend.expectGET('categories.json').respond('["c1"]'); 41 | 42 | var recipeBook = injector.get(RecipeBookComponent); 43 | expect(recipeBook.recipes, isEmpty); 44 | 45 | microLeap(); 46 | backend.flush(); 47 | microLeap(); 48 | 49 | var recipe = recipeBook.recipes[0]; 50 | recipeBook.selectRecipe(recipe); 51 | expect(recipeBook.selectedRecipe, same(recipe)); 52 | }))); 53 | }); 54 | 55 | group('rating component', () { 56 | test('should show the right number of stars', inject((RatingComponent rating) { 57 | rating.maxRating = '5'; 58 | expect(rating.stars, equals([1, 2, 3, 4, 5])); 59 | })); 60 | 61 | test('should handle click', inject((RatingComponent rating) { 62 | rating.maxRating = '5'; 63 | rating.handleClick(3); 64 | expect(rating.rating, equals(3)); 65 | 66 | rating.handleClick(1); 67 | expect(rating.rating, equals(1)); 68 | 69 | rating.handleClick(1); 70 | expect(rating.rating, equals(0)); 71 | 72 | rating.handleClick(1); 73 | expect(rating.rating, equals(1)); 74 | })); 75 | }); 76 | 77 | group('categoryFilter', () { 78 | test('should return subset', inject((CategoryFilter filter) { 79 | var r1 = new Recipe(null, null, 'C1', null, null, null, null); 80 | var r2 = new Recipe(null, null, 'C2', null, null, null, null); 81 | var list = [r1, r2]; 82 | var map = {"C1": false, "C2": true}; 83 | expect(filter(list, map), equals([r2])); 84 | })); 85 | }); 86 | } 87 | -------------------------------------------------------------------------------- /Chapter_06/test/main_test.dart: -------------------------------------------------------------------------------- 1 | library main_test; 2 | 3 | import 'package:unittest/unittest.dart'; 4 | import 'package:di/di.dart'; 5 | import 'package:angular/angular.dart'; 6 | import 'package:angular/mock/module.dart'; 7 | import 'package:tutorial/component/recipe_book.dart'; 8 | import 'package:tutorial/component/rating.dart'; 9 | import 'package:tutorial/formatter/category_filter.dart'; 10 | import 'package:tutorial/service/recipe.dart'; 11 | 12 | import '../web/main.dart'; 13 | 14 | main() { 15 | setUp(() { 16 | setUpInjector(); 17 | module((Module m) => m.install(new MyAppModule())); 18 | }); 19 | tearDown(tearDownInjector); 20 | 21 | group('recipe-book', () { 22 | test('should load recipes', async(inject((Injector injector, 23 | MockHttpBackend backend) { 24 | backend.expectGET('recipes.json').respond('[{"name": "test1"}]'); 25 | backend.expectGET('categories.json').respond('["c1"]'); 26 | 27 | var recipeBook = injector.get(RecipeBookComponent); 28 | expect(recipeBook.allRecipes, isEmpty); 29 | 30 | microLeap(); 31 | backend.flush(); 32 | microLeap(); 33 | 34 | expect(recipeBook.allRecipes, isNot(isEmpty)); 35 | }))); 36 | 37 | test('should select recipe', async(inject((Injector injector, 38 | MockHttpBackend backend) { 39 | backend.expectGET('recipes.json').respond('[{"name": "test1"}]'); 40 | backend.expectGET('categories.json').respond('["c1"]'); 41 | 42 | var recipeBook = injector.get(RecipeBookComponent); 43 | expect(recipeBook.allRecipes, isEmpty); 44 | 45 | microLeap(); 46 | backend.flush(); 47 | microLeap(); 48 | 49 | var recipe = recipeBook.allRecipes[0]; 50 | recipeBook.selectRecipe(recipe); 51 | expect(recipeBook.selectedRecipe, same(recipe)); 52 | }))); 53 | }); 54 | 55 | group('rating component', () { 56 | test('should show the right number of stars', 57 | inject((RatingComponent rating) { 58 | rating.maxRating = '5'; 59 | expect(rating.stars, equals([1, 2, 3, 4, 5])); 60 | })); 61 | 62 | test('should handle click', inject((RatingComponent rating) { 63 | rating.maxRating = '5'; 64 | rating.handleClick(3); 65 | expect(rating.rating, equals(3)); 66 | 67 | rating.handleClick(1); 68 | expect(rating.rating, equals(1)); 69 | 70 | rating.handleClick(1); 71 | expect(rating.rating, equals(0)); 72 | 73 | rating.handleClick(1); 74 | expect(rating.rating, equals(1)); 75 | })); 76 | }); 77 | 78 | group('categoryFilter', () { 79 | test('should return subset', inject((CategoryFilter filter) { 80 | var r1 = new Recipe(null, null, 'C1', null, null, null, null); 81 | var r2 = new Recipe(null, null, 'C2', null, null, null, null); 82 | var list = [r1, r2]; 83 | var map = {"C1": false, "C2": true}; 84 | expect(filter(list, map), equals([r2])); 85 | })); 86 | }); 87 | } 88 | -------------------------------------------------------------------------------- /Chapter_06/lib/component/recipe_book.dart: -------------------------------------------------------------------------------- 1 | library recipe_book_component; 2 | 3 | import 'package:angular/angular.dart'; 4 | import 'package:tutorial/tooltip/tooltip.dart' show TooltipModel; 5 | import 'package:tutorial/service/recipe.dart'; 6 | import 'package:tutorial/service/query.dart'; 7 | 8 | /* The selector field defines the CSS selector that will trigger the component. It can be any valid 9 | * CSS selector which does not cross element boundaries. 10 | * 11 | * The component's public fields are available for data binding from the view. 12 | * Similarly, the component's public methods can be invoked from the view. 13 | */ 14 | @Component( 15 | selector: 'recipe-book', 16 | templateUrl: 'recipe_book.html') 17 | class RecipeBookComponent { 18 | 19 | static const String LOADING_MESSAGE = "Loading recipe book..."; 20 | static const String ERROR_MESSAGE = "Sorry! The cook stepped out of the" 21 | "kitchen and took the recipe book with him!"; 22 | 23 | final Http _http; 24 | final QueryService queryService; 25 | 26 | // Determine the initial load state of the app 27 | String message = LOADING_MESSAGE; 28 | bool recipesLoaded = false; 29 | bool categoriesLoaded = false; 30 | 31 | Map _recipeMap = {}; 32 | Map get recipeMap => _recipeMap; 33 | 34 | List _allRecipes = []; 35 | 36 | List get allRecipes => _allRecipes; 37 | 38 | // Filter box 39 | final Map categoryFilterMap = {}; 40 | final List categories = []; 41 | String nameFilter = ""; 42 | 43 | RecipeBookComponent(this._http, this.queryService) { 44 | _loadData(); 45 | } 46 | 47 | Recipe selectedRecipe; 48 | 49 | void selectRecipe(Recipe recipe) { 50 | selectedRecipe = recipe; 51 | } 52 | 53 | // Tooltip 54 | static final _tooltip = new Expando(); 55 | TooltipModel tooltipForRecipe(Recipe recipe) { 56 | if (_tooltip[recipe] == null) { 57 | _tooltip[recipe] = new TooltipModel(recipe.imgUrl, 58 | "I don't have a picture of these recipes, " 59 | "so here's one of my cat instead!", 60 | 80); 61 | } 62 | return _tooltip[recipe]; // recipe.tooltip 63 | } 64 | 65 | void _loadData() { 66 | queryService.getAllRecipes() 67 | .then((Map allRecipes) { 68 | _recipeMap = allRecipes; 69 | _allRecipes = _recipeMap.values.toList(); 70 | recipesLoaded = true; 71 | }) 72 | .catchError((e) { 73 | print(e); 74 | recipesLoaded = false; 75 | message = ERROR_MESSAGE; 76 | }); 77 | 78 | queryService.getAllCategories() 79 | .then((List allCategories) { 80 | for (String category in allCategories) { 81 | categoryFilterMap[category] = false; 82 | } 83 | categories.addAll(categoryFilterMap.keys); 84 | categoriesLoaded = true; 85 | }) 86 | .catchError((e) { 87 | print(e); 88 | categoriesLoaded = false; 89 | message = ERROR_MESSAGE; 90 | }); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Chapter_05/lib/component/recipe_book.dart: -------------------------------------------------------------------------------- 1 | library recipe_book_component; 2 | 3 | import 'package:angular/angular.dart'; 4 | import 'package:tutorial/tooltip/tooltip.dart' show TooltipModel; 5 | import 'package:tutorial/recipe.dart'; 6 | 7 | /* The selector field defines the CSS selector that will trigger the component. It can be any valid 8 | * CSS selector which does not cross element boundaries. 9 | * 10 | * The component's public fields are available for data binding from the view. 11 | * Similarly, the component's public methods can be invoked from the view. 12 | */ 13 | @Component( 14 | selector: 'recipe-book', 15 | templateUrl: 'recipe_book.html') 16 | class RecipeBookComponent { 17 | static const String LOADING_MESSAGE = "Loading recipe book..."; 18 | static const String ERROR_MESSAGE = "Sorry! The cook stepped out of the " 19 | "kitchen and took the recipe book with him!"; 20 | 21 | final Http _http; 22 | 23 | // Determine the initial load state of the app 24 | String message = LOADING_MESSAGE; 25 | bool recipesLoaded = false; 26 | bool categoriesLoaded = false; 27 | 28 | // Data objects that are loaded from the server side via json 29 | List recipes = []; 30 | 31 | // Filter box 32 | final Map categoryFilterMap = {}; 33 | final List categories = []; 34 | String nameFilterString = ""; 35 | 36 | Recipe selectedRecipe; 37 | 38 | RecipeBookComponent(this._http) { 39 | _loadData(); 40 | } 41 | 42 | void selectRecipe(Recipe recipe) { 43 | selectedRecipe = recipe; 44 | } 45 | 46 | // Tooltip 47 | static final tooltip = new Expando(); 48 | 49 | TooltipModel tooltipForRecipe(Recipe recipe) { 50 | if (tooltip[recipe] == null) { 51 | tooltip[recipe] = new TooltipModel(recipe.imgUrl, 52 | "I don't have a picture of these recipes, " 53 | "so here's one of my cat instead!", 54 | 80); 55 | } 56 | return tooltip[recipe]; // recipe.tooltip 57 | } 58 | 59 | void clearFilters() { 60 | categoryFilterMap.clear(); 61 | nameFilterString = ""; 62 | } 63 | 64 | void _loadData() { 65 | recipesLoaded = false; 66 | categoriesLoaded = false; 67 | _http.get('recipes.json') 68 | .then((HttpResponse response) { 69 | print(response); 70 | recipes = response.data.map((d) => new Recipe.fromJson(d)).toList(); 71 | recipesLoaded = true; 72 | }) 73 | .catchError((e) { 74 | print(e); 75 | recipesLoaded = false; 76 | message = ERROR_MESSAGE; 77 | }); 78 | 79 | _http.get('categories.json') 80 | .then((HttpResponse response) { 81 | print(response); 82 | for (String category in response.data) { 83 | categoryFilterMap[category] = false; 84 | } 85 | categories.clear(); 86 | categories.addAll(categoryFilterMap.keys); 87 | categoriesLoaded = true; 88 | }) 89 | .catchError((e) { 90 | print(e); 91 | categoriesLoaded = false; 92 | message = ERROR_MESSAGE; 93 | }); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Chapter_01/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See http://pub.dartlang.org/doc/glossary.html#lockfile 3 | packages: 4 | analyzer: 5 | description: analyzer 6 | source: hosted 7 | version: "0.22.4" 8 | angular: 9 | description: angular 10 | source: hosted 11 | version: "1.1.0" 12 | args: 13 | description: args 14 | source: hosted 15 | version: "0.12.2+6" 16 | barback: 17 | description: barback 18 | source: hosted 19 | version: "0.15.2+4" 20 | browser: 21 | description: browser 22 | source: hosted 23 | version: "0.10.0+2" 24 | cli_util: 25 | description: cli_util 26 | source: hosted 27 | version: "0.0.1+1" 28 | code_transformers: 29 | description: code_transformers 30 | source: hosted 31 | version: "0.2.7+2" 32 | collection: 33 | description: collection 34 | source: hosted 35 | version: "0.9.4" 36 | csslib: 37 | description: csslib 38 | source: hosted 39 | version: "0.12.0" 40 | di: 41 | description: di 42 | source: hosted 43 | version: "3.3.4" 44 | html: 45 | description: html 46 | source: hosted 47 | version: "0.12.1+1" 48 | html5lib: 49 | description: html5lib 50 | source: hosted 51 | version: "0.12.1" 52 | initialize: 53 | description: initialize 54 | source: hosted 55 | version: "0.2.0" 56 | intl: 57 | description: intl 58 | source: hosted 59 | version: "0.11.12" 60 | logging: 61 | description: logging 62 | source: hosted 63 | version: "0.9.3" 64 | observe: 65 | description: observe 66 | source: hosted 67 | version: "0.12.2+1" 68 | path: 69 | description: path 70 | source: hosted 71 | version: "1.3.4" 72 | perf_api: 73 | description: perf_api 74 | source: hosted 75 | version: "0.0.9" 76 | petitparser: 77 | description: petitparser 78 | source: hosted 79 | version: "1.3.7" 80 | pool: 81 | description: pool 82 | source: hosted 83 | version: "1.0.1" 84 | route_hierarchical: 85 | description: route_hierarchical 86 | source: hosted 87 | version: "0.6.1+1" 88 | smoke: 89 | description: smoke 90 | source: hosted 91 | version: "0.3.2" 92 | source_maps: 93 | description: source_maps 94 | source: hosted 95 | version: "0.10.0+2" 96 | source_span: 97 | description: source_span 98 | source: hosted 99 | version: "1.1.2" 100 | stack_trace: 101 | description: stack_trace 102 | source: hosted 103 | version: "1.2.4" 104 | utf: 105 | description: utf 106 | source: hosted 107 | version: "0.9.0+2" 108 | watcher: 109 | description: watcher 110 | source: hosted 111 | version: "0.9.5" 112 | web_components: 113 | description: web_components 114 | source: hosted 115 | version: "0.10.1" 116 | when: 117 | description: when 118 | source: hosted 119 | version: "0.2.0" 120 | which: 121 | description: which 122 | source: hosted 123 | version: "0.1.3" 124 | -------------------------------------------------------------------------------- /Chapter_03/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See http://pub.dartlang.org/doc/glossary.html#lockfile 3 | packages: 4 | analyzer: 5 | description: analyzer 6 | source: hosted 7 | version: "0.22.4" 8 | angular: 9 | description: angular 10 | source: hosted 11 | version: "1.1.0" 12 | args: 13 | description: args 14 | source: hosted 15 | version: "0.12.2+6" 16 | barback: 17 | description: barback 18 | source: hosted 19 | version: "0.15.2+4" 20 | browser: 21 | description: browser 22 | source: hosted 23 | version: "0.10.0+2" 24 | cli_util: 25 | description: cli_util 26 | source: hosted 27 | version: "0.0.1+1" 28 | code_transformers: 29 | description: code_transformers 30 | source: hosted 31 | version: "0.2.5" 32 | collection: 33 | description: collection 34 | source: hosted 35 | version: "0.9.1" 36 | csslib: 37 | description: csslib 38 | source: hosted 39 | version: "0.11.0+4" 40 | di: 41 | description: di 42 | source: hosted 43 | version: "3.3.3" 44 | html5lib: 45 | description: html5lib 46 | source: hosted 47 | version: "0.12.0" 48 | initialize: 49 | description: initialize 50 | source: hosted 51 | version: "0.2.0" 52 | intl: 53 | description: intl 54 | source: hosted 55 | version: "0.8.10+4" 56 | logging: 57 | description: logging 58 | source: hosted 59 | version: "0.9.1+1" 60 | matcher: 61 | description: matcher 62 | source: hosted 63 | version: "0.10.0" 64 | meta: 65 | description: meta 66 | source: hosted 67 | version: "0.8.8" 68 | mock: 69 | description: mock 70 | source: hosted 71 | version: "0.11.0+2" 72 | observe: 73 | description: observe 74 | source: hosted 75 | version: "0.12.2+1" 76 | path: 77 | description: path 78 | source: hosted 79 | version: "1.3.3" 80 | perf_api: 81 | description: perf_api 82 | source: hosted 83 | version: "0.0.9" 84 | pool: 85 | description: pool 86 | source: hosted 87 | version: "1.0.1" 88 | route_hierarchical: 89 | description: route_hierarchical 90 | source: hosted 91 | version: "0.6.1+1" 92 | smoke: 93 | description: smoke 94 | source: hosted 95 | version: "0.3.1" 96 | source_maps: 97 | description: source_maps 98 | source: hosted 99 | version: "0.10.0+1" 100 | source_span: 101 | description: source_span 102 | source: hosted 103 | version: "1.0.0" 104 | stack_trace: 105 | description: stack_trace 106 | source: hosted 107 | version: "0.9.3+1" 108 | unittest: 109 | description: unittest 110 | source: hosted 111 | version: "0.11.0+5" 112 | utf: 113 | description: utf 114 | source: hosted 115 | version: "0.9.0" 116 | watcher: 117 | description: watcher 118 | source: hosted 119 | version: "0.9.4" 120 | web_components: 121 | description: web_components 122 | source: hosted 123 | version: "0.10.1" 124 | when: 125 | description: when 126 | source: hosted 127 | version: "0.2.0" 128 | which: 129 | description: which 130 | source: hosted 131 | version: "0.1.3" 132 | -------------------------------------------------------------------------------- /Chapter_04/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See http://pub.dartlang.org/doc/glossary.html#lockfile 3 | packages: 4 | analyzer: 5 | description: analyzer 6 | source: hosted 7 | version: "0.22.4" 8 | angular: 9 | description: angular 10 | source: hosted 11 | version: "1.1.0" 12 | args: 13 | description: args 14 | source: hosted 15 | version: "0.12.2+6" 16 | barback: 17 | description: barback 18 | source: hosted 19 | version: "0.15.2+4" 20 | browser: 21 | description: browser 22 | source: hosted 23 | version: "0.10.0+2" 24 | cli_util: 25 | description: cli_util 26 | source: hosted 27 | version: "0.0.1+1" 28 | code_transformers: 29 | description: code_transformers 30 | source: hosted 31 | version: "0.2.5" 32 | collection: 33 | description: collection 34 | source: hosted 35 | version: "0.9.1" 36 | csslib: 37 | description: csslib 38 | source: hosted 39 | version: "0.11.0+4" 40 | di: 41 | description: di 42 | source: hosted 43 | version: "3.3.3" 44 | html5lib: 45 | description: html5lib 46 | source: hosted 47 | version: "0.12.0" 48 | initialize: 49 | description: initialize 50 | source: hosted 51 | version: "0.2.0" 52 | intl: 53 | description: intl 54 | source: hosted 55 | version: "0.8.10+4" 56 | logging: 57 | description: logging 58 | source: hosted 59 | version: "0.9.1+1" 60 | matcher: 61 | description: matcher 62 | source: hosted 63 | version: "0.10.0" 64 | meta: 65 | description: meta 66 | source: hosted 67 | version: "0.8.8" 68 | mock: 69 | description: mock 70 | source: hosted 71 | version: "0.11.0+2" 72 | observe: 73 | description: observe 74 | source: hosted 75 | version: "0.12.2+1" 76 | path: 77 | description: path 78 | source: hosted 79 | version: "1.3.3" 80 | perf_api: 81 | description: perf_api 82 | source: hosted 83 | version: "0.0.9" 84 | pool: 85 | description: pool 86 | source: hosted 87 | version: "1.0.1" 88 | route_hierarchical: 89 | description: route_hierarchical 90 | source: hosted 91 | version: "0.6.1+1" 92 | smoke: 93 | description: smoke 94 | source: hosted 95 | version: "0.3.1" 96 | source_maps: 97 | description: source_maps 98 | source: hosted 99 | version: "0.10.0+1" 100 | source_span: 101 | description: source_span 102 | source: hosted 103 | version: "1.0.0" 104 | stack_trace: 105 | description: stack_trace 106 | source: hosted 107 | version: "0.9.3+1" 108 | unittest: 109 | description: unittest 110 | source: hosted 111 | version: "0.11.0+5" 112 | utf: 113 | description: utf 114 | source: hosted 115 | version: "0.9.0" 116 | watcher: 117 | description: watcher 118 | source: hosted 119 | version: "0.9.4" 120 | web_components: 121 | description: web_components 122 | source: hosted 123 | version: "0.10.1" 124 | when: 125 | description: when 126 | source: hosted 127 | version: "0.2.0" 128 | which: 129 | description: which 130 | source: hosted 131 | version: "0.1.3" 132 | -------------------------------------------------------------------------------- /Chapter_05/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See http://pub.dartlang.org/doc/glossary.html#lockfile 3 | packages: 4 | analyzer: 5 | description: analyzer 6 | source: hosted 7 | version: "0.22.4" 8 | angular: 9 | description: angular 10 | source: hosted 11 | version: "1.1.0" 12 | args: 13 | description: args 14 | source: hosted 15 | version: "0.12.2+6" 16 | barback: 17 | description: barback 18 | source: hosted 19 | version: "0.15.2+4" 20 | browser: 21 | description: browser 22 | source: hosted 23 | version: "0.10.0+2" 24 | cli_util: 25 | description: cli_util 26 | source: hosted 27 | version: "0.0.1+1" 28 | code_transformers: 29 | description: code_transformers 30 | source: hosted 31 | version: "0.2.5" 32 | collection: 33 | description: collection 34 | source: hosted 35 | version: "0.9.1" 36 | csslib: 37 | description: csslib 38 | source: hosted 39 | version: "0.11.0+4" 40 | di: 41 | description: di 42 | source: hosted 43 | version: "3.3.3" 44 | html5lib: 45 | description: html5lib 46 | source: hosted 47 | version: "0.12.0" 48 | initialize: 49 | description: initialize 50 | source: hosted 51 | version: "0.2.0" 52 | intl: 53 | description: intl 54 | source: hosted 55 | version: "0.8.10+4" 56 | logging: 57 | description: logging 58 | source: hosted 59 | version: "0.9.1+1" 60 | matcher: 61 | description: matcher 62 | source: hosted 63 | version: "0.10.0" 64 | meta: 65 | description: meta 66 | source: hosted 67 | version: "0.8.8" 68 | mock: 69 | description: mock 70 | source: hosted 71 | version: "0.11.0+2" 72 | observe: 73 | description: observe 74 | source: hosted 75 | version: "0.12.2+1" 76 | path: 77 | description: path 78 | source: hosted 79 | version: "1.3.3" 80 | perf_api: 81 | description: perf_api 82 | source: hosted 83 | version: "0.0.9" 84 | pool: 85 | description: pool 86 | source: hosted 87 | version: "1.0.1" 88 | route_hierarchical: 89 | description: route_hierarchical 90 | source: hosted 91 | version: "0.6.1+1" 92 | smoke: 93 | description: smoke 94 | source: hosted 95 | version: "0.3.1" 96 | source_maps: 97 | description: source_maps 98 | source: hosted 99 | version: "0.10.0+1" 100 | source_span: 101 | description: source_span 102 | source: hosted 103 | version: "1.0.0" 104 | stack_trace: 105 | description: stack_trace 106 | source: hosted 107 | version: "0.9.3+1" 108 | unittest: 109 | description: unittest 110 | source: hosted 111 | version: "0.11.0+5" 112 | utf: 113 | description: utf 114 | source: hosted 115 | version: "0.9.0" 116 | watcher: 117 | description: watcher 118 | source: hosted 119 | version: "0.9.4" 120 | web_components: 121 | description: web_components 122 | source: hosted 123 | version: "0.10.1" 124 | when: 125 | description: when 126 | source: hosted 127 | version: "0.2.0" 128 | which: 129 | description: which 130 | source: hosted 131 | version: "0.1.3" 132 | -------------------------------------------------------------------------------- /Chapter_02/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See http://pub.dartlang.org/doc/glossary.html#lockfile 3 | packages: 4 | analyzer: 5 | description: analyzer 6 | source: hosted 7 | version: "0.22.4" 8 | angular: 9 | description: angular 10 | source: hosted 11 | version: "1.1.0" 12 | args: 13 | description: args 14 | source: hosted 15 | version: "0.12.2+6" 16 | barback: 17 | description: barback 18 | source: hosted 19 | version: "0.15.2+4" 20 | browser: 21 | description: browser 22 | source: hosted 23 | version: "0.10.0+2" 24 | cli_util: 25 | description: cli_util 26 | source: hosted 27 | version: "0.0.1+1" 28 | code_transformers: 29 | description: code_transformers 30 | source: hosted 31 | version: "0.2.5" 32 | collection: 33 | description: collection 34 | source: hosted 35 | version: "0.9.4" 36 | csslib: 37 | description: csslib 38 | source: hosted 39 | version: "0.11.0+4" 40 | di: 41 | description: di 42 | source: hosted 43 | version: "3.3.3" 44 | html5lib: 45 | description: html5lib 46 | source: hosted 47 | version: "0.12.0" 48 | initialize: 49 | description: initialize 50 | source: hosted 51 | version: "0.2.0" 52 | intl: 53 | description: intl 54 | source: hosted 55 | version: "0.11.9" 56 | logging: 57 | description: logging 58 | source: hosted 59 | version: "0.9.2" 60 | matcher: 61 | description: matcher 62 | source: hosted 63 | version: "0.11.1" 64 | mock: 65 | description: mock 66 | source: hosted 67 | version: "0.11.0+2" 68 | observe: 69 | description: observe 70 | source: hosted 71 | version: "0.12.2+1" 72 | path: 73 | description: path 74 | source: hosted 75 | version: "1.3.3" 76 | perf_api: 77 | description: perf_api 78 | source: hosted 79 | version: "0.0.9" 80 | petitparser: 81 | description: petitparser 82 | source: hosted 83 | version: "1.2.2" 84 | pool: 85 | description: pool 86 | source: hosted 87 | version: "1.0.1" 88 | route_hierarchical: 89 | description: route_hierarchical 90 | source: hosted 91 | version: "0.6.1+1" 92 | smoke: 93 | description: smoke 94 | source: hosted 95 | version: "0.3.1" 96 | source_maps: 97 | description: source_maps 98 | source: hosted 99 | version: "0.9.4" 100 | source_span: 101 | description: source_span 102 | source: hosted 103 | version: "1.0.0" 104 | stack_trace: 105 | description: stack_trace 106 | source: hosted 107 | version: "1.1.1" 108 | unittest: 109 | description: unittest 110 | source: hosted 111 | version: "0.11.0+5" 112 | utf: 113 | description: utf 114 | source: hosted 115 | version: "0.9.0+1" 116 | watcher: 117 | description: watcher 118 | source: hosted 119 | version: "0.9.4" 120 | web_components: 121 | description: web_components 122 | source: hosted 123 | version: "0.10.1" 124 | when: 125 | description: when 126 | source: hosted 127 | version: "0.2.0" 128 | which: 129 | description: which 130 | source: hosted 131 | version: "0.1.3" 132 | -------------------------------------------------------------------------------- /Chapter_06/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See http://pub.dartlang.org/doc/glossary.html#lockfile 3 | packages: 4 | analyzer: 5 | description: analyzer 6 | source: hosted 7 | version: "0.22.4" 8 | angular: 9 | description: angular 10 | source: hosted 11 | version: "1.1.0" 12 | args: 13 | description: args 14 | source: hosted 15 | version: "0.12.2+6" 16 | barback: 17 | description: barback 18 | source: hosted 19 | version: "0.15.2+4" 20 | browser: 21 | description: browser 22 | source: hosted 23 | version: "0.10.0+2" 24 | cli_util: 25 | description: cli_util 26 | source: hosted 27 | version: "0.0.1+1" 28 | code_transformers: 29 | description: code_transformers 30 | source: hosted 31 | version: "0.2.5" 32 | collection: 33 | description: collection 34 | source: hosted 35 | version: "0.9.1" 36 | csslib: 37 | description: csslib 38 | source: hosted 39 | version: "0.11.0+4" 40 | di: 41 | description: di 42 | source: hosted 43 | version: "3.3.3" 44 | html5lib: 45 | description: html5lib 46 | source: hosted 47 | version: "0.12.0" 48 | http_server: 49 | description: http_server 50 | source: hosted 51 | version: "0.9.3" 52 | initialize: 53 | description: initialize 54 | source: hosted 55 | version: "0.2.0" 56 | intl: 57 | description: intl 58 | source: hosted 59 | version: "0.8.10+4" 60 | logging: 61 | description: logging 62 | source: hosted 63 | version: "0.9.1+1" 64 | matcher: 65 | description: matcher 66 | source: hosted 67 | version: "0.10.0" 68 | meta: 69 | description: meta 70 | source: hosted 71 | version: "0.8.8" 72 | mime: 73 | description: mime 74 | source: hosted 75 | version: "0.9.0" 76 | mock: 77 | description: mock 78 | source: hosted 79 | version: "0.11.0+2" 80 | observe: 81 | description: observe 82 | source: hosted 83 | version: "0.12.2+1" 84 | path: 85 | description: path 86 | source: hosted 87 | version: "1.3.3" 88 | perf_api: 89 | description: perf_api 90 | source: hosted 91 | version: "0.0.9" 92 | pool: 93 | description: pool 94 | source: hosted 95 | version: "1.0.1" 96 | route_hierarchical: 97 | description: route_hierarchical 98 | source: hosted 99 | version: "0.6.1+1" 100 | smoke: 101 | description: smoke 102 | source: hosted 103 | version: "0.3.1" 104 | source_maps: 105 | description: source_maps 106 | source: hosted 107 | version: "0.10.0+1" 108 | source_span: 109 | description: source_span 110 | source: hosted 111 | version: "1.0.0" 112 | stack_trace: 113 | description: stack_trace 114 | source: hosted 115 | version: "0.9.3+1" 116 | unittest: 117 | description: unittest 118 | source: hosted 119 | version: "0.11.0+5" 120 | utf: 121 | description: utf 122 | source: hosted 123 | version: "0.9.0" 124 | watcher: 125 | description: watcher 126 | source: hosted 127 | version: "0.9.4" 128 | web_components: 129 | description: web_components 130 | source: hosted 131 | version: "0.10.1" 132 | when: 133 | description: when 134 | source: hosted 135 | version: "0.2.0" 136 | which: 137 | description: which 138 | source: hosted 139 | version: "0.1.3" 140 | -------------------------------------------------------------------------------- /Chapter_05/web/recipes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "1", 4 | "name":"Bleu Cheese Stuffed Mushrooms", 5 | "category":"Appetizers", 6 | "ingredients":[ 7 | "12 mushrooms", 8 | "8 oz bleu cheese", 9 | "1/2 red onion, diced", 10 | "1/4 cup bread crumbs", 11 | "1/4 cup parmesan cheese" 12 | ], 13 | "directions":"Preheat oven to 250 degrees F. Clean the mushrooms. Break the stems off, chop and set aside. Combine bleu cheese, red onions, bread crumbs and chopped mushroom stems. Fill each mushroom with the bleu cheese mixture and place on a baking sheet. Bake for 20 minutes, or until the tops are golden. Sprinkle with parmesan cheese if desired.", 14 | "rating": 1, 15 | "imgUrl": "fonzie1.jpg" 16 | }, 17 | { 18 | "id": "2", 19 | "name":"Cannelini Bean and Mushroom Salad", 20 | "category":"Salads", 21 | "ingredients":[ 22 | "2 cups cannelini", 23 | "a large handful of mushrooms, sliced", 24 | "3 tbsp italian parsley", 25 | "2 tbsp fresh thyme", 26 | "3 tbsp fresh chives", 27 | "a handful of cherry tomatoes, halved", 28 | "slices of parmesan cheese", 29 | "lemon juice", 30 | "olive oil", 31 | "1 garlic clove", 32 | "salt" 33 | ], 34 | "directions":"Cook and drain the beans. Coat mushrooms with olive oil and grill or pan fry them. Combine the beans, mushrooms, herbs and tomatoes. Combine lemon juice, olive oil, garlic and salt and make an emulsion. Pour the dressing over the bean mixture and stir to combine. Use a carrot peeler to peel some parmesan cheese over the top.", 35 | "rating": 3, 36 | "imgUrl": "fonzie2.jpg" 37 | }, 38 | { 39 | "id": "3", 40 | "name":"Pumpkin Black Bean Soup", 41 | "category":"Soups", 42 | "ingredients":[ 43 | "2 tablespoon extra-virgin olive oil", 44 | "1 medium onion, finely chopped", 45 | "3 cups canned or packaged vegetable stock, found on soup aisle", 46 | "1 can (14 1/2 ounces) diced tomatoes in juice", 47 | "1 can (15 ounces) black beans, drained", 48 | "2 cans (15 ounces) pumpkin puree", 49 | "1 cup heavy cream", 50 | "1 tablespoon curry powder", 51 | "1 1/2 teaspoons ground cumin", 52 | "1/2 teaspoon cayenne pepper", 53 | "Coarse salt", 54 | "20 blades fresh chives, chopped or snipped, for garnish" 55 | ], 56 | "directions":"Add oil to a soup pot on medium heat. When oil is hot, add onion. Saute onions 5 minutes. Add broth, tomatoes, black beans and pumpkin puree. Stir to combine ingredients and bring soup to a boil. Reduce heat to medium low and stir in cream, curry, cumin, cayenne and salt, to taste. Simmer 5 minutes, adjust seasonings and serve garnished with chopped chives.", 57 | "rating": 3, 58 | "imgUrl": "fonzie1.jpg" 59 | }, 60 | { 61 | "id": "4", 62 | "name":"Smoked Salmon Pasta", 63 | "category":"Main Dishes", 64 | "ingredients":[ 65 | "8 ounces spaghetti (or other) pasta", 66 | "1/4 cup pine nuts", 67 | "2 Tbsp olive oil", 68 | "1/3 cup chopped shallots (can substitute onions)", 69 | "2 cloves garlic, minced", 70 | "1/3 cup dry white wine", 71 | "1/4 cup cream", 72 | "2 Tbsp lemon zest", 73 | "1 Tbsp lemon juice", 74 | "2 Tbsp chopped fresh parsley or dill", 75 | "4 ounces smoked salmon, cut into bite sized pieces", 76 | "pinch salt", 77 | "Fresh ground black pepper" 78 | ], 79 | "directions":"Bring a pot of water to a boil over high heat to cook the pasta. Meanwhile, toast the pine nuts in a dry pan or toaster oven and set aside. Add olive oil to a sauce pan on medium heat. Add shallots and cook for 5 minutes until soft and just beginning to caramelize. Add garlic and cook until garlic is soft, but not brown. Add wine and reduce. Turn heat to low and add cream (heat needs to be low, or the cream will curdle). When the pasta is finished cooking, drain it and add to the sauce pan. Turn the heat off and stir to combine. Add lemon juice, parsley or dill, pine nuts, and salmon. Add salt and pepper to taste.", 80 | "rating": 2, 81 | "imgUrl": "fonzie2.jpg" 82 | }, 83 | { 84 | "id": "5", 85 | "name":"Pancetta Brussels Sprouts", 86 | "category":"Side Dishes", 87 | "ingredients":[ 88 | "1 lb brussels sprouts", 89 | "1 tbsp olive oil", 90 | "1 tbsp butter", 91 | "1/4 cup pancetta, chopped", 92 | "splash of balsamic vinegar" 93 | ], 94 | "directions":"Wash brussels sprouts, and chop in half. In a pan over medium heat, melt the oil and butter. Add sprouts and pancetta and turn heat to high. Cook until the sprouts are caramelized. Deglaze the pan with a splash of balsamic vinegar.", 95 | "rating": 3, 96 | "imgUrl": "fonzie1.jpg" 97 | }, 98 | { 99 | "id": "6", 100 | "name":"Almond Cookies With Chai Spices", 101 | "category":"Desserts", 102 | "ingredients":[ 103 | "1/2 cup unsalted butter, room temperature", 104 | "1 1/3 cups powdered sugar, divided", 105 | "2 tsp vanilla extract", 106 | "1 tsp almond extract", 107 | "3/4 tsp ground allspice", 108 | "3/4 tsp ground cardamom", 109 | "1/2 tsp ground cinnamon", 110 | "1/4 tsp salt", 111 | "1 cup all purpose flour", 112 | "3/4 cup finely chopped toasted almonds" 113 | ], 114 | "directions":"Preheat oven to 350 degrees F. Using electric mixer, beat butter, 1/3 cup sugar, both extracts, spices, and salt in medium bowl. Beat in flour, then stir in almonds. Using hands, roll dough into tablespoon-size balls. Place on large baking sheet, spacing apart. Bake until pale golden, about 25 minutes. Cool on sheet 5 minutes. Place remaining sugar in large bowl. Working in batches, gently coat hot cookies in sugar. Cool cookies on rack. Roll again in sugar and serve. ", 115 | "rating": 5, 116 | "imgUrl": "fonzie2.jpg" 117 | }, 118 | { 119 | "id": "7", 120 | "name":"Cardamom Poached Pears", 121 | "category":"Desserts", 122 | "ingredients":[ 123 | "2 pears, peeled", 124 | "1 1/4 cups water", 125 | "1/2 cup sugar", 126 | "3 2 inch strips of lemon zest", 127 | "1 cracked whole cardamom", 128 | "A couple grinds of fresh shaved nutmeg", 129 | "vanilla" 130 | ], 131 | "directions":"Combine all ingredients except the pears in a sauce pan and simmer for 10 minutes. Add the pears to the pan and cook, turning the pears occasionally so that all sides cook evenly. Cook for about 10 minutes, or until pears are soft enough to be poked with a fork.", 132 | "rating": 3, 133 | "imgUrl": "fonzie1.jpg" 134 | } 135 | ] 136 | -------------------------------------------------------------------------------- /Chapter_06/web/recipes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "1", 4 | "name":"Bleu Cheese Stuffed Mushrooms", 5 | "category":"Appetizers", 6 | "ingredients":[ 7 | "12 mushrooms", 8 | "8 oz bleu cheese", 9 | "1/2 red onion, diced", 10 | "1/4 cup bread crumbs", 11 | "1/4 cup parmesan cheese" 12 | ], 13 | "directions":"Preheat oven to 250 degrees F. Clean the mushrooms. Break the stems off, chop and set aside. Combine bleu cheese, red onions, bread crumbs and chopped mushroom stems. Fill each mushroom with the bleu cheese mixture and place on a baking sheet. Bake for 20 minutes, or until the tops are golden. Sprinkle with parmesan cheese if desired.", 14 | "rating": 1, 15 | "imgUrl": "fonzie1.jpg" 16 | }, 17 | { 18 | "id": "2", 19 | "name":"Cannelini Bean and Mushroom Salad", 20 | "category":"Salads", 21 | "ingredients":[ 22 | "2 cups cannelini", 23 | "a large handful of mushrooms, sliced", 24 | "3 tbsp italian parsley", 25 | "2 tbsp fresh thyme", 26 | "3 tbsp fresh chives", 27 | "a handful of cherry tomatoes, halved", 28 | "slices of parmesan cheese", 29 | "lemon juice", 30 | "olive oil", 31 | "1 garlic clove", 32 | "salt" 33 | ], 34 | "directions":"Cook and drain the beans. Coat mushrooms with olive oil and grill or pan fry them. Combine the beans, mushrooms, herbs and tomatoes. Combine lemon juice, olive oil, garlic and salt and make an emulsion. Pour the dressing over the bean mixture and stir to combine. Use a carrot peeler to peel some parmesan cheese over the top.", 35 | "rating": 3, 36 | "imgUrl": "fonzie2.jpg" 37 | }, 38 | { 39 | "id": "3", 40 | "name":"Pumpkin Black Bean Soup", 41 | "category":"Soups", 42 | "ingredients":[ 43 | "2 tablespoon extra-virgin olive oil", 44 | "1 medium onion, finely chopped", 45 | "3 cups canned or packaged vegetable stock, found on soup aisle", 46 | "1 can (14 1/2 ounces) diced tomatoes in juice", 47 | "1 can (15 ounces) black beans, drained", 48 | "2 cans (15 ounces) pumpkin puree", 49 | "1 cup heavy cream", 50 | "1 tablespoon curry powder", 51 | "1 1/2 teaspoons ground cumin", 52 | "1/2 teaspoon cayenne pepper", 53 | "Coarse salt", 54 | "20 blades fresh chives, chopped or snipped, for garnish" 55 | ], 56 | "directions":"Add oil to a soup pot on medium heat. When oil is hot, add onion. Saute onions 5 minutes. Add broth, tomatoes, black beans and pumpkin puree. Stir to combine ingredients and bring soup to a boil. Reduce heat to medium low and stir in cream, curry, cumin, cayenne and salt, to taste. Simmer 5 minutes, adjust seasonings and serve garnished with chopped chives.", 57 | "rating": 3, 58 | "imgUrl": "fonzie1.jpg" 59 | }, 60 | { 61 | "id": "4", 62 | "name":"Smoked Salmon Pasta", 63 | "category":"Main Dishes", 64 | "ingredients":[ 65 | "8 ounces spaghetti (or other) pasta", 66 | "1/4 cup pine nuts", 67 | "2 Tbsp olive oil", 68 | "1/3 cup chopped shallots (can substitute onions)", 69 | "2 cloves garlic, minced", 70 | "1/3 cup dry white wine", 71 | "1/4 cup cream", 72 | "2 Tbsp lemon zest", 73 | "1 Tbsp lemon juice", 74 | "2 Tbsp chopped fresh parsley or dill", 75 | "4 ounces smoked salmon, cut into bite sized pieces", 76 | "pinch salt", 77 | "Fresh ground black pepper" 78 | ], 79 | "directions":"Bring a pot of water to a boil over high heat to cook the pasta. Meanwhile, toast the pine nuts in a dry pan or toaster oven and set aside. Add olive oil to a sauce pan on medium heat. Add shallots and cook for 5 minutes until soft and just beginning to caramelize. Add garlic and cook until garlic is soft, but not brown. Add wine and reduce. Turn heat to low and add cream (heat needs to be low, or the cream will curdle). When the pasta is finished cooking, drain it and add to the sauce pan. Turn the heat off and stir to combine. Add lemon juice, parsley or dill, pine nuts, and salmon. Add salt and pepper to taste.", 80 | "rating": 2, 81 | "imgUrl": "fonzie2.jpg" 82 | }, 83 | { 84 | "id": "5", 85 | "name":"Pancetta Brussels Sprouts", 86 | "category":"Side Dishes", 87 | "ingredients":[ 88 | "1 lb brussels sprouts", 89 | "1 tbsp olive oil", 90 | "1 tbsp butter", 91 | "1/4 cup pancetta, chopped", 92 | "splash of balsamic vinegar" 93 | ], 94 | "directions":"Wash brussels sprouts, and chop in half. In a pan over medium heat, melt the oil and butter. Add sprouts and pancetta and turn heat to high. Cook until the sprouts are caramelized. Deglaze the pan with a splash of balsamic vinegar.", 95 | "rating": 3, 96 | "imgUrl": "fonzie1.jpg" 97 | }, 98 | { 99 | "id": "6", 100 | "name":"Almond Cookies With Chai Spices", 101 | "category":"Desserts", 102 | "ingredients":[ 103 | "1/2 cup unsalted butter, room temperature", 104 | "1 1/3 cups powdered sugar, divided", 105 | "2 tsp vanilla extract", 106 | "1 tsp almond extract", 107 | "3/4 tsp ground allspice", 108 | "3/4 tsp ground cardamom", 109 | "1/2 tsp ground cinnamon", 110 | "1/4 tsp salt", 111 | "1 cup all purpose flour", 112 | "3/4 cup finely chopped toasted almonds" 113 | ], 114 | "directions":"Preheat oven to 350 degrees F. Using electric mixer, beat butter, 1/3 cup sugar, both extracts, spices, and salt in medium bowl. Beat in flour, then stir in almonds. Using hands, roll dough into tablespoon-size balls. Place on large baking sheet, spacing apart. Bake until pale golden, about 25 minutes. Cool on sheet 5 minutes. Place remaining sugar in large bowl. Working in batches, gently coat hot cookies in sugar. Cool cookies on rack. Roll again in sugar and serve. ", 115 | "rating": 5, 116 | "imgUrl": "fonzie2.jpg" 117 | }, 118 | { 119 | "id": "7", 120 | "name":"Cardamom Poached Pears", 121 | "category":"Desserts", 122 | "ingredients":[ 123 | "2 pears, peeled", 124 | "1 1/4 cups water", 125 | "1/2 cup sugar", 126 | "3 2 inch strips of lemon zest", 127 | "1 cracked whole cardamom", 128 | "A couple grinds of fresh shaved nutmeg", 129 | "vanilla" 130 | ], 131 | "directions":"Combine all ingredients except the pears in a sauce pan and simmer for 10 minutes. Add the pears to the pan and cook, turning the pears occasionally so that all sides cook evenly. Cook for about 10 minutes, or until pears are soft enough to be poked with a fork.", 132 | "rating": 3, 133 | "imgUrl": "fonzie1.jpg" 134 | } 135 | ] 136 | --------------------------------------------------------------------------------