+ name:
+
+
+
+
+
diff --git a/Chapter_01/main.dart b/Chapter_01/main.dart
new file mode 100644
index 0000000..630aed6
--- /dev/null
+++ b/Chapter_01/main.dart
@@ -0,0 +1,5 @@
+import 'package:angular/angular.dart';
+
+main() {
+ bootstrapAngular([new AngularModule()]);
+}
diff --git a/Chapter_01/pubspec.yaml b/Chapter_01/pubspec.yaml
new file mode 100644
index 0000000..7b19e75
--- /dev/null
+++ b/Chapter_01/pubspec.yaml
@@ -0,0 +1,10 @@
+name: angular_dart_demo
+version: 0.0.1
+dependencies:
+ angular: 0.0.2
+ # git: https://github.com/angular/angular.dart.git
+ # git: https://github.com/mhevery/angular.dart.git
+
+ browser: any
+ js: any
+ unittest: any
diff --git a/Chapter_02/index.html b/Chapter_02/index.html
new file mode 100644
index 0000000..63b349d
--- /dev/null
+++ b/Chapter_02/index.html
@@ -0,0 +1,34 @@
+
+
+
+ Chapter Two - A Simple Recipe Book
+
+
+
+
+
+
Recipe List
+
+
{{recipe.name}}
+
+
+
Recipe Details
+
Name: {{ctrl.selectedRecipe.name}}
+
Category: {{ctrl.selectedRecipe.category}}
+
Rating: {{ctrl.selectedRecipe.rating}}
+
+
+
+ {{ingredient}}
+
+
+
+
Directions: {{ctrl.selectedRecipe.directions}}
+
+
+
+
+
+
diff --git a/Chapter_02/main.dart b/Chapter_02/main.dart
new file mode 100644
index 0000000..cd32903
--- /dev/null
+++ b/Chapter_02/main.dart
@@ -0,0 +1,75 @@
+import 'package:angular/angular.dart';
+
+/* Use the NgDirective annotation to indicate that this class is an
+ * Angular Directive. The compiler will instantiate the directive if
+ * it finds it in the DOM.
+ *
+ * The selector field defines the CSS selector that will trigger the
+ * directive. It can be any valid CSS selector which does not cross
+ * element boundaries.
+ *
+ * The publishAs field specifies that the directive instance should be
+ * assigned to the current scope under the name specified.
+ *
+ * The directive's public fields are available for data binding from the view.
+ * Similarly, the directive's public methods can be invoked from the view.
+ */
+@NgDirective(
+ selector: '[recipe-book]',
+ publishAs: 'ctrl')
+class RecipeBookController {
+
+ List recipes;
+ RecipeBookController() {
+ recipes = _loadData();
+ }
+
+ Recipe selectedRecipe;
+
+ void selectRecipe(Recipe recipe) {
+ selectedRecipe = recipe;
+ }
+
+ List _loadData() {
+ return [
+ new Recipe('My Appetizer','Appetizers',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 1),
+ new Recipe('My Salad','Salads',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 3),
+ new Recipe('My Soup','Soups',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 4),
+ new Recipe('My Main Dish','Main Dishes',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 2),
+ new Recipe('My Side Dish','Side Dishes',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 3),
+ new Recipe('My Awesome Dessert','Desserts',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 5),
+ new Recipe('My So-So Dessert','Desserts',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 3),
+ ];
+ }
+}
+
+class Recipe {
+ String name;
+ String category;
+ List ingredients;
+ String directions;
+ int rating;
+
+ Recipe(this.name, this.category, this.ingredients, this.directions,
+ this.rating);
+}
+
+main() {
+ var module = new AngularModule()
+ ..type(RecipeBookController);
+ bootstrapAngular([module]);
+}
diff --git a/Chapter_02/pubspec.yaml b/Chapter_02/pubspec.yaml
new file mode 100644
index 0000000..7b19e75
--- /dev/null
+++ b/Chapter_02/pubspec.yaml
@@ -0,0 +1,10 @@
+name: angular_dart_demo
+version: 0.0.1
+dependencies:
+ angular: 0.0.2
+ # git: https://github.com/angular/angular.dart.git
+ # git: https://github.com/mhevery/angular.dart.git
+
+ browser: any
+ js: any
+ unittest: any
diff --git a/Chapter_02/style.css b/Chapter_02/style.css
new file mode 100644
index 0000000..b77a62e
--- /dev/null
+++ b/Chapter_02/style.css
@@ -0,0 +1,3 @@
+.pointer {
+ cursor: pointer;
+}
\ No newline at end of file
diff --git a/Chapter_03/index.html b/Chapter_03/index.html
new file mode 100644
index 0000000..9cfc7d6
--- /dev/null
+++ b/Chapter_03/index.html
@@ -0,0 +1,41 @@
+
+
+
+ Chapter Three - A Simple Recipe Book
+
+
+
+
+
+
Recipe List
+
+
+
+ {{recipe.name}}
+
+
+
+
Recipe Details
+
+
Name: {{ctrl.selectedRecipe.name}}
+
Category: {{ctrl.selectedRecipe.category}}
+
Rating:
+
+
+
+
+
+ {{ingredient}}
+
+
+
+
Directions: {{ctrl.selectedRecipe.directions}}
+
+
+
+
+
+
+
diff --git a/Chapter_03/main.dart b/Chapter_03/main.dart
new file mode 100644
index 0000000..a38ba1e
--- /dev/null
+++ b/Chapter_03/main.dart
@@ -0,0 +1,69 @@
+library recipe_book;
+
+import 'package:angular/angular.dart';
+import 'package:di/di.dart';
+
+part 'rating_component.dart';
+
+@NgDirective(
+ selector: '[recipe-book]',
+ publishAs: 'ctrl')
+class RecipeBookController {
+
+ List recipes;
+
+ RecipeBookController() {
+ recipes = _loadData();
+ }
+
+ Recipe selectedRecipe;
+
+ void selectRecipe(Recipe recipe) {
+ selectedRecipe = recipe;
+ }
+
+ List _loadData() {
+ return [
+ new Recipe('My Appetizer','Appetizers',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 1),
+ new Recipe('My Salad','Salads',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 3),
+ new Recipe('My Soup','Soups',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 4),
+ new Recipe('My Main Dish','Main Dishes',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 2),
+ new Recipe('My Side Dish','Side Dishes',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 3),
+ new Recipe('My Awesome Dessert','Desserts',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 5),
+ new Recipe('My So-So Dessert','Desserts',
+ ["Ingredient 1", "Ingredient 2"],
+ "Some Directions", 3),
+ ];
+ }
+}
+
+class Recipe {
+ String name;
+ String category;
+ List ingredients;
+ String directions;
+ int rating;
+
+ Recipe(this.name, this.category, this.ingredients, this.directions,
+ this.rating);
+}
+
+main() {
+ var module = new AngularModule()
+ ..type(RecipeBookController)
+ ..type(RatingComponent);
+
+ bootstrapAngular([module]);
+}
\ No newline at end of file
diff --git a/Chapter_03/pubspec.yaml b/Chapter_03/pubspec.yaml
new file mode 100644
index 0000000..7b19e75
--- /dev/null
+++ b/Chapter_03/pubspec.yaml
@@ -0,0 +1,10 @@
+name: angular_dart_demo
+version: 0.0.1
+dependencies:
+ angular: 0.0.2
+ # git: https://github.com/angular/angular.dart.git
+ # git: https://github.com/mhevery/angular.dart.git
+
+ browser: any
+ js: any
+ unittest: any
diff --git a/Chapter_03/rating_component.css b/Chapter_03/rating_component.css
new file mode 100644
index 0000000..114c9ae
--- /dev/null
+++ b/Chapter_03/rating_component.css
@@ -0,0 +1,10 @@
+.star-off {
+ color: #6E6E6E;
+}
+.star-on {
+ color: #FACC2E;
+}
+.stars {
+ letter-spacing: -2px;
+ cursor: pointer;
+}
diff --git a/Chapter_03/rating_component.dart b/Chapter_03/rating_component.dart
new file mode 100644
index 0000000..d93ba95
--- /dev/null
+++ b/Chapter_03/rating_component.dart
@@ -0,0 +1,78 @@
+part of recipe_book;
+
+/* Use the NgComponent annotation to indicate that this class is an
+ * Angular Component.
+ *
+ * The selector field defines the CSS selector that will trigger the
+ * component. Typically, the CSS selector is an element name.
+ *
+ * The templateUrl field tells the component which HTML template to use
+ * for its view.
+ *
+ * The cssUrl field tells the component which CSS file to use.
+ *
+ * The publishAs field specifies that the component instance should be
+ * assigned to the current scope under the name specified.
+ *
+ * The map field publishes the list of attributes that can be set on
+ * the component. Users of this component will specify these attributes
+ * in the html tag that is used to create the component. For example:
+ *
+ *
+ *
+ * TODO: adopt new map naming conventions as soon as they're ready.
+ * OLD:
+ * 'max' : '@.max',
+ * 'rating' : '=.rating'
+ * NEW:
+ * 'max-rating' : '@maxRating',
+ * 'rating' : '=rating'
+ *
+ * The compnoent's public fields are available for data binding from the
+ * component's view. Similarly, the component's public methods can be
+ * invoked from the component's view.
+ */
+@NgComponent(
+ selector: 'rating',
+ templateUrl: 'rating_component.html',
+ cssUrl: 'rating_component.css',
+ publishAs: 'ctrl',
+ map: const {
+ 'max' : '@.max',
+ 'rating' : '=.rating'
+ }
+)
+class RatingComponent {
+ String _starOnChar = "\u2605";
+ String _starOffChar = "\u2606";
+ String _starOnClass = "star-on";
+ String _starOffClass = "star-off";
+
+ List stars = [];
+
+ int rating;
+
+ set max(String value) {
+ stars = [];
+ var count = value == null ? 5 : int.parse(value);
+ for(var i=1; i <= count; i++) {
+ stars.add(i);
+ }
+ }
+
+ String starClass(int star) {
+ return star > rating ? _starOffClass : _starOnClass;
+ }
+
+ String starChar(int star) {
+ return star > rating ? _starOffChar : _starOnChar;
+ }
+
+ void handleClick(int star) {
+ if (star == 1 && rating == 1) {
+ rating = 0;
+ } else {
+ rating = star;
+ }
+ }
+}
diff --git a/Chapter_03/rating_component.html b/Chapter_03/rating_component.html
new file mode 100644
index 0000000..4d4c94e
--- /dev/null
+++ b/Chapter_03/rating_component.html
@@ -0,0 +1,7 @@
+
+ {{ctrl.starChar(star)}}
+
diff --git a/Chapter_03/style.css b/Chapter_03/style.css
new file mode 100644
index 0000000..e0cd81b
--- /dev/null
+++ b/Chapter_03/style.css
@@ -0,0 +1,7 @@
+.pointer {
+ cursor: pointer;
+}
+
+.extra-space {
+ padding-left: 10px;
+}
diff --git a/Chapter_04/categories.json b/Chapter_04/categories.json
new file mode 100644
index 0000000..9d84deb
--- /dev/null
+++ b/Chapter_04/categories.json
@@ -0,0 +1,8 @@
+[
+ "Appetizers",
+ "Salads",
+ "Soups",
+ "Main Dishes",
+ "Side Dishes",
+ "Desserts"
+]
diff --git a/Chapter_04/index.html b/Chapter_04/index.html
new file mode 100644
index 0000000..de2a853
--- /dev/null
+++ b/Chapter_04/index.html
@@ -0,0 +1,69 @@
+
+
+
+ Chapter Four - A Simple Recipe Book
+
+
+
+
+
+
+ {{ctrl.message}}
+
+
+
Recipe List
+
+
+
+
+
+
+
+
+
+ {{category}}
+
+
+
+
+
+
+
+
+
+ {{recipe.name}}
+
+
+
+
+
+
Recipe Details
+
+
Name: {{ctrl.selectedRecipe.name}}
+
Category: {{ctrl.selectedRecipe.category}}
+
Rating:
+
+
+
+
+
+ {{ingredient}}
+
+
+
+
Directions: {{ctrl.selectedRecipe.directions}}
+
+
+
+
+
+
+
+
+
+
diff --git a/Chapter_04/main.dart b/Chapter_04/main.dart
new file mode 100644
index 0000000..db8ec6e
--- /dev/null
+++ b/Chapter_04/main.dart
@@ -0,0 +1,105 @@
+library recipe_book;
+
+import 'package:angular/angular.dart';
+import 'dart:convert';
+import 'package:perf_api/perf_api.dart';
+import 'package:di/di.dart';
+
+part 'rating_component.dart';
+part 'recipe.dart';
+
+
+@NgFilter(name: 'categoryfilter')
+class CategoryFilter {
+ call(recipeList, filterMap) {
+ if (recipeList is List && filterMap != null && filterMap is Map) {
+ // If there is nothing checked, treat it as "everything is checked"
+ bool nothingChecked = filterMap.values.every((isChecked) => !isChecked);
+ if (nothingChecked) {
+ return recipeList.toList();
+ }
+ return recipeList.where((i) => filterMap[i.category] == true).toList();
+ }
+ }
+}
+
+@NgDirective(
+ selector: '[recipe-book]',
+ publishAs: 'ctrl')
+class RecipeBookController {
+
+ static const String LOADING_MESSAGE = "Loading recipe book...";
+ static const String ERROR_MESSAGE = """Sorry! The cook stepped out of the
+kitchen and took the recipe book with him!""";
+
+ Http _http;
+
+ // Determine the initial load state of the app
+ String message = LOADING_MESSAGE;
+ bool recipesLoaded = false;
+ bool categoriesLoaded = false;
+
+ // Data objects that are loaded from the server side via json
+ List categories = [];
+ List recipes = [];
+
+ // Filter box
+ Map categoryFilterMap = {};
+ String nameFilter = "";
+
+ RecipeBookController(Http this._http) {
+ _loadData();
+ }
+
+ Recipe selectedRecipe;
+
+ void selectRecipe(Recipe recipe) {
+ selectedRecipe = recipe;
+ }
+
+ void clearFilters() {
+ categoryFilterMap.keys.forEach((f) => categoryFilterMap[f] = false);
+ nameFilter = "";
+ }
+
+ void _loadData() {
+ recipesLoaded = false;
+ categoriesLoaded = false;
+
+ _http.get('/angular.dart.tutorial/Chapter_04/recipes.json')
+ .then((HttpResponse response) {
+ for (Map recipe in response.data) {
+ recipes.add(new Recipe.fromJsonMap(recipe));
+ }
+ recipesLoaded = true;
+ },
+ onError: (Object obj) {
+ recipesLoaded = false;
+ message = ERROR_MESSAGE;
+ });
+
+ _http.get('/angular.dart.tutorial/Chapter_04/categories.json')
+ .then((HttpResponse response) {
+ for (String category in response.data) {
+ categories.add(category);
+ categoryFilterMap[category] = false;
+ }
+ categoriesLoaded = true;
+ },
+ onError: (Object obj) {
+ categoriesLoaded = false;
+ message = ERROR_MESSAGE;
+ });
+ }
+}
+
+// TODO - Remove the Profiler type. It's only needed to get rid of Misko's spam
+main() {
+ var module = new AngularModule()
+ ..type(RecipeBookController)
+ ..type(RatingComponent)
+ ..type(CategoryFilter)
+ ..type(Profiler, implementedBy: Profiler);
+
+ ngBootstrap(module: module);
+}
diff --git a/Chapter_04/pubspec.yaml b/Chapter_04/pubspec.yaml
new file mode 100644
index 0000000..8b6bc5d
--- /dev/null
+++ b/Chapter_04/pubspec.yaml
@@ -0,0 +1,11 @@
+name: angular_dart_demo
+version: 0.0.1
+dependencies:
+ angular:
+ # 0.0.2
+ git: https://github.com/angular/angular.dart.git
+ # git: https://github.com/mhevery/angular.dart.git
+
+ browser: any
+ js: any
+ unittest: any
diff --git a/Chapter_04/rating_component.css b/Chapter_04/rating_component.css
new file mode 100644
index 0000000..114c9ae
--- /dev/null
+++ b/Chapter_04/rating_component.css
@@ -0,0 +1,10 @@
+.star-off {
+ color: #6E6E6E;
+}
+.star-on {
+ color: #FACC2E;
+}
+.stars {
+ letter-spacing: -2px;
+ cursor: pointer;
+}
diff --git a/Chapter_04/rating_component.dart b/Chapter_04/rating_component.dart
new file mode 100644
index 0000000..3639717
--- /dev/null
+++ b/Chapter_04/rating_component.dart
@@ -0,0 +1,78 @@
+part of recipe_book;
+
+/* Use the NgComponent annotation to indicate that this class is an
+ * Angular Component.
+ *
+ * The selector field defines the CSS selector that will trigger the
+ * component. Typically, the CSS selector is an element name.
+ *
+ * The templateUrl field tells the component which HTML template to use
+ * for its view.
+ *
+ * The cssUrl field tells the component which CSS file to use.
+ *
+ * The publishAs field specifies that the component instance should be
+ * assigned to the current scope under the name specified.
+ *
+ * The map field publishes the list of attributes that can be set on
+ * the component. Users of this component will specify these attributes
+ * in the html tag that is used to create the component. For example:
+ *
+ *
+ *
+ * TODO: adopt new map naming conventions as soon as they're ready.
+ * OLD:
+ * 'max' : '@.max',
+ * 'rating' : '=.rating'
+ * NEW:
+ * 'max-rating' : '@maxRating',
+ * 'rating' : '<=>rating'
+ *
+ * The compnoent's public fields are available for data binding from the
+ * component's view. Similarly, the component's public methods can be
+ * invoked from the component's view.
+ */
+@NgComponent(
+ selector: 'rating',
+ templateUrl: 'rating_component.html',
+ cssUrl: 'rating_component.css',
+ publishAs: 'ctrl',
+ map: const {
+ 'max-rating' : '@maxRating',
+ 'rating' : '<=>rating'
+ }
+)
+class RatingComponent {
+ String _starOnChar = "\u2605";
+ String _starOffChar = "\u2606";
+ String _starOnClass = "star-on";
+ String _starOffClass = "star-off";
+
+ List stars = [];
+
+ int rating;
+
+ set maxRating(String value) {
+ stars = [];
+ var count = value == null ? 5 : int.parse(value);
+ for(var i=1; i <= count; i++) {
+ stars.add(i);
+ }
+ }
+
+ String starClass(int star) {
+ return star > rating ? _starOffClass : _starOnClass;
+ }
+
+ String starChar(int star) {
+ return star > rating ? _starOffChar : _starOnChar;
+ }
+
+ void handleClick(int star) {
+ if (star == 1 && rating == 1) {
+ rating = 0;
+ } else {
+ rating = star;
+ }
+ }
+}
diff --git a/Chapter_04/rating_component.html b/Chapter_04/rating_component.html
new file mode 100644
index 0000000..4d4c94e
--- /dev/null
+++ b/Chapter_04/rating_component.html
@@ -0,0 +1,7 @@
+
+ {{ctrl.starChar(star)}}
+
diff --git a/Chapter_04/recipe.dart b/Chapter_04/recipe.dart
new file mode 100644
index 0000000..cb3ab0b
--- /dev/null
+++ b/Chapter_04/recipe.dart
@@ -0,0 +1,28 @@
+part of recipe_book;
+
+class Recipe {
+ String name;
+ String category;
+ List ingredients;
+ String directions;
+ int rating;
+
+ Recipe(this.name, this.category, this.ingredients, this.directions,
+ this.rating);
+
+ String toJsonString() {
+ Map data = {
+ "name" : name,
+ "category" : category,
+ "ingredients" : ingredients,
+ "directions" : directions,
+ "rating" : rating
+ };
+ return JSON.encode(data);
+ }
+
+ factory Recipe.fromJsonMap(Map json) {
+ return new Recipe(json['name'], json['category'], json['ingredients'],
+ json['directions'], json['rating']);
+ }
+}
diff --git a/Chapter_04/recipes.json b/Chapter_04/recipes.json
new file mode 100644
index 0000000..a55e63e
--- /dev/null
+++ b/Chapter_04/recipes.json
@@ -0,0 +1,121 @@
+[
+ {
+ "name":"Bleu Cheese Stuffed Mushrooms",
+ "category":"Appetizers",
+ "ingredients":[
+ "12 mushrooms",
+ "8 oz bleu cheese",
+ "1/2 red onion, diced",
+ "1/4 cup bread crumbs",
+ "1/4 cup parmesan cheese"
+ ],
+ "directions":"Preheat oven to 250 degrees. 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.",
+ "rating": 1
+ },
+ {
+ "name":"Cannelini Bean and Mushroom Salad",
+ "category":"Salads",
+ "ingredients":[
+ "2 cups cannelini",
+ "a large handful of mushrooms, sliced",
+ "3 tbsp italian parsley",
+ "2 tbsp fresh thyme",
+ "3 tbsp fresh chives",
+ "a handful of cherry tomatoes, halved",
+ "slices of parmesan cheese",
+ "lemon juice",
+ "olive oil",
+ "1 garlic clove",
+ "salt"
+ ],
+ "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.",
+ "rating": 3
+ },
+ {
+ "name":"Pumpkin Black Bean Soup",
+ "category":"Soups",
+ "ingredients":[
+ "2 tablespoon extra-virgin olive oil",
+ "1 medium onion, finely chopped",
+ "3 cups canned or packaged vegetable stock, found on soup aisle",
+ "1 can (14 1/2 ounces) diced tomatoes in juice",
+ "1 can (15 ounces) black beans, drained",
+ "2 cans (15 ounces) pumpkin puree",
+ "1 cup heavy cream",
+ "1 tablespoon curry powder",
+ "1 1/2 teaspoons ground cumin",
+ "1/2 teaspoon cayenne pepper",
+ "Coarse salt",
+ "20 blades fresh chives, chopped or snipped, for garnish"
+ ],
+ "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.",
+ "rating": 3
+ },
+ {
+ "name":"Smoked Salmon Pasta",
+ "category":"Main Dishes",
+ "ingredients":[
+ "8 ounces spaghetti (or other) pasta",
+ "1/4 cup pine nuts",
+ "2 Tbsp olive oil",
+ "1/3 cup chopped shallots (can substitute onions)",
+ "2 cloves garlic, minced",
+ "1/3 cup dry white wine",
+ "1/4 cup cream",
+ "2 Tbsp lemon zest",
+ "1 Tbsp lemon juice",
+ "2 Tbsp chopped fresh parsley or dill",
+ "4 ounces smoked salmon, cut into bite sized pieces",
+ "pinch salt",
+ "Fresh ground black pepper"
+ ],
+ "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.",
+ "rating": 2
+ },
+ {
+ "name":"Pancetta Brussels Sprouts",
+ "category":"Side Dishes",
+ "ingredients":[
+ "1 lb brussels sprouts",
+ "1 tbsp olive oil",
+ "1 tbsp butter",
+ "1/4 cup pancetta, chopped",
+ "splash of balsamic vinegar"
+ ],
+ "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.",
+ "rating": 3
+ },
+ {
+ "name":"Almond Cookies With Chai Spices",
+ "category":"Desserts",
+ "ingredients":[
+ "1/2 cup unsalted butter, room temperature",
+ "1 1/3 cups powdered sugar, divided",
+ "2 tsp vanilla extract",
+ "1 tsp almond extract",
+ "3/4 tsp ground allspice",
+ "3/4 tsp ground cardamom",
+ "1/2 tsp ground cinnamon",
+ "1/4 tsp salt",
+ "1 cup all purpose flour",
+ "3/4 cup finely chopped toasted almonds"
+ ],
+ "directions":"Preheat oven to 350 degrees. 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. ",
+ "rating": 5
+ },
+ {
+ "name":"Cardamom Poached Pears",
+ "category":"Desserts",
+ "ingredients":[
+ "2 pears, peeled",
+ "1 1/4 cups water",
+ "1/2 cup sugar",
+ "3 2 inch strips of lemon zest",
+ "1 cracked whole cardamom",
+ "A couple grinds of fresh shaved nutmeg",
+ "vanilla"
+ ],
+ "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.",
+ "rating": 3
+ }
+]
diff --git a/Chapter_04/style.css b/Chapter_04/style.css
new file mode 100644
index 0000000..d575a8a
--- /dev/null
+++ b/Chapter_04/style.css
@@ -0,0 +1,15 @@
+.pointer {
+ cursor: pointer;
+}
+
+.extra-space {
+ padding-left: 10px;
+}
+
+[ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
+ display: none !important;
+}
+
+ul {
+ list-style-type: none;
+}
\ No newline at end of file
diff --git a/Chapter_05/add.html b/Chapter_05/add.html
new file mode 100644
index 0000000..5c5569c
--- /dev/null
+++ b/Chapter_05/add.html
@@ -0,0 +1,4 @@
+
+
Add recipe
+ Now it's your turn. Write some code to add a new recipe
+
diff --git a/Chapter_05/categories.json b/Chapter_05/categories.json
new file mode 100644
index 0000000..9d84deb
--- /dev/null
+++ b/Chapter_05/categories.json
@@ -0,0 +1,8 @@
+[
+ "Appetizers",
+ "Salads",
+ "Soups",
+ "Main Dishes",
+ "Side Dishes",
+ "Desserts"
+]
diff --git a/Chapter_05/category_filter.dart b/Chapter_05/category_filter.dart
new file mode 100644
index 0000000..d5377dd
--- /dev/null
+++ b/Chapter_05/category_filter.dart
@@ -0,0 +1,16 @@
+part of recipe_book;
+
+@NgFilter(name: 'categoryfilter')
+class CategoryFilter {
+ call(recipeList, filterMap) {
+ if (recipeList is List && filterMap != null && filterMap is Map) {
+ // If there is nothing checked, treat it as "everything is checked"
+ bool nothingChecked = filterMap.values.every((isChecked) => !isChecked);
+ if (nothingChecked) {
+ return recipeList.toList();
+ }
+ return recipeList.where((i) => filterMap[i.category] == true).toList();
+ }
+ }
+}
+
diff --git a/Chapter_05/edit.html b/Chapter_05/edit.html
new file mode 100644
index 0000000..17def10
--- /dev/null
+++ b/Chapter_05/edit.html
@@ -0,0 +1,4 @@
+
+
Edit recipe
+ Now it's your turn. Write some code to edit the recipe
+
diff --git a/Chapter_05/index.html b/Chapter_05/index.html
new file mode 100644
index 0000000..68f504d
--- /dev/null
+++ b/Chapter_05/index.html
@@ -0,0 +1,52 @@
+
+
+
+ Chapter Five - A Simple Recipe Book
+
+
+
+
+