├── .gitignore
├── LICENSE
├── README.md
├── ch01_01_2way_binding_simple
├── index.html
└── lib
│ └── angular
│ └── angular.js
├── ch01_02_2way_binding_colorpicker
├── index.html
└── lib
│ └── angular
│ └── angular.js
├── ch01_03_colorpicker_directive
├── colorPickerTemplate.html
├── index.html
├── lib
│ └── angular
│ │ └── angular.js
└── scripts
│ └── colorPickerApp.js
├── ch01_04_filter
├── index.html
├── lib
│ └── angular
│ │ └── angular.js
└── scripts
│ └── dateApp.js
├── ch02_01_controller_model
├── index.html
├── lib
│ └── angular
│ │ └── angular.js
└── scripts
│ └── myApp.js
├── ch02_02_route
├── index.html
├── lib
│ ├── angular-route
│ │ └── angular-route.js
│ └── angular
│ │ └── angular.js
├── scripts
│ └── myApp.js
└── templates
│ ├── mainTemplate.html
│ ├── userDetailsTemplate.html
│ └── userOverviewTemplate.html
├── ch02_03_template_expression
├── index.html
├── lib
│ └── angular
│ │ └── angular.js
└── scripts
│ └── myApp.js
├── ch02_04_filter
├── index.html
├── lib
│ └── angular
│ │ └── angular.js
└── scripts
│ └── myApp.js
├── ch02_05_service_factory
├── index.html
├── lib
│ └── angular
│ │ └── angular.js
└── scripts
│ └── myApp.js
├── ch02_06_service_provider
├── index.html
├── lib
│ └── angular
│ │ └── angular.js
└── scripts
│ └── myApp.js
├── ch02_07_directive
├── index.html
├── lib
│ └── angular
│ │ └── angular.js
├── scripts
│ ├── app.js
│ ├── controllers
│ │ └── mainCtrl.js
│ └── directives
│ │ └── colorPickerDirective.js
└── templates
│ └── colorPickerTemplate.html
├── ch03_01_project_init
└── app
│ ├── index.html
│ └── lib
│ ├── angular-route
│ └── angular-route.js
│ └── angular
│ └── angular.js
├── ch03_02_details_view
├── app
│ ├── index.html
│ ├── lib
│ │ ├── angular-mocks
│ │ │ └── angular-mocks.js
│ │ ├── angular-route
│ │ │ └── angular-route.js
│ │ └── angular
│ │ │ └── angular.js
│ ├── scripts
│ │ ├── app.js
│ │ └── controllers
│ │ │ └── book_details.js
│ └── templates
│ │ ├── book_details.html
│ │ └── book_details_with_expressions.html
├── karma-e2e.conf.js
├── karma.conf.js
├── package.json
└── test
│ └── e2e
│ └── templates
│ └── book_details.spec.js
├── ch03_03_list_view
├── app
│ ├── index.html
│ ├── lib
│ │ ├── angular-mocks
│ │ │ └── angular-mocks.js
│ │ ├── angular-route
│ │ │ └── angular-route.js
│ │ └── angular
│ │ │ └── angular.js
│ ├── scripts
│ │ ├── app.js
│ │ └── controllers
│ │ │ ├── book_details.js
│ │ │ └── book_list.js
│ └── templates
│ │ ├── book_details.html
│ │ ├── book_details_with_expressions.html
│ │ └── book_list.html
├── karma-e2e.conf.js
├── karma.conf.js
├── package.json
└── test
│ └── e2e
│ └── templates
│ ├── book_details.spec.js
│ └── book_list.spec.js
├── ch03_04_navigation
├── app
│ ├── index.html
│ ├── lib
│ │ ├── angular-mocks
│ │ │ └── angular-mocks.js
│ │ ├── angular-route
│ │ │ └── angular-route.js
│ │ └── angular
│ │ │ └── angular.js
│ ├── scripts
│ │ ├── app.js
│ │ └── controllers
│ │ │ ├── book_details.js
│ │ │ └── book_list.js
│ └── templates
│ │ ├── book_details.html
│ │ ├── book_details_with_expressions.html
│ │ └── book_list.html
├── karma-e2e.conf.js
├── karma.conf.js
├── package.json
└── test
│ └── e2e
│ └── templates
│ ├── book_details.spec.js
│ └── book_list.spec.js
├── ch03_05_first_service
├── app
│ ├── index.html
│ ├── lib
│ │ ├── angular-mocks
│ │ │ └── angular-mocks.js
│ │ ├── angular-route
│ │ │ └── angular-route.js
│ │ └── angular
│ │ │ └── angular.js
│ ├── scripts
│ │ ├── app.js
│ │ ├── controllers
│ │ │ ├── book_details.js
│ │ │ └── book_list.js
│ │ └── services
│ │ │ └── book_data.js
│ └── templates
│ │ ├── book_details.html
│ │ ├── book_details_with_expressions.html
│ │ └── book_list.html
├── karma-e2e.conf.js
├── karma.conf.js
├── package.json
└── test
│ ├── e2e
│ └── templates
│ │ ├── book_details.spec.js
│ │ └── book_list.spec.js
│ └── unit
│ └── services
│ └── book_data.spec.js
├── ch04_00_backend
├── .gitignore
├── package.json
└── server.js
├── ch04_01_admin
├── .gitignore
├── app
│ ├── index.html
│ ├── lib
│ │ ├── angular-mocks
│ │ │ └── angular-mocks.js
│ │ ├── angular-route
│ │ │ └── angular-route.js
│ │ └── angular
│ │ │ └── angular.js
│ ├── scripts
│ │ ├── app.js
│ │ ├── controllers
│ │ │ ├── admin_book_list.js
│ │ │ ├── admin_delete_book.js
│ │ │ ├── admin_edit_book.js
│ │ │ ├── admin_new_book.js
│ │ │ ├── book_details.js
│ │ │ └── book_list.js
│ │ └── services
│ │ │ └── book_data.js
│ ├── styles
│ │ └── main.css
│ └── templates
│ │ ├── admin
│ │ ├── book_delete.html
│ │ └── book_form.html
│ │ ├── book_details.html
│ │ ├── book_details_with_expressions.html
│ │ └── book_list.html
├── karma-e2e.conf.js
├── karma.conf.js
├── package.json
└── test
│ ├── e2e
│ └── templates
│ │ ├── admin_delete_book.spec.js
│ │ ├── admin_edit_book.spec.js
│ │ ├── admin_new_book.spec.js
│ │ ├── book_details.spec.js
│ │ └── book_list.spec.js
│ └── unit
│ └── services
│ └── book_data.spec.js
├── ch04_02_tags
├── app
│ ├── component_templates
│ │ └── directives
│ │ │ └── tags.html
│ ├── index.html
│ ├── lib
│ │ ├── angular-mocks
│ │ │ └── angular-mocks.js
│ │ ├── angular-route
│ │ │ └── angular-route.js
│ │ ├── angular
│ │ │ └── angular.js
│ │ ├── bootstrap-tokenfield
│ │ │ ├── bootstrap-tokenfield.css
│ │ │ ├── bootstrap-tokenfield.js
│ │ │ └── bootstrap-tokenfield.min.js
│ │ ├── bootstrap
│ │ │ └── bootstrap.min.css
│ │ ├── jquery-ui
│ │ │ ├── images
│ │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png
│ │ │ │ └── ui-bg_glass_75_dadada_1x400.png
│ │ │ ├── jquery-ui.css
│ │ │ └── jquery-ui.min.js
│ │ └── jquery
│ │ │ ├── jquery-1.10.2.min.js
│ │ │ └── jquery-1.10.2.min.map
│ ├── scripts
│ │ ├── app.js
│ │ ├── controllers
│ │ │ ├── admin_book_list.js
│ │ │ ├── admin_delete_book.js
│ │ │ ├── admin_edit_book.js
│ │ │ ├── admin_new_book.js
│ │ │ ├── book_details.js
│ │ │ └── book_list.js
│ │ ├── directives
│ │ │ ├── tags.js
│ │ │ └── tokenfield.js
│ │ └── services
│ │ │ └── book_data.js
│ ├── styles
│ │ └── main.css
│ └── templates
│ │ ├── admin
│ │ ├── book_delete.html
│ │ └── book_form.html
│ │ ├── book_details.html
│ │ ├── book_details_with_expressions.html
│ │ └── book_list.html
├── karma-e2e.conf.js
├── karma.conf.js
├── package.json
└── test
│ ├── e2e
│ └── templates
│ │ ├── admin_delete_book.spec.js
│ │ ├── admin_edit_book.spec.js
│ │ ├── admin_new_book.spec.js
│ │ ├── book_details.spec.js
│ │ └── book_list.spec.js
│ └── unit
│ ├── directives
│ └── tokenfield.spec.js
│ └── services
│ └── book_data.spec.js
├── ch04_03_http
├── app
│ ├── component_templates
│ │ └── directives
│ │ │ └── tags.html
│ ├── index.html
│ ├── lib
│ │ ├── angular-mocks
│ │ │ └── angular-mocks.js
│ │ ├── angular-route
│ │ │ └── angular-route.js
│ │ ├── angular
│ │ │ └── angular.js
│ │ ├── bootstrap-tokenfield
│ │ │ ├── bootstrap-tokenfield.css
│ │ │ ├── bootstrap-tokenfield.js
│ │ │ └── bootstrap-tokenfield.min.js
│ │ ├── bootstrap
│ │ │ └── bootstrap.min.css
│ │ ├── jquery-ui
│ │ │ ├── images
│ │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png
│ │ │ │ └── ui-bg_glass_75_dadada_1x400.png
│ │ │ ├── jquery-ui.css
│ │ │ └── jquery-ui.min.js
│ │ └── jquery
│ │ │ ├── jquery-1.10.2.min.js
│ │ │ └── jquery-1.10.2.min.map
│ ├── scripts
│ │ ├── app.js
│ │ ├── controllers
│ │ │ ├── admin_book_list.js
│ │ │ ├── admin_delete_book.js
│ │ │ ├── admin_edit_book.js
│ │ │ ├── admin_new_book.js
│ │ │ ├── admin_reset.js
│ │ │ ├── book_details.js
│ │ │ └── book_list.js
│ │ ├── directives
│ │ │ ├── tags.js
│ │ │ └── tokenfield.js
│ │ └── services
│ │ │ └── book_data.js
│ ├── styles
│ │ └── main.css
│ └── templates
│ │ ├── admin
│ │ ├── book_delete.html
│ │ ├── book_form.html
│ │ └── reset.html
│ │ ├── book_details.html
│ │ ├── book_details_with_expressions.html
│ │ └── book_list.html
├── karma-e2e.conf.js
├── karma.conf.js
├── package.json
└── test
│ ├── e2e
│ └── templates
│ │ ├── admin_delete_book.spec.js
│ │ ├── admin_edit_book.spec.js
│ │ ├── admin_new_book.spec.js
│ │ ├── book_details.spec.js
│ │ └── book_list.spec.js
│ └── unit
│ ├── directives
│ └── tokenfield.spec.js
│ └── services
│ └── book_data.spec.js
├── ch07_01_requirejs
├── app
│ ├── .gitignore
│ ├── bower.json
│ ├── index.html
│ ├── scripts
│ │ ├── app.js
│ │ ├── config
│ │ │ └── routes.js
│ │ ├── controllers
│ │ │ ├── book_details.js
│ │ │ └── book_list.js
│ │ ├── requireConfig.js
│ │ └── services
│ │ │ └── book_data.js
│ └── templates
│ │ ├── book_details.html
│ │ ├── book_details_with_expressions.html
│ │ └── book_list.html
├── karma-e2e.conf.js
├── karma.conf.js
└── test
│ ├── e2e
│ └── templates
│ │ ├── book_details.spec.js
│ │ └── book_list.spec.js
│ ├── testRequireConfig.js
│ └── unit
│ └── services
│ └── book_data.spec.js
├── ch07_02_requirejs_cdn
├── app
│ ├── .gitignore
│ ├── index.html
│ ├── scripts
│ │ ├── app.js
│ │ ├── config
│ │ │ └── routes.js
│ │ ├── controllers
│ │ │ ├── book_details.js
│ │ │ └── book_list.js
│ │ ├── requireConfig.js
│ │ └── services
│ │ │ └── book_data.js
│ └── templates
│ │ ├── book_details.html
│ │ ├── book_details_with_expressions.html
│ │ └── book_list.html
├── karma.conf.js
└── test
│ ├── testRequireConfig.js
│ └── unit
│ └── services
│ └── book_data.spec.js
└── ch07_03_mobile
└── app
├── .gitignore
├── bower.json
├── index.html
├── scripts
├── app.js
└── directives
│ └── draggable.js
└── styles
└── draggable.css
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .idea
3 | *.iml
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 angularjs-de
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AngularJS: Eine praktische Einführung in das JavaScript-Framework
2 |
3 | In diesem Repository finden Sie die Quellcode-Beispiele zum Buch AngularJS: Eine praktische Einführung in das JavaScript-Framework von Philipp Tarasiewicz und Robin Böhm (dpunkt.verlag).
4 |
5 | ## AngularJS Version
6 | Die Beispiele nutzen AngularJS in Version 1.2.10.
7 |
--------------------------------------------------------------------------------
/ch01_01_2way_binding_simple/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Hello {{yourName}}!
13 |
14 |
15 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/ch01_02_2way_binding_colorpicker/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | R:
9 | G:
10 | B:
11 | A:
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/ch01_03_colorpicker_directive/colorPickerTemplate.html:
--------------------------------------------------------------------------------
1 | R:
4 | G:
7 | B:
10 | A:
13 |
14 |
17 |
--------------------------------------------------------------------------------
/ch01_03_colorpicker_directive/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | AngularJS Schnellstart ColorPicker
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/ch01_03_colorpicker_directive/scripts/colorPickerApp.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var colorPickerApp = angular.module('colorPickerApp', []);
4 |
5 | colorPickerApp.controller('MainCtrl', function ($scope) {
6 | $scope.onColorChange = function(r,g,b,a) {
7 | console.log('onColorChange', r, g, b, a);
8 | };
9 | });
10 |
11 | colorPickerApp.directive('colorPicker', function () {
12 | return {
13 | scope: {
14 | r: '@initR',
15 | g: '@initG',
16 | b: '@initB',
17 | a: '@initA',
18 | onChange: '&'
19 | },
20 | restrict: 'E',
21 | templateUrl: 'colorPickerTemplate.html',
22 | link: function(scope) {
23 | var COLORS = ['r', 'g', 'b', 'a'];
24 |
25 | COLORS.forEach(function(value) {
26 | scope.$watch(value, function(newValue, oldValue) {
27 | if (newValue !== oldValue) {
28 | if (angular.isFunction(scope.onChange)) {
29 | scope.onChange(generateColorChangeObject());
30 | }
31 | }
32 | });
33 | });
34 |
35 | var generateColorChangeObject = function() {
36 | var obj = {};
37 |
38 | COLORS.forEach(function(value) {
39 | obj[value] = scope[value];
40 | });
41 |
42 | return obj;
43 | };
44 | }
45 | };
46 | });
47 |
--------------------------------------------------------------------------------
/ch01_04_filter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Today's date:
9 | {{ now | date:'dd. MMMM yyyy' }}
10 | And the time is:
11 | {{ now | date:'HH:mm:ss' }}
12 | {{ 'This is an example for a long text' | truncate:18 }}
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ch01_04_filter/scripts/dateApp.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var dateApp = angular.module('dateApp', []);
4 |
5 | dateApp.controller('DateCtrl', function ($scope, $timeout) {
6 | $scope.now = 'Loading...';
7 |
8 | var updateTime = function() {
9 | $timeout(function() {
10 | $scope.now = new Date();
11 | updateTime();
12 | }, 1000);
13 | };
14 |
15 | updateTime();
16 | });
17 |
18 | dateApp.filter('truncate', function () {
19 | return function (input, charCount) {
20 | var output = input;
21 |
22 | if (output.length > charCount) {
23 | output = output.substr(0, charCount) + '...';
24 | }
25 |
26 | return output;
27 | };
28 | });
29 |
--------------------------------------------------------------------------------
/ch02_01_controller_model/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ch02_01_controller_model/scripts/myApp.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | angular.module('myApp', []);
4 |
5 | angular.module('myApp').controller('MyCtrl', function ($scope) {
6 | // a simple JavaScript object
7 | $scope.user = {
8 | name: 'John Doe',
9 | age: 27,
10 | email: 'john@doe.com'
11 | };
12 |
13 | // a simple JavaScript array
14 | $scope.family = [
15 | 'James Doe', 'Clarissa Doe', 'Ted Doe'
16 | ];
17 |
18 | // a primitive value
19 | $scope.loggedIn = true;
20 | });
21 |
--------------------------------------------------------------------------------
/ch02_02_route/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | myApp
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/ch02_02_route/scripts/myApp.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | angular.module('myApp', ['ngRoute']);
4 |
5 | angular.module('myApp').config(function ($routeProvider) {
6 | $routeProvider
7 | .when('/', {
8 | templateUrl: 'templates/mainTemplate.html',
9 | controller: 'MainCtrl'
10 | })
11 | .when('/user/:userId', {
12 | templateUrl: 'templates/userDetailsTemplate.html',
13 | controller: 'UserDetailsCtrl'
14 | })
15 | .when('/user', {
16 | templateUrl: 'templates/userOverviewTemplate.html',
17 | controller: 'UserOverviewCtrl'
18 | })
19 | .otherwise({
20 | redirectTo: '/'
21 | });
22 | });
23 |
24 | angular.module('myApp').controller('MainCtrl', function () {
25 | console.log('MainCtrl invoked!');
26 | });
27 |
28 | angular.module('myApp').controller('UserDetailsCtrl', function ($scope, $routeParams) {
29 | console.log('UserDetailsCtrl invoked with userId: ' + $routeParams.userId);
30 | $scope.userId = $routeParams.userId;
31 | });
32 |
33 | angular.module('myApp').controller('UserOverviewCtrl', function () {
34 | console.log('UserOverviewCtrl invoked!');
35 | });
36 |
--------------------------------------------------------------------------------
/ch02_02_route/templates/mainTemplate.html:
--------------------------------------------------------------------------------
1 | mainTemplate
--------------------------------------------------------------------------------
/ch02_02_route/templates/userDetailsTemplate.html:
--------------------------------------------------------------------------------
1 | userDetailsTemplate with userId: {{ userId }}
--------------------------------------------------------------------------------
/ch02_02_route/templates/userOverviewTemplate.html:
--------------------------------------------------------------------------------
1 | userOverviewTemplate
--------------------------------------------------------------------------------
/ch02_03_template_expression/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Hello, {{user.name}}!
11 | Five years ago you were
12 | {{user.age - 5}} years old.
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/ch02_03_template_expression/scripts/myApp.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | angular.module('myApp', []);
4 |
5 | angular.module('myApp').controller('MyCtrl', function ($scope) {
6 | // a simple JavaScript object
7 | $scope.user = {
8 | name: 'John Doe',
9 | age: 27,
10 | email: 'john@doe.com'
11 | };
12 |
13 | // a simple JavaScript array
14 | $scope.family = [
15 | 'James Doe', 'Clarissa Doe', 'Ted Doe'
16 | ];
17 |
18 | // a primitive value
19 | $scope.loggedIn = true;
20 | });
21 |
--------------------------------------------------------------------------------
/ch02_04_filter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Hello, {{user.name | uppercase}}!
11 | Five years ago you were
12 | {{user.age - 5}} years old.
13 |
14 |
15 |
16 |
17 |
18 | Hello, {{user.name | alternatingCase}}!
19 | Five years ago you were
20 | {{user.age - 5}} years old.
21 |
22 |
23 |
24 |
25 | Family members of {{user.name}} (all):
26 |
29 |
30 |
31 |
32 | Family members of {{user.name}} (filtered by filter-filter set to 'clarissa'):
33 |
34 | -
35 | {{member}}
36 |
37 |
38 |
39 |
40 |
41 | Family members of {{user.name}} (filtered by custom filter endsWithDoe):
42 |
45 |
46 |
47 |
48 | Family members of {{user.name}} (filtered by custom filter endsWith with parameter 'Doe'):
49 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/ch02_04_filter/scripts/myApp.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | angular.module('myApp', []);
4 |
5 | angular.module('myApp').controller('MyCtrl', function ($scope) {
6 | // a simple JavaScript object
7 | $scope.user = {
8 | name: 'John Doe',
9 | age: 27,
10 | email: 'john@doe.com'
11 | };
12 |
13 | // a simple JavaScript array
14 | $scope.family = [
15 | 'James Doe', 'Clarissa Doe', 'Ted Doe',
16 | 'Burk Smith', 'Samantha Jones', 'Bill Brooks'
17 | ];
18 |
19 | // a primitive value
20 | $scope.loggedIn = true;
21 | });
22 |
23 | angular.module('myApp').filter('alternatingCase', function () {
24 | return function (input) {
25 | var output = '',
26 | tmp;
27 |
28 | for (var i = 0; i < input.length; i++) {
29 | tmp = input.charAt(i);
30 |
31 | if (i % 2 === 0) {
32 | output += tmp.toUpperCase();
33 | }
34 | else {
35 | output += tmp.toLowerCase();
36 | }
37 | }
38 |
39 | return output;
40 | };
41 | });
42 |
43 | angular.module('myApp').filter('endsWithDoe', function () {
44 | return function (inputArray) {
45 | var outputArray = [];
46 |
47 | angular.forEach(inputArray, function (item) {
48 | if (item.length >= 3
49 | && item.substring(item.length - 3) === 'Doe') {
50 | outputArray.push(item);
51 | }
52 | });
53 |
54 | return outputArray;
55 | };
56 | });
57 |
58 | angular.module('myApp').filter('endsWith', function () {
59 | return function (inputArray, endsWith) {
60 | var outputArray = [],
61 | subString,
62 | hasMinLen,
63 | isSubStringMatching;
64 |
65 | angular.forEach(inputArray, function (item) {
66 | hasMinLen = item.length >= endsWith.length;
67 | subString = item.substring(item.length - endsWith.length);
68 | isSubStringMatching = subString === endsWith;
69 |
70 | if (hasMinLen && isSubStringMatching) {
71 | outputArray.push(item);
72 | }
73 | });
74 |
75 | return outputArray;
76 | };
77 | });
78 |
--------------------------------------------------------------------------------
/ch02_05_service_factory/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ch02_05_service_factory/scripts/myApp.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | angular.module('myApp', []);
4 |
5 | angular.module('myApp').controller('MyCtrl', function(log) {
6 | log.debug('MyCtrl has been invoked!');
7 | });
8 |
9 | angular.module('myApp').factory('log', function () {
10 | // Service Implementation
11 | var log = function (level, message) {
12 | console.log('[' + level + '] ' + message);
13 | };
14 |
15 | // Public API
16 | return {
17 | error: function (message) {
18 | log('ERROR', message);
19 | },
20 | info: function (message) {
21 | log('INFO', message);
22 | },
23 | debug: function (message) {
24 | log('DEBUG', message);
25 | }
26 | };
27 | });
28 |
--------------------------------------------------------------------------------
/ch02_06_service_provider/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ch02_06_service_provider/scripts/myApp.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | angular.module('myApp', []);
4 |
5 | angular.module('myApp').provider('log', function () {
6 | /* Private state/methods */
7 | var prefix = '',
8 | suffix = '';
9 |
10 | // Service Implementation
11 | var log = function (level, message) {
12 | console.log(prefix + '[' + level + '] ' + message + suffix);
13 | };
14 |
15 | /* Public state/methods */
16 | // Configuration methods / Public methods
17 | this.setPrefix = function (newPrefix) {
18 | prefix = newPrefix;
19 | };
20 |
21 | this.setSuffix = function (newSuffix) {
22 | suffix = newSuffix;
23 | };
24 |
25 | this.$get = function () {
26 | // Public API
27 | return {
28 | error: function (message) {
29 | log('ERROR', message);
30 | },
31 | info: function (message) {
32 | log('INFO', message);
33 | },
34 | debug: function (message) {
35 | log('DEBUG', message);
36 | }
37 | };
38 | };
39 | });
40 |
41 | angular.module('myApp').config(function(logProvider) {
42 | logProvider.setPrefix('[myApp]');
43 | logProvider.setSuffix('.');
44 | });
45 |
46 | angular.module('myApp').controller('MyCtrl', function(log) {
47 | log.debug('MyCtrl has been invoked');
48 | });
49 |
--------------------------------------------------------------------------------
/ch02_07_directive/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ch02_07_directive/scripts/app.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var colorPickerApp = angular.module('colorPickerApp', []);
4 |
--------------------------------------------------------------------------------
/ch02_07_directive/scripts/controllers/mainCtrl.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | colorPickerApp.controller('MainCtrl', function ($scope) {
4 | $scope.onColorChange = function(r,g,b,a) {
5 | console.log('onColorChange', r, g, b, a);
6 | };
7 | });
8 |
--------------------------------------------------------------------------------
/ch02_07_directive/scripts/directives/colorPickerDirective.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | colorPickerApp.directive('colorPicker', function () {
4 | return {
5 | scope: {
6 | r: '@initR',
7 | g: '@initG',
8 | b: '@initB',
9 | a: '@initA',
10 | onChange: '&'
11 | },
12 | restrict: 'E',
13 | templateUrl: 'templates/colorPickerTemplate.html',
14 | link: function(scope) {
15 | var COLORS = ['r', 'g', 'b', 'a'];
16 |
17 | COLORS.forEach(function(value) {
18 | scope.$watch(value, function(newValue, oldValue) {
19 | if (newValue !== oldValue) {
20 | if (angular.isFunction(scope.onChange)) {
21 | scope.onChange(generateColorChangeObject());
22 | }
23 | }
24 | });
25 | });
26 |
27 | var generateColorChangeObject = function() {
28 | var obj = {};
29 |
30 | COLORS.forEach(function(value) {
31 | obj[value] = scope[value];
32 | });
33 |
34 | return obj;
35 | };
36 | }
37 | };
38 | });
39 |
--------------------------------------------------------------------------------
/ch02_07_directive/templates/colorPickerTemplate.html:
--------------------------------------------------------------------------------
1 | R:
4 | G:
7 | B:
10 | A:
13 |
14 |
17 |
--------------------------------------------------------------------------------
/ch03_01_project_init/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BookMonkey
6 |
7 |
8 | Hello {{ name }}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ch03_02_details_view/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BookMonkey
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/ch03_02_details_view/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var bmApp = angular.module('bmApp', ['ngRoute']);
4 |
5 | bmApp.config(function ($routeProvider) {
6 | $routeProvider.when('/books/:isbn', {
7 | //templateUrl: 'templates/book_details_with_expressions.html',
8 | templateUrl: 'templates/book_details.html',
9 | controller: 'BookDetailsCtrl'
10 | });
11 | });
--------------------------------------------------------------------------------
/ch03_02_details_view/app/scripts/controllers/book_details.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('BookDetailsCtrl', function ($scope) {
4 | $scope.book = {
5 | title : 'JavaScript für Enterprise-Entwickler',
6 | subtitle : 'Professionell programmieren im Browser und auf dem Server',
7 | isbn : '978-3-89864-728-1',
8 | abstract : 'JavaScript ist längst nicht mehr nur für klassische Webprogrammierer interessant.',
9 | numPages : 302,
10 | author : 'Oliver Ochs',
11 | publisher : {
12 | name: 'dpunkt.verlag',
13 | url : 'http://dpunkt.de/'
14 | }
15 | };
16 | });
17 |
--------------------------------------------------------------------------------
/ch03_02_details_view/app/templates/book_details.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
9 |
11 | -
12 | Verlag:
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ch03_02_details_view/app/templates/book_details_with_expressions.html:
--------------------------------------------------------------------------------
1 | {{ book.title }}
2 | {{ book.subtitle }}
3 |
4 |
5 | - ISBN: {{ book.isbn }}
6 | - Seiten: {{ book.numPages }}
7 | - Autor: {{ book.author }}
8 | -
9 | Verlag:
10 | {{ book.publisher.name }}
13 |
14 |
15 |
16 |
17 | {{ book.abstract }}
--------------------------------------------------------------------------------
/ch03_02_details_view/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for e2e tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | basePath: '',
7 |
8 | files: [
9 | 'test/e2e/**/*.js'
10 | ],
11 |
12 | logLevel: config.LOG_DEBUG,
13 |
14 | autoWatch: false,
15 |
16 | browsers: ['Chrome'],
17 |
18 | frameworks: ['ng-scenario'],
19 |
20 | singleRun: true,
21 |
22 | urlRoot: '/_karma_/',
23 |
24 | proxies: {
25 | '/': 'http://localhost:8080/'
26 | }
27 |
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/ch03_02_details_view/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for unit tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 |
10 | // frameworks to use
11 | frameworks: ['jasmine'],
12 |
13 |
14 | // list of files / patterns to load in the browser
15 | files: [
16 | 'app/lib/angular/angular.js',
17 | 'app/lib/angular-route/angular-route.js',
18 | 'app/lib/angular-mocks/angular-mocks.js',
19 | 'app/scripts/app.js',
20 | 'app/scripts/**/*.js',
21 | 'test/unit/**/*.js'
22 | ],
23 |
24 |
25 | // list of files to exclude
26 | exclude: [
27 |
28 | ],
29 |
30 |
31 | // test results reporter to use
32 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
33 | reporters: ['progress'],
34 |
35 |
36 | // web server port
37 | port: 9876,
38 |
39 |
40 | // enable / disable colors in the output (reporters and logs)
41 | colors: true,
42 |
43 |
44 | // level of logging
45 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
46 | logLevel: config.LOG_DEBUG,
47 |
48 |
49 | // enable / disable watching file and executing tests whenever any file changes
50 | autoWatch: false,
51 |
52 |
53 | // Start these browsers, currently available:
54 | // - Chrome
55 | // - ChromeCanary
56 | // - Firefox
57 | // - Opera
58 | // - Safari (only Mac)
59 | // - PhantomJS
60 | // - IE (only Windows)
61 | browsers: ['Chrome'],
62 |
63 |
64 | // If browser does not capture in given timeout [ms], kill it
65 | captureTimeout: 60000,
66 |
67 |
68 | // Continuous Integration mode
69 | // if true, it capture browsers, run tests and exit
70 | singleRun: true
71 | });
72 | };
73 |
--------------------------------------------------------------------------------
/ch03_02_details_view/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bookmonkey",
3 | "version": "0.0.1",
4 | "description": "A simple AngularJS app serving as an example for the German book 'AngularJS: Eine praktische Einführung in das JavaScript-Framework'.",
5 | "main": "app/index.html",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "test": "karma start",
11 | "posttest": "karma start karma-e2e.conf.js"
12 | },
13 | "repository": "",
14 | "keywords": [
15 | "angularjs",
16 | "angularjs-de"
17 | ],
18 | "author": "Philipp Tarasiewicz , Robin Böhm ",
19 | "homepage": "http://angularjs.de/",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "karma": "0.12.0",
23 | "karma-chrome-launcher": "0.1.2",
24 | "karma-jasmine": "0.2.2",
25 | "karma-ng-scenario": "0.1.0",
26 | "karma-ng-html2js-preprocessor": "0.1.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ch03_02_details_view/test/e2e/templates/book_details.spec.js:
--------------------------------------------------------------------------------
1 | describe("E2E: book details view", function() {
2 |
3 | beforeEach(function() {
4 | browser().navigateTo('/');
5 | });
6 |
7 | it('should show the correct book details', function() {
8 | browser().navigateTo('#/books/978-3-89864-728-1');
9 |
10 | expect(element('.bm-book-title').html()).toBe(
11 | 'JavaScript für Enterprise-Entwickler'
12 | );
13 | expect(element('.bm-book-subtitle').html()).toBe(
14 | 'Professionell programmieren im Browser und auf dem Server'
15 | );
16 | expect(element('.bm-book-isbn').html()).toBe(
17 | 'ISBN: 978-3-89864-728-1'
18 | );
19 | expect(element('.bm-book-num-pages').html()).toBe(
20 | 'Seiten: 302'
21 | );
22 | expect(element('.bm-book-author').html()).toBe(
23 | 'Autor: Oliver Ochs'
24 | );
25 | expect(element('.bm-book-publisher-name').html()).toBe(
26 | 'dpunkt.verlag'
27 | );
28 | expect(element('.bm-book-publisher-name').attr('href')).toBe(
29 | 'http://dpunkt.de/'
30 | );
31 | expect(element('.bm-book-abstract').html()).toBe(
32 | 'JavaScript ist längst nicht mehr nur für' +
33 | ' klassische Webprogrammierer interessant.'
34 | );
35 | });
36 |
37 | });
--------------------------------------------------------------------------------
/ch03_03_list_view/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BookMonkey
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ch03_03_list_view/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var bmApp = angular.module('bmApp', ['ngRoute']);
4 |
5 | bmApp.config(function ($routeProvider) {
6 | $routeProvider.when('/books/:isbn', {
7 | //templateUrl: 'templates/book_details_with_expressions.html',
8 | templateUrl: 'templates/book_details.html',
9 | controller: 'BookDetailsCtrl'
10 | })
11 | .when('/books', {
12 | templateUrl: 'templates/book_list.html',
13 | controller: 'BookListCtrl'
14 | });
15 | });
--------------------------------------------------------------------------------
/ch03_03_list_view/app/scripts/controllers/book_details.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('BookDetailsCtrl', function ($scope) {
4 | $scope.book = {
5 | title : 'JavaScript für Enterprise-Entwickler',
6 | subtitle : 'Professionell programmieren im Browser und auf dem Server',
7 | isbn : '978-3-89864-728-1',
8 | abstract : 'JavaScript ist längst nicht mehr nur für klassische Webprogrammierer interessant.',
9 | numPages : 302,
10 | author : 'Oliver Ochs',
11 | publisher : {
12 | name: 'dpunkt.verlag',
13 | url : 'http://dpunkt.de/'
14 | }
15 | };
16 | });
17 |
--------------------------------------------------------------------------------
/ch03_03_list_view/app/scripts/controllers/book_list.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('BookListCtrl', function ($scope, $filter) {
4 | $scope.getBookOrder = function(book) {
5 | return book.title;
6 | };
7 |
8 | $scope.books = [
9 | {
10 | title : 'JavaScript für Enterprise-Entwickler',
11 | subtitle : 'Professionell programmieren im Browser und auf dem Server',
12 | isbn : '978-3-89864-728-1',
13 | abstract : 'JavaScript ist längst nicht mehr nur für klassische Webprogrammierer interessant.',
14 | numPages : 302,
15 | author : 'Oliver Ochs',
16 | publisher : {
17 | name: 'dpunkt.verlag',
18 | url : 'http://dpunkt.de/'
19 | }
20 | },
21 | {
22 | title : 'Node.js & Co.',
23 | subtitle : 'Skalierbare, hochperformante und echtzeitfähige Webanwendungen professionell in JavaScript entwickeln',
24 | isbn : '978-3-89864-829-5',
25 | abstract : 'Nach dem Webbrowser erobert JavaScript nun auch den Webserver.',
26 | numPages : 334,
27 | author : 'Golo Roden',
28 | publisher : {
29 | name: 'dpunkt.verlag',
30 | url : 'http://dpunkt.de/'
31 | }
32 | },
33 | {
34 | title : 'CoffeeScript',
35 | subtitle : 'Einfach JavaScript',
36 | isbn : '978-3-86490-050-1',
37 | abstract : 'CoffeeScript ist eine junge, kleine Programmiersprache, die nach JavaScript übersetzt wird.',
38 | numPages : 200,
39 | author : 'Andreas Schubert',
40 | publisher : {
41 | name: 'dpunkt.verlag',
42 | url : 'http://dpunkt.de/'
43 | }
44 | }
45 | ];
46 |
47 | // This is just to demonstrate the programmatic usage of a filter
48 | var orderBy = $filter('orderBy');
49 |
50 | var titles = $scope.books.map(function(book) {
51 | return {title: book.title};
52 | });
53 |
54 | console.log('titles before ordering', titles);
55 |
56 | // This is the actual invocation of the filter
57 | titles = orderBy(titles, 'title');
58 |
59 | console.log('titles after ordering', titles);
60 | });
61 |
--------------------------------------------------------------------------------
/ch03_03_list_view/app/templates/book_details.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
9 |
11 | -
12 | Verlag:
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ch03_03_list_view/app/templates/book_details_with_expressions.html:
--------------------------------------------------------------------------------
1 | {{ book.title }}
2 | {{ book.subtitle }}
3 |
4 |
5 | - ISBN: {{ book.isbn }}
6 | - Seiten: {{ book.numPages }}
7 | - Autor: {{ book.author }}
8 | -
9 | Verlag:
10 | {{ book.publisher.name }}
13 |
14 |
15 |
16 |
17 | {{ book.abstract }}
--------------------------------------------------------------------------------
/ch03_03_list_view/app/templates/book_list.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 | |
7 | |
8 | |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ch03_03_list_view/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for e2e tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | basePath: '',
7 |
8 | files: [
9 | 'test/e2e/**/*.js'
10 | ],
11 |
12 | logLevel: config.LOG_DEBUG,
13 |
14 | autoWatch: false,
15 |
16 | browsers: ['Chrome'],
17 |
18 | frameworks: ['ng-scenario'],
19 |
20 | singleRun: true,
21 |
22 | urlRoot: '/_karma_/',
23 |
24 | proxies: {
25 | '/': 'http://localhost:8080/'
26 | }
27 |
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/ch03_03_list_view/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for unit tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 |
10 | // frameworks to use
11 | frameworks: ['jasmine'],
12 |
13 |
14 | // list of files / patterns to load in the browser
15 | files: [
16 | 'app/lib/angular/angular.js',
17 | 'app/lib/angular-route/angular-route.js',
18 | 'app/lib/angular-mocks/angular-mocks.js',
19 | 'app/scripts/app.js',
20 | 'app/scripts/**/*.js',
21 | 'test/unit/**/*.js'
22 | ],
23 |
24 |
25 | // list of files to exclude
26 | exclude: [
27 |
28 | ],
29 |
30 |
31 | // test results reporter to use
32 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
33 | reporters: ['progress'],
34 |
35 |
36 | // web server port
37 | port: 9876,
38 |
39 |
40 | // enable / disable colors in the output (reporters and logs)
41 | colors: true,
42 |
43 |
44 | // level of logging
45 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
46 | logLevel: config.LOG_DEBUG,
47 |
48 |
49 | // enable / disable watching file and executing tests whenever any file changes
50 | autoWatch: false,
51 |
52 |
53 | // Start these browsers, currently available:
54 | // - Chrome
55 | // - ChromeCanary
56 | // - Firefox
57 | // - Opera
58 | // - Safari (only Mac)
59 | // - PhantomJS
60 | // - IE (only Windows)
61 | browsers: ['Chrome'],
62 |
63 |
64 | // If browser does not capture in given timeout [ms], kill it
65 | captureTimeout: 60000,
66 |
67 |
68 | // Continuous Integration mode
69 | // if true, it capture browsers, run tests and exit
70 | singleRun: true
71 | });
72 | };
73 |
--------------------------------------------------------------------------------
/ch03_03_list_view/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bookmonkey",
3 | "version": "0.0.1",
4 | "description": "A simple AngularJS app serving as an example for the German book 'AngularJS: Eine praktische Einführung in das JavaScript-Framework'.",
5 | "main": "app/index.html",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "test": "karma start",
11 | "posttest": "karma start karma-e2e.conf.js"
12 | },
13 | "repository": "",
14 | "keywords": [
15 | "angularjs",
16 | "angularjs-de"
17 | ],
18 | "author": "Philipp Tarasiewicz , Robin Böhm ",
19 | "homepage": "http://angularjs.de/",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "karma": "0.12.0",
23 | "karma-chrome-launcher": "0.1.2",
24 | "karma-jasmine": "0.2.2",
25 | "karma-ng-scenario": "0.1.0",
26 | "karma-ng-html2js-preprocessor": "0.1.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ch03_03_list_view/test/e2e/templates/book_details.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book details view", function() {
4 |
5 | beforeEach(function() {
6 | browser().navigateTo('/');
7 | });
8 |
9 | it('should show the correct book details', function() {
10 | browser().navigateTo('#/books/978-3-89864-728-1');
11 |
12 | expect(element('.bm-book-title').html()).toBe(
13 | 'JavaScript für Enterprise-Entwickler'
14 | );
15 | expect(element('.bm-book-subtitle').html()).toBe(
16 | 'Professionell programmieren im Browser und auf dem Server'
17 | );
18 | expect(element('.bm-book-isbn').html()).toBe(
19 | 'ISBN: 978-3-89864-728-1'
20 | );
21 | expect(element('.bm-book-num-pages').html()).toBe(
22 | 'Seiten: 302'
23 | );
24 | expect(element('.bm-book-author').html()).toBe(
25 | 'Autor: Oliver Ochs'
26 | );
27 | expect(element('.bm-book-publisher-name').html()).toBe(
28 | 'dpunkt.verlag'
29 | );
30 | expect(element('.bm-book-publisher-name').attr('href')).toBe(
31 | 'http://dpunkt.de/'
32 | );
33 | expect(element('.bm-book-abstract').html()).toBe(
34 | 'JavaScript ist längst nicht mehr nur für' +
35 | ' klassische Webprogrammierer interessant.'
36 | );
37 | });
38 |
39 | });
--------------------------------------------------------------------------------
/ch03_03_list_view/test/e2e/templates/book_list.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book list view", function () {
4 |
5 | // Define the array of books in the expected order.
6 | // Sorted by title.
7 | var expectedBooks = [
8 | {
9 | title: 'CoffeeScript',
10 | isbn: '978-3-86490-050-1',
11 | author: 'Andreas Schubert'
12 | },
13 | {
14 | title: 'JavaScript für Enterprise-Entwickler',
15 | isbn: '978-3-89864-728-1',
16 | author: 'Oliver Ochs'
17 | },
18 | {
19 | title: 'Node.js & Co.',
20 | isbn: '978-3-89864-829-5',
21 | author: 'Golo Roden'
22 | }
23 | ];
24 |
25 | // Derive an array that only contains titles
26 | // for easier expectation checks.
27 | var orderedTitles = expectedBooks.map(function(book) {
28 | return book.title;
29 | });
30 |
31 | beforeEach(function () {
32 | browser().navigateTo('#/books');
33 | browser().reload();
34 | });
35 |
36 | var selector = 'table.bm-book-list tr';
37 |
38 | it('should show the correct number of books', function () {
39 | expect(repeater(selector).count()).toEqual(expectedBooks.length);
40 | });
41 |
42 | it('should show the books in the proper order', function() {
43 | // Are they in the expected order (ascending sorted by title)?
44 | expect(repeater(selector).column('book.title')).toEqual(orderedTitles);
45 | });
46 |
47 | it('should show the correct book information', function() {
48 | // Do the other book details (isbn, author) match?
49 | for (var i = 0, n = expectedBooks.length; i < n; i++) {
50 | expect(repeater(selector).row(i))
51 | .toEqual(
52 | [
53 | expectedBooks[i].title,
54 | expectedBooks[i].author,
55 | expectedBooks[i].isbn
56 | ]
57 | );
58 | }
59 | });
60 |
61 | it('should allow filtering by book title', function() {
62 | // Coffee
63 | var searchText = orderedTitles[0].substr(0, 6);
64 | input('searchText').enter(searchText);
65 | expect(
66 | repeater(selector).column('book.title')
67 | ).toEqual([orderedTitles[0]]);
68 | });
69 |
70 | it('should allow filtering by author', function() {
71 | // Andreas
72 | var searchText = expectedBooks[0].author.substr(0, 7);
73 | input('searchText').enter(searchText);
74 | expect(
75 | repeater(selector).column('book.title')
76 | ).toEqual([orderedTitles[0]]);
77 | });
78 |
79 | it('should allow filtering by isbn', function() {
80 | // 050-1
81 | var searchText = expectedBooks[0].isbn.substr(-5, 5);
82 | input('searchText').enter(searchText);
83 | expect(
84 | repeater(selector).column('book.title')
85 | ).toEqual([orderedTitles[0]]);
86 | });
87 | });
--------------------------------------------------------------------------------
/ch03_04_navigation/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BookMonkey
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ch03_04_navigation/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var bmApp = angular.module('bmApp', ['ngRoute']);
4 |
5 | bmApp.config(function ($routeProvider) {
6 | $routeProvider.when('/books/:isbn', {
7 | //templateUrl: 'templates/book_details_with_expressions.html',
8 | templateUrl: 'templates/book_details.html',
9 | controller: 'BookDetailsCtrl'
10 | })
11 | .when('/books', {
12 | templateUrl: 'templates/book_list.html',
13 | controller: 'BookListCtrl'
14 | })
15 | .otherwise({
16 | redirectTo: '/books'
17 | });
18 | });
--------------------------------------------------------------------------------
/ch03_04_navigation/app/scripts/controllers/book_details.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('BookDetailsCtrl', function ($scope, $location) {
4 | $scope.book = {
5 | title : 'JavaScript für Enterprise-Entwickler',
6 | subtitle : 'Professionell programmieren im Browser und auf dem Server',
7 | isbn : '978-3-89864-728-1',
8 | abstract : 'JavaScript ist längst nicht mehr nur für klassische Webprogrammierer interessant.',
9 | numPages : 302,
10 | author : 'Oliver Ochs',
11 | publisher : {
12 | name: 'dpunkt.verlag',
13 | url : 'http://dpunkt.de/'
14 | }
15 | };
16 |
17 | $scope.goToListView = function() {
18 | $location.path('/books');
19 | };
20 | });
21 |
--------------------------------------------------------------------------------
/ch03_04_navigation/app/scripts/controllers/book_list.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('BookListCtrl', function ($scope, $filter) {
4 | $scope.getBookOrder = function(book) {
5 | return book.title;
6 | };
7 |
8 | $scope.books = [
9 | {
10 | title : 'JavaScript für Enterprise-Entwickler',
11 | subtitle : 'Professionell programmieren im Browser und auf dem Server',
12 | isbn : '978-3-89864-728-1',
13 | abstract : 'JavaScript ist längst nicht mehr nur für klassische Webprogrammierer interessant.',
14 | numPages : 302,
15 | author : 'Oliver Ochs',
16 | publisher : {
17 | name: 'dpunkt.verlag',
18 | url : 'http://dpunkt.de/'
19 | }
20 | },
21 | {
22 | title : 'Node.js & Co.',
23 | subtitle : 'Skalierbare, hochperformante und echtzeitfähige Webanwendungen professionell in JavaScript entwickeln',
24 | isbn : '978-3-89864-829-5',
25 | abstract : 'Nach dem Webbrowser erobert JavaScript nun auch den Webserver.',
26 | numPages : 334,
27 | author : 'Golo Roden',
28 | publisher : {
29 | name: 'dpunkt.verlag',
30 | url : 'http://dpunkt.de/'
31 | }
32 | },
33 | {
34 | title : 'CoffeeScript',
35 | subtitle : 'Einfach JavaScript',
36 | isbn : '978-3-86490-050-1',
37 | abstract : 'CoffeeScript ist eine junge, kleine Programmiersprache, die nach JavaScript übersetzt wird.',
38 | numPages : 200,
39 | author : 'Andreas Schubert',
40 | publisher : {
41 | name: 'dpunkt.verlag',
42 | url : 'http://dpunkt.de/'
43 | }
44 | }
45 | ];
46 |
47 | // This is just to demonstrate the programmatic usage of a filter
48 | var orderBy = $filter('orderBy');
49 |
50 | var titles = $scope.books.map(function(book) {
51 | return {title: book.title};
52 | });
53 |
54 | console.log('titles before ordering', titles);
55 |
56 | // This is the actual invocation of the filter
57 | titles = orderBy(titles, 'title');
58 |
59 | console.log('titles after ordering', titles);
60 | });
61 |
--------------------------------------------------------------------------------
/ch03_04_navigation/app/templates/book_details.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
9 |
11 | -
12 | Verlag:
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Zurück
--------------------------------------------------------------------------------
/ch03_04_navigation/app/templates/book_details_with_expressions.html:
--------------------------------------------------------------------------------
1 | {{ book.title }}
2 | {{ book.subtitle }}
3 |
4 |
5 | - ISBN: {{ book.isbn }}
6 | - Seiten: {{ book.numPages }}
7 | - Autor: {{ book.author }}
8 | -
9 | Verlag:
10 | {{ book.publisher.name }}
13 |
14 |
15 |
16 |
17 | {{ book.abstract }}
--------------------------------------------------------------------------------
/ch03_04_navigation/app/templates/book_list.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 | |
7 | |
8 | |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ch03_04_navigation/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for e2e tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | basePath: '',
7 |
8 | files: [
9 | 'test/e2e/**/*.js'
10 | ],
11 |
12 | logLevel: config.LOG_DEBUG,
13 |
14 | autoWatch: false,
15 |
16 | browsers: ['Chrome'],
17 |
18 | frameworks: ['ng-scenario'],
19 |
20 | singleRun: true,
21 |
22 | urlRoot: '/_karma_/',
23 |
24 | proxies: {
25 | '/': 'http://localhost:8080/'
26 | }
27 |
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/ch03_04_navigation/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for unit tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 |
10 | // frameworks to use
11 | frameworks: ['jasmine'],
12 |
13 |
14 | // list of files / patterns to load in the browser
15 | files: [
16 | 'app/lib/angular/angular.js',
17 | 'app/lib/angular-route/angular-route.js',
18 | 'app/lib/angular-mocks/angular-mocks.js',
19 | 'app/scripts/app.js',
20 | 'app/scripts/**/*.js',
21 | 'test/unit/**/*.js'
22 | ],
23 |
24 |
25 | // list of files to exclude
26 | exclude: [
27 |
28 | ],
29 |
30 |
31 | // test results reporter to use
32 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
33 | reporters: ['progress'],
34 |
35 |
36 | // web server port
37 | port: 9876,
38 |
39 |
40 | // enable / disable colors in the output (reporters and logs)
41 | colors: true,
42 |
43 |
44 | // level of logging
45 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
46 | logLevel: config.LOG_DEBUG,
47 |
48 |
49 | // enable / disable watching file and executing tests whenever any file changes
50 | autoWatch: false,
51 |
52 |
53 | // Start these browsers, currently available:
54 | // - Chrome
55 | // - ChromeCanary
56 | // - Firefox
57 | // - Opera
58 | // - Safari (only Mac)
59 | // - PhantomJS
60 | // - IE (only Windows)
61 | browsers: ['Chrome'],
62 |
63 |
64 | // If browser does not capture in given timeout [ms], kill it
65 | captureTimeout: 60000,
66 |
67 |
68 | // Continuous Integration mode
69 | // if true, it capture browsers, run tests and exit
70 | singleRun: true
71 | });
72 | };
73 |
--------------------------------------------------------------------------------
/ch03_04_navigation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bookmonkey",
3 | "version": "0.0.1",
4 | "description": "A simple AngularJS app serving as an example for the German book 'AngularJS: Eine praktische Einführung in das JavaScript-Framework'.",
5 | "main": "app/index.html",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "test": "karma start",
11 | "posttest": "karma start karma-e2e.conf.js"
12 | },
13 | "repository": "",
14 | "keywords": [
15 | "angularjs",
16 | "angularjs-de"
17 | ],
18 | "author": "Philipp Tarasiewicz , Robin Böhm ",
19 | "homepage": "http://angularjs.de/",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "karma": "0.12.0",
23 | "karma-chrome-launcher": "0.1.2",
24 | "karma-jasmine": "0.2.2",
25 | "karma-ng-scenario": "0.1.0",
26 | "karma-ng-html2js-preprocessor": "0.1.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ch03_04_navigation/test/e2e/templates/book_details.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book details view", function() {
4 |
5 | beforeEach(function() {
6 | browser().navigateTo('/');
7 | });
8 |
9 | it('should show the correct book details', function() {
10 | browser().navigateTo('#/books/978-3-89864-728-1');
11 |
12 | expect(element('.bm-book-title').html()).toBe(
13 | 'JavaScript für Enterprise-Entwickler'
14 | );
15 | expect(element('.bm-book-subtitle').html()).toBe(
16 | 'Professionell programmieren im Browser und auf dem Server'
17 | );
18 | expect(element('.bm-book-isbn').html()).toBe(
19 | 'ISBN: 978-3-89864-728-1'
20 | );
21 | expect(element('.bm-book-num-pages').html()).toBe(
22 | 'Seiten: 302'
23 | );
24 | expect(element('.bm-book-author').html()).toBe(
25 | 'Autor: Oliver Ochs'
26 | );
27 | expect(element('.bm-book-publisher-name').html()).toBe(
28 | 'dpunkt.verlag'
29 | );
30 | expect(element('.bm-book-publisher-name').attr('href')).toBe(
31 | 'http://dpunkt.de/'
32 | );
33 | expect(element('.bm-book-abstract').html()).toBe(
34 | 'JavaScript ist längst nicht mehr nur für' +
35 | ' klassische Webprogrammierer interessant.'
36 | );
37 | });
38 |
39 | it('should appropriately navigate to list view', function() {
40 | browser().navigateTo('#/books/978-3-89864-728-1');
41 |
42 | element('.bm-list-view-btn').click();
43 |
44 | expect(
45 | browser().location().path()
46 | ).toEqual('/books');
47 | });
48 |
49 | });
--------------------------------------------------------------------------------
/ch03_04_navigation/test/e2e/templates/book_list.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book list view", function () {
4 |
5 | // Define the array of books in the expected order.
6 | // Sorted by title.
7 | var expectedBooks = [
8 | {
9 | title: 'CoffeeScript',
10 | isbn: '978-3-86490-050-1',
11 | author: 'Andreas Schubert'
12 | },
13 | {
14 | title: 'JavaScript für Enterprise-Entwickler',
15 | isbn: '978-3-89864-728-1',
16 | author: 'Oliver Ochs'
17 | },
18 | {
19 | title: 'Node.js & Co.',
20 | isbn: '978-3-89864-829-5',
21 | author: 'Golo Roden'
22 | }
23 | ];
24 |
25 | // Derive an array that only contains titles
26 | // for easier expectation checks.
27 | var orderedTitles = expectedBooks.map(function(book) {
28 | return book.title;
29 | });
30 |
31 | beforeEach(function () {
32 | browser().navigateTo('#/books');
33 | browser().reload();
34 | });
35 |
36 | var selector = 'table.bm-book-list tr';
37 |
38 | it('should show the correct number of books', function () {
39 | expect(repeater(selector).count()).toEqual(expectedBooks.length);
40 | });
41 |
42 | it('should show the books in the proper order', function() {
43 | // Are they in the expected order (ascending sorted by title)?
44 | expect(repeater(selector).column('book.title')).toEqual(orderedTitles);
45 | });
46 |
47 | it('should show the correct book information', function() {
48 | // Do the other book details (isbn, author) match?
49 | for (var i = 0, n = expectedBooks.length; i < n; i++) {
50 | expect(repeater(selector).row(i))
51 | .toEqual(
52 | [
53 | expectedBooks[i].title,
54 | expectedBooks[i].author,
55 | expectedBooks[i].isbn
56 | ]
57 | );
58 | }
59 | });
60 |
61 | it('should allow filtering by book title', function() {
62 | // Coffee
63 | var searchText = orderedTitles[0].substr(0, 6);
64 | input('searchText').enter(searchText);
65 | expect(
66 | repeater(selector).column('book.title')
67 | ).toEqual([orderedTitles[0]]);
68 | });
69 |
70 | it('should allow filtering by author', function() {
71 | // Andreas
72 | var searchText = expectedBooks[0].author.substr(0, 7);
73 | input('searchText').enter(searchText);
74 | expect(
75 | repeater(selector).column('book.title')
76 | ).toEqual([orderedTitles[0]]);
77 | });
78 |
79 | it('should allow filtering by isbn', function() {
80 | // 050-1
81 | var searchText = expectedBooks[0].isbn.substr(-5, 5);
82 | input('searchText').enter(searchText);
83 | expect(
84 | repeater(selector).column('book.title')
85 | ).toEqual([orderedTitles[0]]);
86 | });
87 |
88 | it('should appropriately navigate to details view', function() {
89 | var i = 0,
90 | detailsLink = selector + ':nth-child('+ (i+1) +') a';
91 | element(detailsLink).click();
92 |
93 | expect(
94 | browser().location().path()
95 | ).toEqual('/books/' + expectedBooks[i].isbn);
96 | });
97 |
98 | });
--------------------------------------------------------------------------------
/ch03_05_first_service/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BookMonkey
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/ch03_05_first_service/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var bmApp = angular.module('bmApp', ['ngRoute']);
4 |
5 | bmApp.config(function ($routeProvider) {
6 | $routeProvider.when('/books/:isbn', {
7 | //templateUrl: 'templates/book_details_with_expressions.html',
8 | templateUrl: 'templates/book_details.html',
9 | controller: 'BookDetailsCtrl'
10 | })
11 | .when('/books', {
12 | templateUrl: 'templates/book_list.html',
13 | controller: 'BookListCtrl'
14 | })
15 | .otherwise({
16 | redirectTo: '/books'
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/ch03_05_first_service/app/scripts/controllers/book_details.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('BookDetailsCtrl',
4 | function ($scope, $location, $routeParams, BookDataService) {
5 | var isbn = $routeParams.isbn;
6 | $scope.book = BookDataService.getBookByIsbn(isbn);
7 |
8 | $scope.goToListView = function() {
9 | $location.path('/books');
10 | };
11 | });
12 |
--------------------------------------------------------------------------------
/ch03_05_first_service/app/scripts/controllers/book_list.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('BookListCtrl', function ($scope, BookDataService) {
4 | $scope.getBookOrder = function(book) {
5 | return book.title;
6 | };
7 |
8 | $scope.books = BookDataService.getBooks();
9 | });
10 |
--------------------------------------------------------------------------------
/ch03_05_first_service/app/scripts/services/book_data.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.factory('BookDataService', function() {
4 | var srv = {};
5 |
6 | srv._books = [
7 | {
8 | title : 'JavaScript für Enterprise-Entwickler',
9 | subtitle : 'Professionell programmieren im Browser und auf dem Server',
10 | isbn : '978-3-89864-728-1',
11 | abstract : 'JavaScript ist längst nicht mehr nur für klassische Webprogrammierer interessant.',
12 | numPages : 302,
13 | author : 'Oliver Ochs',
14 | publisher : {
15 | name: 'dpunkt.verlag',
16 | url : 'http://dpunkt.de/'
17 | }
18 | },
19 | {
20 | title : 'Node.js & Co.',
21 | subtitle : 'Skalierbare, hochperformante und echtzeitfähige Webanwendungen professionell in JavaScript entwickeln',
22 | isbn : '978-3-89864-829-5',
23 | abstract : 'Nach dem Webbrowser erobert JavaScript nun auch den Webserver.',
24 | numPages : 334,
25 | author : 'Golo Roden',
26 | publisher : {
27 | name: 'dpunkt.verlag',
28 | url : 'http://dpunkt.de/'
29 | }
30 | },
31 | {
32 | title : 'CoffeeScript',
33 | subtitle : 'Einfach JavaScript',
34 | isbn : '978-3-86490-050-1',
35 | abstract : 'CoffeeScript ist eine junge, kleine Programmiersprache, die nach JavaScript übersetzt wird.',
36 | numPages : 200,
37 | author : 'Andreas Schubert',
38 | publisher : {
39 | name: 'dpunkt.verlag',
40 | url : 'http://dpunkt.de/'
41 | }
42 | }
43 | ];
44 |
45 | // Service implementation
46 | srv.getBookByIsbn = function(isbn) {
47 | for (var i = 0, n = srv._books.length; i < n; i++) {
48 | if (isbn === srv._books[i].isbn) {
49 | return angular.copy(srv._books[i]);
50 | }
51 | }
52 |
53 | return null;
54 | };
55 |
56 | srv.getBooks = function() {
57 | // Copy the array in order not to expose internal data structures
58 | return angular.copy(srv._books);
59 | };
60 |
61 | // Public API
62 | return {
63 | getBookByIsbn: function(isbn) {
64 | return srv.getBookByIsbn(isbn);
65 | },
66 | getBooks: function() {
67 | return srv.getBooks();
68 | }
69 | };
70 | });
71 |
--------------------------------------------------------------------------------
/ch03_05_first_service/app/templates/book_details.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
9 |
11 | -
12 | Verlag:
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Zurück
25 |
--------------------------------------------------------------------------------
/ch03_05_first_service/app/templates/book_details_with_expressions.html:
--------------------------------------------------------------------------------
1 | {{ book.title }}
2 | {{ book.subtitle }}
3 |
4 |
5 | - ISBN: {{ book.isbn }}
6 | - Seiten: {{ book.numPages }}
7 | - Autor: {{ book.author }}
8 | -
9 | Verlag:
10 | {{ book.publisher.name }}
13 |
14 |
15 |
16 |
17 | {{ book.abstract }}
--------------------------------------------------------------------------------
/ch03_05_first_service/app/templates/book_list.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 | |
7 | |
8 | |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ch03_05_first_service/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for e2e tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | basePath: '',
7 |
8 | files: [
9 | 'test/e2e/**/*.js'
10 | ],
11 |
12 | logLevel: config.LOG_DEBUG,
13 |
14 | autoWatch: false,
15 |
16 | browsers: ['Chrome'],
17 |
18 | frameworks: ['ng-scenario'],
19 |
20 | singleRun: true,
21 |
22 | urlRoot: '/_karma_/',
23 |
24 | proxies: {
25 | '/': 'http://localhost:8080/'
26 | }
27 |
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/ch03_05_first_service/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for unit tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 |
10 | // frameworks to use
11 | frameworks: ['jasmine'],
12 |
13 |
14 | // list of files / patterns to load in the browser
15 | files: [
16 | 'app/lib/angular/angular.js',
17 | 'app/lib/angular-route/angular-route.js',
18 | 'app/lib/angular-mocks/angular-mocks.js',
19 | 'app/scripts/app.js',
20 | 'app/scripts/**/*.js',
21 | 'test/unit/**/*.js'
22 | ],
23 |
24 |
25 | // list of files to exclude
26 | exclude: [
27 |
28 | ],
29 |
30 |
31 | // test results reporter to use
32 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
33 | reporters: ['progress'],
34 |
35 |
36 | // web server port
37 | port: 9876,
38 |
39 |
40 | // enable / disable colors in the output (reporters and logs)
41 | colors: true,
42 |
43 |
44 | // level of logging
45 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
46 | logLevel: config.LOG_DEBUG,
47 |
48 |
49 | // enable / disable watching file and executing tests whenever any file changes
50 | autoWatch: false,
51 |
52 |
53 | // Start these browsers, currently available:
54 | // - Chrome
55 | // - ChromeCanary
56 | // - Firefox
57 | // - Opera
58 | // - Safari (only Mac)
59 | // - PhantomJS
60 | // - IE (only Windows)
61 | browsers: ['Chrome'],
62 |
63 |
64 | // If browser does not capture in given timeout [ms], kill it
65 | captureTimeout: 60000,
66 |
67 |
68 | // Continuous Integration mode
69 | // if true, it capture browsers, run tests and exit
70 | singleRun: true
71 | });
72 | };
73 |
--------------------------------------------------------------------------------
/ch03_05_first_service/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bookmonkey",
3 | "version": "0.0.1",
4 | "description": "A simple AngularJS app serving as an example for the German book 'AngularJS: Eine praktische Einführung in das JavaScript-Framework'.",
5 | "main": "app/index.html",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "test": "karma start",
11 | "posttest": "karma start karma-e2e.conf.js"
12 | },
13 | "repository": "",
14 | "keywords": [
15 | "angularjs",
16 | "angularjs-de"
17 | ],
18 | "author": "Philipp Tarasiewicz , Robin Böhm ",
19 | "homepage": "http://angularjs.de/",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "karma": "0.12.0",
23 | "karma-chrome-launcher": "0.1.2",
24 | "karma-jasmine": "0.2.2",
25 | "karma-ng-scenario": "0.1.0",
26 | "karma-ng-html2js-preprocessor": "0.1.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ch03_05_first_service/test/e2e/templates/book_details.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book details view", function() {
4 |
5 | beforeEach(function() {
6 | browser().navigateTo('/');
7 | });
8 |
9 | it('should show the correct book details', function() {
10 | browser().navigateTo('#/books/978-3-89864-728-1');
11 |
12 | expect(element('.bm-book-title').html()).toBe(
13 | 'JavaScript für Enterprise-Entwickler'
14 | );
15 | expect(element('.bm-book-subtitle').html()).toBe(
16 | 'Professionell programmieren im Browser und auf dem Server'
17 | );
18 | expect(element('.bm-book-isbn').html()).toBe(
19 | 'ISBN: 978-3-89864-728-1'
20 | );
21 | expect(element('.bm-book-num-pages').html()).toBe(
22 | 'Seiten: 302'
23 | );
24 | expect(element('.bm-book-author').html()).toBe(
25 | 'Autor: Oliver Ochs'
26 | );
27 | expect(element('.bm-book-publisher-name').html()).toBe(
28 | 'dpunkt.verlag'
29 | );
30 | expect(element('.bm-book-publisher-name').attr('href')).toBe(
31 | 'http://dpunkt.de/'
32 | );
33 | expect(element('.bm-book-abstract').html()).toBe(
34 | 'JavaScript ist längst nicht mehr nur für' +
35 | ' klassische Webprogrammierer interessant.'
36 | );
37 | });
38 |
39 | it('should appropriately navigate to list view', function() {
40 | browser().navigateTo('#/books/978-3-89864-728-1');
41 |
42 | element('.bm-list-view-btn').click();
43 |
44 | expect(
45 | browser().location().path()
46 | ).toEqual('/books');
47 | });
48 |
49 | });
--------------------------------------------------------------------------------
/ch03_05_first_service/test/e2e/templates/book_list.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book list view", function () {
4 |
5 | // Define the array of books in the expected order.
6 | // Sorted by title.
7 | var expectedBooks = [
8 | {
9 | title: 'CoffeeScript',
10 | isbn: '978-3-86490-050-1',
11 | author: 'Andreas Schubert'
12 | },
13 | {
14 | title: 'JavaScript für Enterprise-Entwickler',
15 | isbn: '978-3-89864-728-1',
16 | author: 'Oliver Ochs'
17 | },
18 | {
19 | title: 'Node.js & Co.',
20 | isbn: '978-3-89864-829-5',
21 | author: 'Golo Roden'
22 | }
23 | ];
24 |
25 | // Derive an array that only contains titles
26 | // for easier expectation checks.
27 | var orderedTitles = expectedBooks.map(function(book) {
28 | return book.title;
29 | });
30 |
31 | beforeEach(function () {
32 | browser().navigateTo('#/books');
33 | browser().reload();
34 | });
35 |
36 | var selector = 'table.bm-book-list tr';
37 |
38 | it('should show the correct number of books', function () {
39 | expect(repeater(selector).count()).toEqual(expectedBooks.length);
40 | });
41 |
42 | it('should show the books in the proper order', function() {
43 | // Are they in the expected order (ascending sorted by title)?
44 | expect(repeater(selector).column('book.title')).toEqual(orderedTitles);
45 | });
46 |
47 | it('should show the correct book information', function() {
48 | // Do the other book details (isbn, author) match?
49 | for (var i = 0, n = expectedBooks.length; i < n; i++) {
50 | expect(repeater(selector).row(i))
51 | .toEqual(
52 | [
53 | expectedBooks[i].title,
54 | expectedBooks[i].author,
55 | expectedBooks[i].isbn
56 | ]
57 | );
58 | }
59 | });
60 |
61 | it('should allow filtering by book title', function() {
62 | // Coffee
63 | var searchText = orderedTitles[0].substr(0, 6);
64 | input('searchText').enter(searchText);
65 | expect(
66 | repeater(selector).column('book.title')
67 | ).toEqual([orderedTitles[0]]);
68 | });
69 |
70 | it('should allow filtering by author', function() {
71 | // Andreas
72 | var searchText = expectedBooks[0].author.substr(0, 7);
73 | input('searchText').enter(searchText);
74 | expect(
75 | repeater(selector).column('book.title')
76 | ).toEqual([orderedTitles[0]]);
77 | });
78 |
79 | it('should allow filtering by isbn', function() {
80 | // 050-1
81 | var searchText = expectedBooks[0].isbn.substr(-5, 5);
82 | input('searchText').enter(searchText);
83 | expect(
84 | repeater(selector).column('book.title')
85 | ).toEqual([orderedTitles[0]]);
86 | });
87 |
88 | it('should appropriately navigate to details view', function() {
89 | var i = 0,
90 | detailsLink = selector + ':nth-child('+ (i+1) +') a';
91 | element(detailsLink).click();
92 |
93 | expect(
94 | browser().location().path()
95 | ).toEqual('/books/' + expectedBooks[i].isbn);
96 | });
97 |
98 | });
--------------------------------------------------------------------------------
/ch03_05_first_service/test/unit/services/book_data.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Service: BookDataService', function () {
4 |
5 | var BookDataService;
6 |
7 | // load the application module
8 | beforeEach(module('bmApp'));
9 |
10 | // get a reference to the service
11 | beforeEach(inject(function (_BookDataService_) {
12 | BookDataService = _BookDataService_;
13 | }));
14 |
15 | describe('Public API', function() {
16 | it('should include a getBookByIsbn() function', function () {
17 | expect(BookDataService.getBookByIsbn).toBeDefined();
18 | });
19 |
20 | it('should include a getBooks() function', function () {
21 | expect(BookDataService.getBooks).toBeDefined();
22 | });
23 | });
24 |
25 | describe('Public API usage', function() {
26 | describe('getBookByIsbn()', function() {
27 | it('should return the proper book object (valid isbn)', function() {
28 | var isbn = '978-3-86490-050-1',
29 | book = BookDataService.getBookByIsbn(isbn);
30 | expect(book.title).toBe('CoffeeScript');
31 | });
32 |
33 | it('should return null (invalid isbn)', function() {
34 | var isbn = 'test',
35 | book = BookDataService.getBookByIsbn(isbn);
36 | expect(book).toBeNull();
37 | });
38 | });
39 |
40 | describe('getBooks()', function() {
41 | it('should return a proper array of book objects', function() {
42 | var books = BookDataService.getBooks();
43 | expect(books.length).toBe(3);
44 | });
45 | });
46 | });
47 |
48 | });
49 |
--------------------------------------------------------------------------------
/ch04_00_backend/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/ch04_00_backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bookmonkey-simple-backend",
3 | "description": "A simple backend for the BookMonkey example application.",
4 | "private": true,
5 | "version": "0.0.1",
6 | "dependencies": {
7 | "express": "3.x"
8 | },
9 | "main": "server"
10 | }
--------------------------------------------------------------------------------
/ch04_01_admin/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/ch04_01_admin/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BookMonkey
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/ch04_01_admin/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var bmApp = angular.module('bmApp', ['ngRoute']);
4 |
5 | bmApp.config(function ($routeProvider) {
6 | $routeProvider.when('/books/:isbn', {
7 | //templateUrl: 'templates/book_details_with_expressions.html',
8 | templateUrl: 'templates/book_details.html',
9 | controller: 'BookDetailsCtrl'
10 | })
11 | .when('/books', {
12 | templateUrl: 'templates/book_list.html',
13 | controller: 'BookListCtrl'
14 | })
15 |
16 | /* Admin routes */
17 | .when('/admin/books', {
18 | // we reuse the template from the list view
19 | templateUrl: 'templates/book_list.html',
20 | controller: 'AdminBookListCtrl'
21 | })
22 | .when('/admin/books/new', {
23 | templateUrl: 'templates/admin/book_form.html',
24 | controller: 'AdminNewBookCtrl'
25 | })
26 | .when('/admin/books/:isbn/edit', {
27 | templateUrl: 'templates/admin/book_form.html',
28 | controller: 'AdminEditBookCtrl'
29 | })
30 | .when('/admin/books/:isbn/delete', {
31 | templateUrl: 'templates/admin/book_delete.html',
32 | controller: 'AdminDeleteBookCtrl'
33 | })
34 |
35 | /* Default route */
36 | .otherwise({
37 | redirectTo: '/books'
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/ch04_01_admin/app/scripts/controllers/admin_book_list.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('AdminBookListCtrl', function ($scope, BookDataService) {
4 | $scope.isAdmin = true;
5 |
6 | $scope.getBookOrder = function(book) {
7 | return book.title;
8 | };
9 |
10 | $scope.books = BookDataService.getBooks();
11 | });
12 |
--------------------------------------------------------------------------------
/ch04_01_admin/app/scripts/controllers/admin_delete_book.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('AdminDeleteBookCtrl', function ($scope, $routeParams, $location, BookDataService) {
4 | var isbn = $routeParams.isbn;
5 | $scope.book = BookDataService.getBookByIsbn(isbn);
6 |
7 | $scope.deleteBook = function(isbn) {
8 | BookDataService.deleteBookByIsbn(isbn);
9 | goToAdminListView();
10 | };
11 |
12 | $scope.cancel = function() {
13 | goToAdminListView();
14 | };
15 |
16 | var goToAdminListView = function() {
17 | $location.path('/admin/books');
18 | };
19 | });
20 |
--------------------------------------------------------------------------------
/ch04_01_admin/app/scripts/controllers/admin_edit_book.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('AdminEditBookCtrl', function ($scope, $routeParams, $location, BookDataService) {
4 | $scope.isEditMode = true;
5 | $scope.submitBtnLabel = 'Buch editieren';
6 |
7 | var isbn = $routeParams.isbn;
8 | $scope.book = BookDataService.getBookByIsbn(isbn);
9 |
10 | $scope.submitAction = function() {
11 | BookDataService.updateBook($scope.book);
12 | goToAdminListView();
13 | };
14 |
15 | $scope.cancelAction = function() {
16 | goToAdminListView();
17 | };
18 |
19 | var goToAdminListView = function() {
20 | $location.path('/admin/books');
21 | };
22 | });
23 |
--------------------------------------------------------------------------------
/ch04_01_admin/app/scripts/controllers/admin_new_book.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('AdminNewBookCtrl', function ($scope, $location, BookDataService) {
4 | $scope.book = {};
5 | $scope.submitBtnLabel = 'Buch anlegen';
6 |
7 | $scope.submitAction = function() {
8 | BookDataService.storeBook($scope.book);
9 | goToAdminListView();
10 | };
11 |
12 | $scope.cancelAction = function() {
13 | goToAdminListView();
14 | };
15 |
16 | var goToAdminListView = function() {
17 | $location.path('/admin/books');
18 | };
19 | });
20 |
--------------------------------------------------------------------------------
/ch04_01_admin/app/scripts/controllers/book_details.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('BookDetailsCtrl',
4 | function ($scope, $location, $routeParams, BookDataService) {
5 | var isbn = $routeParams.isbn;
6 | $scope.book = BookDataService.getBookByIsbn(isbn);
7 |
8 | $scope.goToListView = function() {
9 | if ($location.path().indexOf('/admin') === 0) {
10 | $location.path('/admin/books');
11 | }
12 | else {
13 | $location.path('/books');
14 | }
15 | };
16 | });
17 |
--------------------------------------------------------------------------------
/ch04_01_admin/app/scripts/controllers/book_list.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('BookListCtrl', function ($scope, BookDataService) {
4 | $scope.getBookOrder = function(book) {
5 | return book.title;
6 | };
7 |
8 | $scope.books = BookDataService.getBooks();
9 | });
10 |
--------------------------------------------------------------------------------
/ch04_01_admin/app/styles/main.css:
--------------------------------------------------------------------------------
1 | .split-screen {
2 | display: inline-block;
3 | box-sizing: border-box;
4 | vertical-align: top;
5 | width: 48%;
6 | }
7 |
8 | .simple-border {
9 | border: black dashed 1px;
10 | }
11 |
12 | .padded {
13 | padding: 10px;
14 | }
15 |
16 | form input.ng-invalid.ng-dirty {
17 | background-color: #ffcbc1;
18 | }
19 |
20 | form input.ng-invalid:focus {
21 | outline-color: red;
22 | }
23 |
24 | form input.ng-valid.ng-dirty {
25 | background-color: #1dca07;
26 | }
--------------------------------------------------------------------------------
/ch04_01_admin/app/templates/admin/book_delete.html:
--------------------------------------------------------------------------------
1 | Administrationsbereich
2 |
3 | Soll das Buch
4 | "{{ book.title }}"
5 | wirklich gelöscht werden?
6 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/ch04_01_admin/app/templates/admin/book_form.html:
--------------------------------------------------------------------------------
1 | Administrationsbereich
2 |
3 |
Neues Buch anlegen
4 | Buch editieren
5 |
6 |
72 |
73 |
74 |
Vorschau
75 |
77 |
78 |
--------------------------------------------------------------------------------
/ch04_01_admin/app/templates/book_details.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
9 |
11 | -
12 | Verlag:
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Zurück
25 |
--------------------------------------------------------------------------------
/ch04_01_admin/app/templates/book_details_with_expressions.html:
--------------------------------------------------------------------------------
1 | {{ book.title }}
2 | {{ book.subtitle }}
3 |
4 |
5 | - ISBN: {{ book.isbn }}
6 | - Seiten: {{ book.numPages }}
7 | - Autor: {{ book.author }}
8 | -
9 | Verlag:
10 | {{ book.publisher.name }}
13 |
14 |
15 |
16 |
17 | {{ book.abstract }}
--------------------------------------------------------------------------------
/ch04_01_admin/app/templates/book_list.html:
--------------------------------------------------------------------------------
1 | Administrationsbereich
2 |
4 |
33 |
34 |
35 | Neues Buch anlegen
36 |
37 |
--------------------------------------------------------------------------------
/ch04_01_admin/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for e2e tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | basePath: '',
7 |
8 | files: [
9 | 'test/e2e/**/*.js'
10 | ],
11 |
12 | logLevel: config.LOG_DEBUG,
13 |
14 | autoWatch: false,
15 |
16 | browsers: ['Chrome'],
17 |
18 | frameworks: ['ng-scenario'],
19 |
20 | singleRun: true,
21 |
22 | urlRoot: '/_karma_/',
23 |
24 | proxies: {
25 | '/': 'http://localhost:8080/'
26 | }
27 |
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/ch04_01_admin/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for unit tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 |
10 | // frameworks to use
11 | frameworks: ['jasmine'],
12 |
13 |
14 | // list of files / patterns to load in the browser
15 | files: [
16 | 'app/lib/angular/angular.js',
17 | 'app/lib/angular-route/angular-route.js',
18 | 'app/lib/angular-mocks/angular-mocks.js',
19 | 'app/scripts/app.js',
20 | 'app/scripts/**/*.js',
21 | 'test/unit/**/*.js'
22 | ],
23 |
24 |
25 | // list of files to exclude
26 | exclude: [
27 |
28 | ],
29 |
30 |
31 | // test results reporter to use
32 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
33 | reporters: ['progress'],
34 |
35 |
36 | // web server port
37 | port: 9876,
38 |
39 |
40 | // enable / disable colors in the output (reporters and logs)
41 | colors: true,
42 |
43 |
44 | // level of logging
45 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
46 | logLevel: config.LOG_DEBUG,
47 |
48 |
49 | // enable / disable watching file and executing tests whenever any file changes
50 | autoWatch: false,
51 |
52 |
53 | // Start these browsers, currently available:
54 | // - Chrome
55 | // - ChromeCanary
56 | // - Firefox
57 | // - Opera
58 | // - Safari (only Mac)
59 | // - PhantomJS
60 | // - IE (only Windows)
61 | browsers: ['Chrome'],
62 |
63 |
64 | // If browser does not capture in given timeout [ms], kill it
65 | captureTimeout: 60000,
66 |
67 |
68 | // Continuous Integration mode
69 | // if true, it capture browsers, run tests and exit
70 | singleRun: true
71 | });
72 | };
73 |
--------------------------------------------------------------------------------
/ch04_01_admin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bookmonkey",
3 | "version": "0.0.1",
4 | "description": "A simple AngularJS app serving as an example for the German book 'AngularJS: Eine praktische Einführung in das JavaScript-Framework'.",
5 | "main": "app/index.html",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "test": "karma start",
11 | "posttest": "karma start karma-e2e.conf.js"
12 | },
13 | "repository": "",
14 | "keywords": [
15 | "angularjs",
16 | "angularjs-de"
17 | ],
18 | "author": "Philipp Tarasiewicz , Robin Böhm ",
19 | "homepage": "http://angularjs.de/",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "karma": "0.12.0",
23 | "karma-chrome-launcher": "0.1.2",
24 | "karma-jasmine": "0.2.2",
25 | "karma-ng-scenario": "0.1.0",
26 | "karma-ng-html2js-preprocessor": "0.1.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ch04_01_admin/test/e2e/templates/admin_delete_book.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book creation view", function () {
4 | var exampleBook = {
5 | title : 'JavaScript effektiv',
6 | subtitle : '68 Dinge, die ein guter JavaScript-Entwickler wissen sollte',
7 | isbn : '978-3-86490-127-0',
8 | abstract : 'Wollen Sie JavaScript wirklich beherrschen?',
9 | numPages : 240,
10 | author : 'David Herman',
11 | publisher : {
12 | name: 'dpunkt.verlag',
13 | url : 'http://dpunkt.de/'
14 | }
15 | };
16 |
17 | beforeEach(function () {
18 | browser().navigateTo('/#/admin/books/new');
19 | browser().reload();
20 | enterExampleBook('button.bm-submit-btn');
21 | });
22 |
23 | var selector = 'table.bm-book-list tr';
24 |
25 | it('should navigate to admin list view containing the edited book entry', function () {
26 | element(selector + ' a.bm-edit-link').query(function (editLinks, done) {
27 | for (var i = 0, n = editLinks.length; i < n; i++) {
28 | if (angular.element(editLinks[i]).text() === exampleBook.title) {
29 | executeAndCheck(i);
30 | break;
31 | }
32 | }
33 |
34 | done();
35 | });
36 | });
37 |
38 | var executeAndCheck = function(editLinkIndex) {
39 | var entryCount = 4;
40 |
41 | expect(
42 | repeater(selector).count()
43 | ).toBe(entryCount);
44 |
45 | // specify selector for the delete link
46 | var deleteLink = selector + ':nth-child('+ (editLinkIndex+1) +') a.bm-delete-link';
47 |
48 | // click on delete link
49 | element(deleteLink).click();
50 |
51 | expect(
52 | browser().location().path()
53 | ).toEqual('/admin/books/' + exampleBook.isbn + '/delete');
54 |
55 | // in deletion dialog click on delete button
56 | element('button.bm-delete-btn').click();
57 |
58 | // admin list view should show up
59 | expect(
60 | browser().location().path()
61 | ).toEqual('/admin/books');
62 |
63 | // list shouldn't contain the deleted entry anymore
64 | expect(
65 | repeater(selector).column('book.title')
66 | ).not().toContain(exampleBook.title);
67 |
68 | expect(
69 | repeater(selector).count()
70 | ).toBe(entryCount - 1);
71 | };
72 |
73 | var enterExampleBook = function(clickSelector) {
74 | input('book.title').enter(exampleBook.title);
75 | input('book.subtitle').enter(exampleBook.subtitle);
76 | input('book.isbn').enter(exampleBook.isbn);
77 | input('book.abstract').enter(exampleBook.abstract);
78 | input('book.numPages').enter(exampleBook.numPages);
79 | input('book.author').enter(exampleBook.author);
80 | input('book.publisher.name').enter(exampleBook.publisher.name);
81 | input('book.publisher.url').enter(exampleBook.publisher.url);
82 |
83 | element(clickSelector).click();
84 | };
85 | });
86 |
--------------------------------------------------------------------------------
/ch04_01_admin/test/e2e/templates/admin_edit_book.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book creation view", function () {
4 | var exampleBook = {
5 | title : 'JavaScript effektiv',
6 | subtitle : '68 Dinge, die ein guter JavaScript-Entwickler wissen sollte',
7 | isbn : '978-3-86490-127-0',
8 | abstract : 'Wollen Sie JavaScript wirklich beherrschen?',
9 | numPages : 240,
10 | author : 'David Herman',
11 | publisher : {
12 | name: 'dpunkt.verlag',
13 | url : 'http://dpunkt.de/'
14 | }
15 | };
16 |
17 | beforeEach(function () {
18 | browser().navigateTo('/#/admin/books/new');
19 | browser().reload();
20 | enterExampleBook('button.bm-submit-btn');
21 | });
22 |
23 | var selector = 'table.bm-book-list tr';
24 |
25 | it('should navigate to admin list view containing the edited book entry', function () {
26 | element(selector + ' a.bm-edit-link').query(function (editLinks, done) {
27 | for (var i = 0, n = editLinks.length; i < n; i++) {
28 | if (angular.element(editLinks[i]).text() === exampleBook.title) {
29 | executeAndCheck(i);
30 | break;
31 | }
32 | }
33 |
34 | done();
35 | });
36 | });
37 |
38 | var executeAndCheck = function(editLinkIndex) {
39 | // specify selector for the edit link
40 | var editLink = selector + ':nth-child('+ (editLinkIndex+1) +') a.bm-edit-link';
41 |
42 | // click on edit link
43 | element(editLink).click();
44 |
45 | // in edit form enter 'TEST' into book title field
46 | input('book.title').enter('TEST');
47 |
48 | // click on submit button
49 | element('button.bm-submit-btn').click();
50 |
51 | // admin list view should show up
52 | expect(
53 | browser().location().path()
54 | ).toEqual('/admin/books');
55 |
56 | // there should be a 'TEST' entry
57 | expect(
58 | repeater(selector).column('book.title')
59 | ).toContain('TEST');
60 |
61 | // list shouldn't contain entry with old title
62 | expect(
63 | repeater(selector).column('book.title')
64 | ).not().toContain(exampleBook.title);
65 | };
66 |
67 | var enterExampleBook = function(clickSelector) {
68 | input('book.title').enter(exampleBook.title);
69 | input('book.subtitle').enter(exampleBook.subtitle);
70 | input('book.isbn').enter(exampleBook.isbn);
71 | input('book.abstract').enter(exampleBook.abstract);
72 | input('book.numPages').enter(exampleBook.numPages);
73 | input('book.author').enter(exampleBook.author);
74 | input('book.publisher.name').enter(exampleBook.publisher.name);
75 | input('book.publisher.url').enter(exampleBook.publisher.url);
76 |
77 | element(clickSelector).click();
78 | };
79 | });
80 |
--------------------------------------------------------------------------------
/ch04_01_admin/test/e2e/templates/admin_new_book.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book creation view", function () {
4 | var exampleBook = {
5 | title : 'JavaScript effektiv',
6 | subtitle : '68 Dinge, die ein guter JavaScript-Entwickler wissen sollte',
7 | isbn : '978-3-86490-127-0',
8 | abstract : 'Wollen Sie JavaScript wirklich beherrschen?',
9 | numPages : 240,
10 | author : 'David Herman',
11 | publisher : {
12 | name: 'dpunkt.verlag',
13 | url : 'http://dpunkt.de/'
14 | }
15 | };
16 |
17 | beforeEach(function () {
18 | browser().navigateTo('/#/admin/books/new');
19 | browser().reload();
20 | });
21 |
22 | var selector = 'table.bm-book-list tr';
23 |
24 | it('should navigate to admin list view having one more book entry', function () {
25 | enterExampleBook('button.bm-submit-btn');
26 |
27 | expect(
28 | browser().location().path()
29 | ).toEqual('/admin/books');
30 |
31 | expect(
32 | repeater(selector).column('book.title')
33 | ).toContain(exampleBook.title);
34 | });
35 |
36 | it('should navigate to admin list view without having one more book entry', function () {
37 | enterExampleBook('button.bm-cancel-btn');
38 |
39 | expect(
40 | browser().location().path()
41 | ).toEqual('/admin/books');
42 |
43 | expect(
44 | repeater(selector).column('book.title')
45 | ).not().toContain(exampleBook.title);
46 | });
47 |
48 | var enterExampleBook = function(clickSelector) {
49 | input('book.title').enter(exampleBook.title);
50 | input('book.subtitle').enter(exampleBook.subtitle);
51 | input('book.isbn').enter(exampleBook.isbn);
52 | input('book.abstract').enter(exampleBook.abstract);
53 | input('book.numPages').enter(exampleBook.numPages);
54 | input('book.author').enter(exampleBook.author);
55 | input('book.publisher.name').enter(exampleBook.publisher.name);
56 | input('book.publisher.url').enter(exampleBook.publisher.url);
57 |
58 | element(clickSelector).click();
59 | };
60 | });
61 |
--------------------------------------------------------------------------------
/ch04_01_admin/test/e2e/templates/book_details.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book details view", function() {
4 |
5 | beforeEach(function() {
6 | browser().navigateTo('/');
7 | });
8 |
9 | it('should show the correct book details', function() {
10 | browser().navigateTo('#/books/978-3-89864-728-1');
11 |
12 | expect(element('.bm-book-title').html()).toBe(
13 | 'JavaScript für Enterprise-Entwickler'
14 | );
15 | expect(element('.bm-book-subtitle').html()).toBe(
16 | 'Professionell programmieren im Browser und auf dem Server'
17 | );
18 | expect(element('.bm-book-isbn').html()).toBe(
19 | 'ISBN: 978-3-89864-728-1'
20 | );
21 | expect(element('.bm-book-num-pages').html()).toBe(
22 | 'Seiten: 302'
23 | );
24 | expect(element('.bm-book-author').html()).toBe(
25 | 'Autor: Oliver Ochs'
26 | );
27 | expect(element('.bm-book-publisher-name').html()).toBe(
28 | 'dpunkt.verlag'
29 | );
30 | expect(element('.bm-book-publisher-name').attr('href')).toBe(
31 | 'http://dpunkt.de/'
32 | );
33 | expect(element('.bm-book-abstract').html()).toBe(
34 | 'JavaScript ist längst nicht mehr nur für' +
35 | ' klassische Webprogrammierer interessant.'
36 | );
37 | });
38 |
39 | it('should appropriately navigate to list view', function() {
40 | browser().navigateTo('#/books/978-3-89864-728-1');
41 |
42 | element('.bm-list-view-btn').click();
43 |
44 | expect(
45 | browser().location().path()
46 | ).toEqual('/books');
47 | });
48 |
49 | });
--------------------------------------------------------------------------------
/ch04_02_tags/app/component_templates/directives/tags.html:
--------------------------------------------------------------------------------
1 |
2 | {{ tag }}
3 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BookMonkey
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/lib/jquery-ui/images/ui-bg_flat_75_ffffff_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angularjs-de/dpunkt-buch-beispiele/b94596cd63468df61064b3cbbcf19d9c9dda2c67/ch04_02_tags/app/lib/jquery-ui/images/ui-bg_flat_75_ffffff_40x100.png
--------------------------------------------------------------------------------
/ch04_02_tags/app/lib/jquery-ui/images/ui-bg_glass_75_dadada_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angularjs-de/dpunkt-buch-beispiele/b94596cd63468df61064b3cbbcf19d9c9dda2c67/ch04_02_tags/app/lib/jquery-ui/images/ui-bg_glass_75_dadada_1x400.png
--------------------------------------------------------------------------------
/ch04_02_tags/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var bmApp = angular.module('bmApp', ['ngRoute']);
4 |
5 | bmApp.config(function ($routeProvider) {
6 | $routeProvider.when('/books/:isbn', {
7 | //templateUrl: 'templates/book_details_with_expressions.html',
8 | templateUrl: 'templates/book_details.html',
9 | controller: 'BookDetailsCtrl'
10 | })
11 | .when('/books', {
12 | templateUrl: 'templates/book_list.html',
13 | controller: 'BookListCtrl'
14 | })
15 |
16 | /* Admin routes */
17 | .when('/admin/books', {
18 | // we reuse the template from the list view
19 | templateUrl: 'templates/book_list.html',
20 | controller: 'AdminBookListCtrl'
21 | })
22 | .when('/admin/books/new', {
23 | templateUrl: 'templates/admin/book_form.html',
24 | controller: 'AdminNewBookCtrl'
25 | })
26 | .when('/admin/books/:isbn/edit', {
27 | templateUrl: 'templates/admin/book_form.html',
28 | controller: 'AdminEditBookCtrl'
29 | })
30 | .when('/admin/books/:isbn/delete', {
31 | templateUrl: 'templates/admin/book_delete.html',
32 | controller: 'AdminDeleteBookCtrl'
33 | })
34 |
35 | /* Default route */
36 | .otherwise({
37 | redirectTo: '/books'
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/scripts/controllers/admin_book_list.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('AdminBookListCtrl', function ($scope, BookDataService) {
4 | $scope.isAdmin = true;
5 |
6 | $scope.getBookOrder = function(book) {
7 | return book.title;
8 | };
9 |
10 | $scope.books = BookDataService.getBooks();
11 | });
12 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/scripts/controllers/admin_delete_book.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('AdminDeleteBookCtrl', function ($scope, $routeParams, $location, BookDataService) {
4 | var isbn = $routeParams.isbn;
5 | $scope.book = BookDataService.getBookByIsbn(isbn);
6 |
7 | $scope.deleteBook = function(isbn) {
8 | BookDataService.deleteBookByIsbn(isbn);
9 | goToAdminListView();
10 | };
11 |
12 | $scope.cancel = function() {
13 | goToAdminListView();
14 | };
15 |
16 | var goToAdminListView = function() {
17 | $location.path('/admin/books');
18 | };
19 | });
20 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/scripts/controllers/admin_edit_book.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('AdminEditBookCtrl', function ($scope, $routeParams, $location, BookDataService) {
4 | $scope.isEditMode = true;
5 | $scope.submitBtnLabel = 'Buch editieren';
6 |
7 | var isbn = $routeParams.isbn;
8 | $scope.book = BookDataService.getBookByIsbn(isbn);
9 |
10 | $scope.submitAction = function() {
11 | BookDataService.updateBook($scope.book);
12 | goToAdminListView();
13 | };
14 |
15 | $scope.cancelAction = function() {
16 | goToAdminListView();
17 | };
18 |
19 | var goToAdminListView = function() {
20 | $location.path('/admin/books');
21 | };
22 | });
23 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/scripts/controllers/admin_new_book.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('AdminNewBookCtrl', function ($scope, $location, BookDataService) {
4 | $scope.book = {};
5 | $scope.submitBtnLabel = 'Buch anlegen';
6 |
7 | $scope.submitAction = function() {
8 | BookDataService.storeBook($scope.book);
9 | goToAdminListView();
10 | };
11 |
12 | $scope.cancelAction = function() {
13 | goToAdminListView();
14 | };
15 |
16 | var goToAdminListView = function() {
17 | $location.path('/admin/books');
18 | };
19 | });
20 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/scripts/controllers/book_details.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('BookDetailsCtrl',
4 | function ($scope, $location, $routeParams, BookDataService) {
5 | var isbn = $routeParams.isbn;
6 | $scope.book = BookDataService.getBookByIsbn(isbn);
7 |
8 | $scope.goToListView = function() {
9 | if ($location.path().indexOf('/admin') === 0) {
10 | $location.path('/admin/books');
11 | }
12 | else {
13 | $location.path('/books');
14 | }
15 | };
16 | });
17 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/scripts/controllers/book_list.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('BookListCtrl', function ($scope, BookDataService) {
4 | $scope.getBookOrder = function(book) {
5 | return book.title;
6 | };
7 |
8 | $scope.books = BookDataService.getBooks();
9 | });
10 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/scripts/directives/tags.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.directive('tags', function() {
4 | return {
5 | restrict: 'E',
6 | scope: {
7 | tagData: '='
8 | },
9 | templateUrl: 'component_templates/directives/tags.html'
10 | }
11 | });
12 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/scripts/directives/tokenfield.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.directive('tokenfield', function(BookDataService) {
4 | return {
5 | restrict: 'A',
6 | scope: {
7 | // tokenfield holds a two-way bounded
8 | // reference to the book object
9 | tokenfield: '='
10 | },
11 | link: function(scope, elem) {
12 | // only call $apply when directive is initialized
13 | // to avoid 'digest already in progress'
14 | var initialized = false;
15 |
16 | elem.tokenfield({
17 | autocomplete: {
18 | source: BookDataService.getTags(),
19 | delay: 100
20 | },
21 | showAutocompleteOnFocus: false,
22 | allowDuplicates: false,
23 | createTokensOnBlur: true
24 | }).on('afterCreateToken', function (e) {
25 | addToken(e.token.value);
26 | }).on('removeToken', function (e) {
27 | removeToken(e.token.value);
28 | });
29 |
30 | function addToken(token) {
31 | if (initialized) {
32 | // $apply() to trigger dirty checking
33 | // because of 3rd-party callback
34 | scope.$apply(function() {
35 | scope.tokenfield.tags.push(token);
36 | });
37 | }
38 | }
39 |
40 | function removeToken(token) {
41 | if (initialized) {
42 | // $apply() to trigger dirty checking
43 | // because of 3rd-party callback
44 | scope.$apply(function() {
45 | var tags = scope.tokenfield.tags,
46 | i = tags.length;
47 | while(i--) {
48 | if (token === tags[i]) {
49 | tags.splice(i, 1);
50 | break;
51 | }
52 | }
53 | });
54 | }
55 | }
56 |
57 | function init() {
58 | if (angular.isDefined(scope.tokenfield.tags)) {
59 | if (scope.tokenfield.tags.length > 0) {
60 | // this call emits an 'afterCreateToken' event
61 | // and this would imply a 'digest already in progress'
62 | // without the initialized flag
63 | elem.tokenfield('setTokens', scope.tokenfield.tags);
64 | }
65 | }
66 | else {
67 | scope.tokenfield.tags = [];
68 | }
69 |
70 | initialized = true;
71 | }
72 |
73 | init();
74 | }
75 | }
76 | });
77 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/styles/main.css:
--------------------------------------------------------------------------------
1 | .bm-content {
2 | padding: 10px;
3 | }
4 |
5 | .split-screen {
6 | display: inline-block;
7 | box-sizing: border-box;
8 | vertical-align: top;
9 | width: 48%;
10 | }
11 |
12 | .simple-border {
13 | border: black dashed 1px;
14 | }
15 |
16 | .padded {
17 | padding: 10px;
18 | }
19 |
20 | form input.ng-invalid.ng-dirty {
21 | background-color: #ffcbc1;
22 | }
23 |
24 | form input.ng-invalid:focus {
25 | outline-color: red;
26 | }
27 |
28 | form input.ng-valid.ng-dirty {
29 | background-color: #1dca07;
30 | }
31 |
32 | .bm-tag {
33 | font-family: 'Consolas', monospace;
34 | font-size: 0.9em;
35 | background-color: #276dff;
36 | color: #ffffff;
37 | margin: 2px;
38 | padding: 2px;
39 | border-radius: 8px;
40 | }
--------------------------------------------------------------------------------
/ch04_02_tags/app/templates/admin/book_delete.html:
--------------------------------------------------------------------------------
1 | Administrationsbereich
2 | Soll das Buch "{{ book.title }}" wirklich gelöscht werden?
3 |
5 |
6 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/templates/admin/book_form.html:
--------------------------------------------------------------------------------
1 | Administrationsbereich
2 |
3 |
Neues Buch anlegen
4 | Buch editieren
5 |
6 |
74 |
75 |
76 |
Vorschau
77 |
79 |
80 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/templates/book_details.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
9 |
11 | -
12 | Verlag:
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Zurück
26 |
--------------------------------------------------------------------------------
/ch04_02_tags/app/templates/book_details_with_expressions.html:
--------------------------------------------------------------------------------
1 | {{ book.title }}
2 | {{ book.subtitle }}
3 |
4 |
5 | - ISBN: {{ book.isbn }}
6 | - Seiten: {{ book.numPages }}
7 | - Autor: {{ book.author }}
8 | -
9 | Verlag:
10 | {{ book.publisher.name }}
13 |
14 |
15 |
16 |
17 | {{ book.abstract }}
--------------------------------------------------------------------------------
/ch04_02_tags/app/templates/book_list.html:
--------------------------------------------------------------------------------
1 | Administrationsbereich
2 |
4 |
33 |
34 |
35 | Neues Buch anlegen
36 |
37 |
--------------------------------------------------------------------------------
/ch04_02_tags/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for e2e tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | basePath: '',
7 |
8 | files: [
9 | 'test/e2e/**/*.js'
10 | ],
11 |
12 | logLevel: config.LOG_DEBUG,
13 |
14 | autoWatch: false,
15 |
16 | browsers: ['Chrome'],
17 |
18 | frameworks: ['ng-scenario'],
19 |
20 | singleRun: true,
21 |
22 | urlRoot: '/_karma_/',
23 |
24 | proxies: {
25 | '/': 'http://localhost:8080/'
26 | }
27 |
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/ch04_02_tags/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for unit tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 |
10 | // frameworks to use
11 | frameworks: ['jasmine'],
12 |
13 |
14 | // list of files / patterns to load in the browser
15 | files: [
16 | 'app/lib/jquery/jquery-1.10.2.min.js',
17 | 'app/lib/jquery-ui/jquery-ui.min.js',
18 | 'app/lib/bootstrap-tokenfield/bootstrap-tokenfield.min.js',
19 | 'app/lib/angular/angular.js',
20 | 'app/lib/angular-route/angular-route.js',
21 | 'app/lib/angular-mocks/angular-mocks.js',
22 | 'app/scripts/app.js',
23 | 'app/scripts/**/*.js',
24 | 'test/unit/**/*.js'
25 | ],
26 |
27 |
28 | // list of files to exclude
29 | exclude: [
30 |
31 | ],
32 |
33 |
34 | // test results reporter to use
35 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
36 | reporters: ['progress'],
37 |
38 |
39 | // web server port
40 | port: 9876,
41 |
42 |
43 | // enable / disable colors in the output (reporters and logs)
44 | colors: true,
45 |
46 |
47 | // level of logging
48 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
49 | logLevel: config.LOG_DEBUG,
50 |
51 |
52 | // enable / disable watching file and executing tests whenever any file changes
53 | autoWatch: false,
54 |
55 |
56 | // Start these browsers, currently available:
57 | // - Chrome
58 | // - ChromeCanary
59 | // - Firefox
60 | // - Opera
61 | // - Safari (only Mac)
62 | // - PhantomJS
63 | // - IE (only Windows)
64 | browsers: ['Chrome'],
65 |
66 |
67 | // If browser does not capture in given timeout [ms], kill it
68 | captureTimeout: 60000,
69 |
70 |
71 | // Continuous Integration mode
72 | // if true, it capture browsers, run tests and exit
73 | singleRun: true
74 | });
75 | };
76 |
--------------------------------------------------------------------------------
/ch04_02_tags/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bookmonkey",
3 | "version": "0.0.1",
4 | "description": "A simple AngularJS app serving as an example for the German book 'AngularJS: Eine praktische Einführung in das JavaScript-Framework'.",
5 | "main": "app/index.html",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "test": "karma start",
11 | "posttest": "karma start karma-e2e.conf.js"
12 | },
13 | "repository": "",
14 | "keywords": [
15 | "angularjs",
16 | "angularjs-de"
17 | ],
18 | "author": "Philipp Tarasiewicz , Robin Böhm ",
19 | "homepage": "http://angularjs.de/",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "karma": "0.12.0",
23 | "karma-chrome-launcher": "0.1.2",
24 | "karma-jasmine": "0.2.2",
25 | "karma-ng-scenario": "0.1.0",
26 | "karma-ng-html2js-preprocessor": "0.1.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ch04_02_tags/test/e2e/templates/admin_delete_book.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book creation view", function () {
4 | var exampleBook = {
5 | title : 'JavaScript effektiv',
6 | subtitle : '68 Dinge, die ein guter JavaScript-Entwickler wissen sollte',
7 | isbn : '978-3-86490-127-0',
8 | abstract : 'Wollen Sie JavaScript wirklich beherrschen?',
9 | numPages : 240,
10 | author : 'David Herman',
11 | publisher : {
12 | name: 'dpunkt.verlag',
13 | url : 'http://dpunkt.de/'
14 | }
15 | };
16 |
17 | beforeEach(function () {
18 | browser().navigateTo('/#/admin/books/new');
19 | browser().reload();
20 | enterExampleBook('button.bm-submit-btn');
21 | });
22 |
23 | var selector = 'table.bm-book-list tr';
24 |
25 | it('should navigate to admin list view containing the edited book entry', function () {
26 | element(selector + ' a.bm-edit-link').query(function (editLinks, done) {
27 | for (var i = 0, n = editLinks.length; i < n; i++) {
28 | if (angular.element(editLinks[i]).text() === exampleBook.title) {
29 | executeAndCheck(i);
30 | break;
31 | }
32 | }
33 |
34 | done();
35 | });
36 | });
37 |
38 | var executeAndCheck = function(editLinkIndex) {
39 | var entryCount = 4;
40 |
41 | expect(
42 | repeater(selector).count()
43 | ).toBe(entryCount);
44 |
45 | // specify selector for the delete link
46 | var deleteLink = selector + ':nth-child('+ (editLinkIndex+1) +') a.bm-delete-link';
47 |
48 | // click on delete link
49 | element(deleteLink).click();
50 |
51 | expect(
52 | browser().location().path()
53 | ).toEqual('/admin/books/' + exampleBook.isbn + '/delete');
54 |
55 | // in deletion dialog click on delete button
56 | element('button.bm-delete-btn').click();
57 |
58 | // admin list view should show up
59 | expect(
60 | browser().location().path()
61 | ).toEqual('/admin/books');
62 |
63 | // list shouldn't contain the deleted entry anymore
64 | expect(
65 | repeater(selector).column('book.title')
66 | ).not().toContain(exampleBook.title);
67 |
68 | expect(
69 | repeater(selector).count()
70 | ).toBe(entryCount - 1);
71 | };
72 |
73 | var enterExampleBook = function(clickSelector) {
74 | input('book.title').enter(exampleBook.title);
75 | input('book.subtitle').enter(exampleBook.subtitle);
76 | input('book.isbn').enter(exampleBook.isbn);
77 | input('book.abstract').enter(exampleBook.abstract);
78 | input('book.numPages').enter(exampleBook.numPages);
79 | input('book.author').enter(exampleBook.author);
80 | input('book.publisher.name').enter(exampleBook.publisher.name);
81 | input('book.publisher.url').enter(exampleBook.publisher.url);
82 |
83 | element(clickSelector).click();
84 | };
85 | });
86 |
--------------------------------------------------------------------------------
/ch04_02_tags/test/e2e/templates/admin_edit_book.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book creation view", function () {
4 | var exampleBook = {
5 | title : 'JavaScript effektiv',
6 | subtitle : '68 Dinge, die ein guter JavaScript-Entwickler wissen sollte',
7 | isbn : '978-3-86490-127-0',
8 | abstract : 'Wollen Sie JavaScript wirklich beherrschen?',
9 | numPages : 240,
10 | author : 'David Herman',
11 | publisher : {
12 | name: 'dpunkt.verlag',
13 | url : 'http://dpunkt.de/'
14 | }
15 | };
16 |
17 | beforeEach(function () {
18 | browser().navigateTo('/#/admin/books/new');
19 | browser().reload();
20 | enterExampleBook('button.bm-submit-btn');
21 | });
22 |
23 | var selector = 'table.bm-book-list tr';
24 |
25 | it('should navigate to admin list view containing the edited book entry', function () {
26 | element(selector + ' a.bm-edit-link').query(function (editLinks, done) {
27 | for (var i = 0, n = editLinks.length; i < n; i++) {
28 | if (angular.element(editLinks[i]).text() === exampleBook.title) {
29 | executeAndCheck(i);
30 | break;
31 | }
32 | }
33 |
34 | done();
35 | });
36 | });
37 |
38 | var executeAndCheck = function(editLinkIndex) {
39 | // specify selector for the edit link
40 | var editLink = selector + ':nth-child('+ (editLinkIndex+1) +') a.bm-edit-link';
41 |
42 | // click on edit link
43 | element(editLink).click();
44 |
45 | // in edit form enter 'TEST' into book title field
46 | input('book.title').enter('TEST');
47 |
48 | // click on submit button
49 | element('button.bm-submit-btn').click();
50 |
51 | // admin list view should show up
52 | expect(
53 | browser().location().path()
54 | ).toEqual('/admin/books');
55 |
56 | // there should be a 'TEST' entry
57 | expect(
58 | repeater(selector).column('book.title')
59 | ).toContain('TEST');
60 |
61 | // list shouldn't contain entry with old title
62 | expect(
63 | repeater(selector).column('book.title')
64 | ).not().toContain(exampleBook.title);
65 | };
66 |
67 | var enterExampleBook = function(clickSelector) {
68 | input('book.title').enter(exampleBook.title);
69 | input('book.subtitle').enter(exampleBook.subtitle);
70 | input('book.isbn').enter(exampleBook.isbn);
71 | input('book.abstract').enter(exampleBook.abstract);
72 | input('book.numPages').enter(exampleBook.numPages);
73 | input('book.author').enter(exampleBook.author);
74 | input('book.publisher.name').enter(exampleBook.publisher.name);
75 | input('book.publisher.url').enter(exampleBook.publisher.url);
76 |
77 | element(clickSelector).click();
78 | };
79 | });
80 |
--------------------------------------------------------------------------------
/ch04_02_tags/test/e2e/templates/admin_new_book.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book creation view", function () {
4 | var exampleBook = {
5 | title : 'JavaScript effektiv',
6 | subtitle : '68 Dinge, die ein guter JavaScript-Entwickler wissen sollte',
7 | isbn : '978-3-86490-127-0',
8 | abstract : 'Wollen Sie JavaScript wirklich beherrschen?',
9 | numPages : 240,
10 | author : 'David Herman',
11 | publisher : {
12 | name: 'dpunkt.verlag',
13 | url : 'http://dpunkt.de/'
14 | }
15 | };
16 |
17 | beforeEach(function () {
18 | browser().navigateTo('/#/admin/books/new');
19 | browser().reload();
20 | });
21 |
22 | var selector = 'table.bm-book-list tr';
23 |
24 | it('should navigate to admin list view having one more book entry', function () {
25 | enterExampleBook('button.bm-submit-btn');
26 |
27 | expect(
28 | browser().location().path()
29 | ).toEqual('/admin/books');
30 |
31 | expect(
32 | repeater(selector).column('book.title')
33 | ).toContain(exampleBook.title);
34 | });
35 |
36 | it('should navigate to admin list view without having one more book entry', function () {
37 | enterExampleBook('button.bm-cancel-btn');
38 |
39 | expect(
40 | browser().location().path()
41 | ).toEqual('/admin/books');
42 |
43 | expect(
44 | repeater(selector).column('book.title')
45 | ).not().toContain(exampleBook.title);
46 | });
47 |
48 | var enterExampleBook = function(clickSelector) {
49 | input('book.title').enter(exampleBook.title);
50 | input('book.subtitle').enter(exampleBook.subtitle);
51 | input('book.isbn').enter(exampleBook.isbn);
52 | input('book.abstract').enter(exampleBook.abstract);
53 | input('book.numPages').enter(exampleBook.numPages);
54 | input('book.author').enter(exampleBook.author);
55 | input('book.publisher.name').enter(exampleBook.publisher.name);
56 | input('book.publisher.url').enter(exampleBook.publisher.url);
57 |
58 | element(clickSelector).click();
59 | };
60 | });
61 |
--------------------------------------------------------------------------------
/ch04_02_tags/test/e2e/templates/book_details.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book details view", function() {
4 |
5 | beforeEach(function() {
6 | browser().navigateTo('/');
7 | });
8 |
9 | it('should show the correct book details', function() {
10 | browser().navigateTo('#/books/978-3-89864-728-1');
11 |
12 | expect(element('.bm-book-title').html()).toBe(
13 | 'JavaScript für Enterprise-Entwickler'
14 | );
15 | expect(element('.bm-book-subtitle').html()).toBe(
16 | 'Professionell programmieren im Browser und auf dem Server'
17 | );
18 | expect(element('.bm-book-isbn').html()).toBe(
19 | 'ISBN: 978-3-89864-728-1'
20 | );
21 | expect(element('.bm-book-num-pages').html()).toBe(
22 | 'Seiten: 302'
23 | );
24 | expect(element('.bm-book-author').html()).toBe(
25 | 'Autor: Oliver Ochs'
26 | );
27 | expect(element('.bm-book-publisher-name').html()).toBe(
28 | 'dpunkt.verlag'
29 | );
30 | expect(element('.bm-book-publisher-name').attr('href')).toBe(
31 | 'http://dpunkt.de/'
32 | );
33 | expect(element('.bm-book-abstract').html()).toBe(
34 | 'JavaScript ist längst nicht mehr nur für' +
35 | ' klassische Webprogrammierer interessant.'
36 | );
37 | });
38 |
39 | it('should appropriately navigate to list view', function() {
40 | browser().navigateTo('#/books/978-3-89864-728-1');
41 |
42 | element('.bm-list-view-btn').click();
43 |
44 | expect(
45 | browser().location().path()
46 | ).toEqual('/books');
47 | });
48 |
49 | });
--------------------------------------------------------------------------------
/ch04_02_tags/test/unit/directives/tokenfield.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Directive: tokenfield', function () {
4 |
5 | var $compile,
6 | $rootScope,
7 | element,
8 | scope;
9 |
10 | var testTags = ['test1', 'test2', 'test3'];
11 |
12 | // load the application module
13 | beforeEach(module('bmApp'));
14 |
15 | // get a reference to all used services
16 | beforeEach(inject(function (_$compile_, _$rootScope_) {
17 | $compile = _$compile_;
18 | $rootScope = _$rootScope_;
19 | }));
20 |
21 | // actual init logic
22 | beforeEach(function() {
23 | scope = $rootScope.$new();
24 |
25 | scope.book = {
26 | tags: angular.copy(testTags)
27 | };
28 |
29 | element = $compile('')(scope);
30 | });
31 |
32 | it('should properly create available tokens on initialization', function () {
33 | var tokens = element.parent().find('div.token');
34 |
35 | expect(tokens.length).toBe(testTags.length);
36 |
37 | tokens.each(function(index, token) {
38 | expect(angular.element(token).data('value')).toEqual(testTags[index]);
39 | });
40 | });
41 |
42 | it('should properly add new tokens', function () {
43 | var tokenCount = element.parent().find('div.token').length,
44 | tokenInput = element.parent().find('input.token-input'),
45 | testToken = 'test4';
46 |
47 | tokenInput.focus();
48 | tokenInput.val(testToken);
49 | tokenInput.blur();
50 |
51 | var tokenCountAfter = element.parent().find('div.token').length;
52 | expect(tokenCountAfter).toBe(tokenCount + 1);
53 | expect(scope.book.tags.length).toBe(tokenCountAfter);
54 | expect(element.parent().html()).toContain(testToken);
55 | });
56 |
57 | it('should properly remove new tokens', function () {
58 | var indexToRemove = 0;
59 |
60 | angular.element(
61 | element.parent().find('div.token')[indexToRemove]
62 | ).find('a.close').click();
63 |
64 | expect(element.parent().find('div.token').length).toBe(testTags.length - 1);
65 | expect(scope.book.tags.length).toBe(testTags.length - 1);
66 | expect(element.parent().html()).not.toContain(testTags[indexToRemove]);
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/ch04_03_http/app/component_templates/directives/tags.html:
--------------------------------------------------------------------------------
1 |
2 | {{ tag }}
3 |
--------------------------------------------------------------------------------
/ch04_03_http/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BookMonkey
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/ch04_03_http/app/lib/jquery-ui/images/ui-bg_flat_75_ffffff_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angularjs-de/dpunkt-buch-beispiele/b94596cd63468df61064b3cbbcf19d9c9dda2c67/ch04_03_http/app/lib/jquery-ui/images/ui-bg_flat_75_ffffff_40x100.png
--------------------------------------------------------------------------------
/ch04_03_http/app/lib/jquery-ui/images/ui-bg_glass_75_dadada_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/angularjs-de/dpunkt-buch-beispiele/b94596cd63468df61064b3cbbcf19d9c9dda2c67/ch04_03_http/app/lib/jquery-ui/images/ui-bg_glass_75_dadada_1x400.png
--------------------------------------------------------------------------------
/ch04_03_http/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var bmApp = angular.module('bmApp', ['ngRoute']);
4 |
5 | bmApp.config(function ($routeProvider) {
6 | $routeProvider.when('/books/:isbn', {
7 | //templateUrl: 'templates/book_details_with_expressions.html',
8 | templateUrl: 'templates/book_details.html',
9 | controller: 'BookDetailsCtrl'
10 | })
11 | .when('/books', {
12 | templateUrl: 'templates/book_list.html',
13 | controller: 'BookListCtrl'
14 | })
15 |
16 | /* Admin routes */
17 | .when('/admin/books', {
18 | // we reuse the template from the list view
19 | templateUrl: 'templates/book_list.html',
20 | controller: 'AdminBookListCtrl'
21 | })
22 | .when('/admin/books/new', {
23 | templateUrl: 'templates/admin/book_form.html',
24 | controller: 'AdminNewBookCtrl'
25 | })
26 | .when('/admin/books/:isbn/edit', {
27 | templateUrl: 'templates/admin/book_form.html',
28 | controller: 'AdminEditBookCtrl'
29 | })
30 | .when('/admin/books/:isbn/delete', {
31 | templateUrl: 'templates/admin/book_delete.html',
32 | controller: 'AdminDeleteBookCtrl'
33 | })
34 |
35 | /* Route to reset backend */
36 | .when('/admin/reset', {
37 | templateUrl: 'templates/admin/reset.html',
38 | controller: 'AdminResetCtrl'
39 | })
40 |
41 | /* Default route */
42 | .otherwise({
43 | redirectTo: '/books'
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/ch04_03_http/app/scripts/controllers/admin_book_list.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('AdminBookListCtrl', function ($scope, BookDataService) {
4 | $scope.isAdmin = true;
5 |
6 | $scope.getBookOrder = function(book) {
7 | return book.title;
8 | };
9 |
10 | BookDataService.getBooks().then(function(res) {
11 | $scope.books = res.data;
12 | }, function(error) {
13 | console.log('An error occurred!', error);
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/ch04_03_http/app/scripts/controllers/admin_delete_book.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('AdminDeleteBookCtrl', function ($scope, $routeParams, $location, BookDataService) {
4 | var isbn = $routeParams.isbn;
5 | BookDataService.getBookByIsbn(isbn).then(function(res) {
6 | $scope.book = res.data;
7 | }, function(error) {
8 | console.log('An error occurred!', error);
9 | });
10 |
11 | $scope.deleteBook = function(isbn) {
12 | BookDataService.deleteBookByIsbn(isbn).then(function() {
13 | goToAdminListView();
14 | }, function(error) {
15 | console.log('An error occurred!', error);
16 | });
17 |
18 | };
19 |
20 | $scope.cancel = function() {
21 | goToAdminListView();
22 | };
23 |
24 | var goToAdminListView = function() {
25 | $location.path('/admin/books');
26 | };
27 | });
28 |
--------------------------------------------------------------------------------
/ch04_03_http/app/scripts/controllers/admin_edit_book.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('AdminEditBookCtrl', function ($scope, $routeParams, $location, BookDataService) {
4 | $scope.isEditMode = true;
5 | $scope.submitBtnLabel = 'Buch editieren';
6 |
7 | var isbn = $routeParams.isbn;
8 | BookDataService.getBookByIsbn(isbn).then(function(res) {
9 | $scope.book = res.data;
10 | }, function(error) {
11 | console.log('An error occurred!', error);
12 | });
13 |
14 | $scope.submitAction = function() {
15 | BookDataService.updateBook($scope.book).then(function() {
16 | goToAdminListView();
17 | }, function(error) {
18 | console.log('An error occurred!', error);
19 | });
20 | };
21 |
22 | $scope.cancelAction = function() {
23 | goToAdminListView();
24 | };
25 |
26 | var goToAdminListView = function() {
27 | $location.path('/admin/books');
28 | };
29 | });
30 |
--------------------------------------------------------------------------------
/ch04_03_http/app/scripts/controllers/admin_new_book.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('AdminNewBookCtrl', function ($scope, $location, BookDataService) {
4 | $scope.book = {};
5 | $scope.submitBtnLabel = 'Buch anlegen';
6 |
7 | $scope.submitAction = function() {
8 | BookDataService.storeBook($scope.book).then(function() {
9 | goToAdminListView();
10 | }, function(error) {
11 | console.log('An error occurred!', error);
12 | });
13 | };
14 |
15 | $scope.cancelAction = function() {
16 | goToAdminListView();
17 | };
18 |
19 | var goToAdminListView = function() {
20 | $location.path('/admin/books');
21 | };
22 | });
23 |
--------------------------------------------------------------------------------
/ch04_03_http/app/scripts/controllers/admin_reset.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('AdminResetCtrl', function ($scope, $http) {
4 | $scope.reset = false;
5 |
6 | $http.get('http://localhost:4730/api/reset').then(function () {
7 | $scope.reset = true;
8 | }, function (error) {
9 | console.log('An error occurred!', error);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/ch04_03_http/app/scripts/controllers/book_details.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('BookDetailsCtrl',
4 | function ($scope, $location, $routeParams, BookDataService) {
5 | var isbn = $routeParams.isbn;
6 |
7 | BookDataService.getBookByIsbn(isbn).then(function(res) {
8 | $scope.book = res.data;
9 | }, function(error) {
10 | console.log('An error occurred!', error);
11 | });
12 |
13 | $scope.goToListView = function() {
14 | if ($location.path().indexOf('/admin') === 0) {
15 | $location.path('/admin/books');
16 | }
17 | else {
18 | $location.path('/books');
19 | }
20 | };
21 | });
22 |
--------------------------------------------------------------------------------
/ch04_03_http/app/scripts/controllers/book_list.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.controller('BookListCtrl', function ($scope, BookDataService) {
4 | $scope.getBookOrder = function(book) {
5 | return book.title;
6 | };
7 |
8 | BookDataService.getBooks().then(function(res) {
9 | $scope.books = res.data;
10 | }, function(error) {
11 | console.log('An error occurred!', error);
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/ch04_03_http/app/scripts/directives/tags.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.directive('tags', function() {
4 | return {
5 | restrict: 'E',
6 | scope: {
7 | tagData: '='
8 | },
9 | templateUrl: 'component_templates/directives/tags.html'
10 | }
11 | });
12 |
--------------------------------------------------------------------------------
/ch04_03_http/app/scripts/services/book_data.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | bmApp.factory('BookDataService', function($http) {
4 | var srv = {};
5 |
6 | srv._baseUrl = 'http://localhost:4730';
7 |
8 | // Service implementation
9 | srv.getBookByIsbn = function(isbn) {
10 | return $http.get(
11 | srv._baseUrl + '/api/books/' + isbn
12 | );
13 | };
14 |
15 | srv.getBooks = function() {
16 | return $http.get(
17 | srv._baseUrl + '/api/books'
18 | );
19 | };
20 |
21 | srv.getTags = function() {
22 | return $http.get(
23 | srv._baseUrl + '/api/tags'
24 | );
25 | };
26 |
27 | srv.storeBook = function(book) {
28 | return $http.post(
29 | srv._baseUrl + '/api/books', book
30 | );
31 | };
32 |
33 | srv.updateBook = function(book) {
34 | return $http.put(
35 | srv._baseUrl + '/api/books/' + book.isbn, book
36 | );
37 | };
38 |
39 | srv.deleteBookByIsbn = function(isbn) {
40 | return $http.delete(
41 | srv._baseUrl + '/api/books/' + isbn
42 | );
43 | };
44 |
45 | // Public API
46 | return {
47 | getBookByIsbn: function(isbn) {
48 | return srv.getBookByIsbn(isbn);
49 | },
50 | getBooks: function() {
51 | return srv.getBooks();
52 | },
53 | getTags: function() {
54 | return srv.getTags();
55 | },
56 | storeBook: function(book) {
57 | return srv.storeBook(book);
58 | },
59 | updateBook: function(book) {
60 | return srv.updateBook(book);
61 | },
62 | deleteBookByIsbn: function(isbn) {
63 | return srv.deleteBookByIsbn(isbn);
64 | }
65 | };
66 | });
--------------------------------------------------------------------------------
/ch04_03_http/app/styles/main.css:
--------------------------------------------------------------------------------
1 | .bm-content {
2 | padding: 10px;
3 | }
4 |
5 | .split-screen {
6 | display: inline-block;
7 | box-sizing: border-box;
8 | vertical-align: top;
9 | width: 48%;
10 | }
11 |
12 | .simple-border {
13 | border: black dashed 1px;
14 | }
15 |
16 | .padded {
17 | padding: 10px;
18 | }
19 |
20 | form input.ng-invalid.ng-dirty {
21 | background-color: #ffcbc1;
22 | }
23 |
24 | form input.ng-invalid:focus {
25 | outline-color: red;
26 | }
27 |
28 | form input.ng-valid.ng-dirty {
29 | background-color: #1dca07;
30 | }
31 |
32 | .bm-tag {
33 | font-family: 'Consolas', monospace;
34 | font-size: 0.9em;
35 | background-color: #276dff;
36 | color: #ffffff;
37 | margin: 2px;
38 | padding: 2px;
39 | border-radius: 8px;
40 | }
--------------------------------------------------------------------------------
/ch04_03_http/app/templates/admin/book_delete.html:
--------------------------------------------------------------------------------
1 | Administrationsbereich
2 | Soll das Buch "{{ book.title }}" wirklich gelöscht werden?
3 |
5 |
6 |
--------------------------------------------------------------------------------
/ch04_03_http/app/templates/admin/book_form.html:
--------------------------------------------------------------------------------
1 | Administrationsbereich
2 |
3 |
Neues Buch anlegen
4 | Buch editieren
5 |
6 |
74 |
75 |
76 |
Vorschau
77 |
79 |
80 |
--------------------------------------------------------------------------------
/ch04_03_http/app/templates/admin/reset.html:
--------------------------------------------------------------------------------
1 | Administrationsbereich
2 | Backend wurde erfolgreich zurückgesetzt!
3 | Backend wird gerade zurückgesetzt...
--------------------------------------------------------------------------------
/ch04_03_http/app/templates/book_details.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
9 |
11 | -
12 | Verlag:
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Zurück
26 |
--------------------------------------------------------------------------------
/ch04_03_http/app/templates/book_details_with_expressions.html:
--------------------------------------------------------------------------------
1 | {{ book.title }}
2 | {{ book.subtitle }}
3 |
4 |
5 | - ISBN: {{ book.isbn }}
6 | - Seiten: {{ book.numPages }}
7 | - Autor: {{ book.author }}
8 | -
9 | Verlag:
10 | {{ book.publisher.name }}
13 |
14 |
15 |
16 |
17 | {{ book.abstract }}
--------------------------------------------------------------------------------
/ch04_03_http/app/templates/book_list.html:
--------------------------------------------------------------------------------
1 | Administrationsbereich
2 |
4 |
33 |
34 |
35 | Neues Buch anlegen
36 |
37 |
--------------------------------------------------------------------------------
/ch04_03_http/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for e2e tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | basePath: '',
7 |
8 | files: [
9 | 'test/e2e/**/*.js'
10 | ],
11 |
12 | logLevel: config.LOG_DEBUG,
13 |
14 | autoWatch: false,
15 |
16 | browsers: ['Chrome'],
17 |
18 | frameworks: ['ng-scenario'],
19 |
20 | singleRun: true,
21 |
22 | urlRoot: '/_karma_/',
23 |
24 | proxies: {
25 | '/': 'http://localhost:8080/'
26 | }
27 |
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/ch04_03_http/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for unit tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 |
10 | // frameworks to use
11 | frameworks: ['jasmine'],
12 |
13 |
14 | // list of files / patterns to load in the browser
15 | files: [
16 | 'app/lib/jquery/jquery-1.10.2.min.js',
17 | 'app/lib/jquery-ui/jquery-ui.min.js',
18 | 'app/lib/bootstrap-tokenfield/bootstrap-tokenfield.min.js',
19 | 'app/lib/angular/angular.js',
20 | 'app/lib/angular-route/angular-route.js',
21 | 'app/lib/angular-mocks/angular-mocks.js',
22 | 'app/scripts/app.js',
23 | 'app/scripts/**/*.js',
24 | 'test/unit/**/*.js'
25 | ],
26 |
27 |
28 | // list of files to exclude
29 | exclude: [
30 |
31 | ],
32 |
33 |
34 | // test results reporter to use
35 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
36 | reporters: ['progress'],
37 |
38 |
39 | // web server port
40 | port: 9876,
41 |
42 |
43 | // enable / disable colors in the output (reporters and logs)
44 | colors: true,
45 |
46 |
47 | // level of logging
48 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
49 | logLevel: config.LOG_DEBUG,
50 |
51 |
52 | // enable / disable watching file and executing tests whenever any file changes
53 | autoWatch: false,
54 |
55 |
56 | // Start these browsers, currently available:
57 | // - Chrome
58 | // - ChromeCanary
59 | // - Firefox
60 | // - Opera
61 | // - Safari (only Mac)
62 | // - PhantomJS
63 | // - IE (only Windows)
64 | browsers: ['Chrome'],
65 |
66 |
67 | // If browser does not capture in given timeout [ms], kill it
68 | captureTimeout: 60000,
69 |
70 |
71 | // Continuous Integration mode
72 | // if true, it capture browsers, run tests and exit
73 | singleRun: true
74 | });
75 | };
76 |
--------------------------------------------------------------------------------
/ch04_03_http/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bookmonkey",
3 | "version": "0.0.1",
4 | "description": "A simple AngularJS app serving as an example for the German book 'AngularJS: Eine praktische Einführung in das JavaScript-Framework'.",
5 | "main": "app/index.html",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "test": "karma start",
11 | "posttest": "karma start karma-e2e.conf.js"
12 | },
13 | "repository": "",
14 | "keywords": [
15 | "angularjs",
16 | "angularjs-de"
17 | ],
18 | "author": "Philipp Tarasiewicz , Robin Böhm ",
19 | "homepage": "http://angularjs.de/",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "karma": "0.12.0",
23 | "karma-chrome-launcher": "0.1.2",
24 | "karma-jasmine": "0.2.2",
25 | "karma-ng-scenario": "0.1.0",
26 | "karma-ng-html2js-preprocessor": "0.1.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ch04_03_http/test/e2e/templates/admin_delete_book.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book creation view", function () {
4 | var exampleBook = {
5 | title : 'JavaScript effektiv',
6 | subtitle : '68 Dinge, die ein guter JavaScript-Entwickler wissen sollte',
7 | isbn : '978-3-86490-127-0',
8 | abstract : 'Wollen Sie JavaScript wirklich beherrschen?',
9 | numPages : 240,
10 | author : 'David Herman',
11 | publisher : {
12 | name: 'dpunkt.verlag',
13 | url : 'http://dpunkt.de/'
14 | }
15 | };
16 |
17 | beforeEach(function () {
18 | browser().navigateTo('/#/admin/reset');
19 | browser().navigateTo('/#/admin/books/new');
20 | browser().reload();
21 | enterExampleBook('button.bm-submit-btn');
22 | });
23 |
24 | var selector = 'table.bm-book-list tr';
25 |
26 | it('should navigate to admin list view containing the edited book entry', function () {
27 | element(selector + ' a.bm-edit-link').query(function (editLinks, done) {
28 | for (var i = 0, n = editLinks.length; i < n; i++) {
29 | if (angular.element(editLinks[i]).text() === exampleBook.title) {
30 | executeAndCheck(i);
31 | break;
32 | }
33 | }
34 |
35 | done();
36 | });
37 | });
38 |
39 | var executeAndCheck = function(editLinkIndex) {
40 | var entryCount = 4;
41 |
42 | expect(
43 | repeater(selector).count()
44 | ).toBe(entryCount);
45 |
46 | // specify selector for the delete link
47 | var deleteLink = selector + ':nth-child('+ (editLinkIndex+1) +') a.bm-delete-link';
48 |
49 | // click on delete link
50 | element(deleteLink).click();
51 |
52 | expect(
53 | browser().location().path()
54 | ).toEqual('/admin/books/' + exampleBook.isbn + '/delete');
55 |
56 | // in deletion dialog click on delete button
57 | element('button.bm-delete-btn').click();
58 |
59 | // admin list view should show up
60 | expect(
61 | browser().location().path()
62 | ).toEqual('/admin/books');
63 |
64 | // list shouldn't contain the deleted entry anymore
65 | expect(
66 | repeater(selector).column('book.title')
67 | ).not().toContain(exampleBook.title);
68 |
69 | expect(
70 | repeater(selector).count()
71 | ).toBe(entryCount - 1);
72 | };
73 |
74 | var enterExampleBook = function(clickSelector) {
75 | input('book.title').enter(exampleBook.title);
76 | input('book.subtitle').enter(exampleBook.subtitle);
77 | input('book.isbn').enter(exampleBook.isbn);
78 | input('book.abstract').enter(exampleBook.abstract);
79 | input('book.numPages').enter(exampleBook.numPages);
80 | input('book.author').enter(exampleBook.author);
81 | input('book.publisher.name').enter(exampleBook.publisher.name);
82 | input('book.publisher.url').enter(exampleBook.publisher.url);
83 |
84 | element(clickSelector).click();
85 | };
86 | });
87 |
--------------------------------------------------------------------------------
/ch04_03_http/test/e2e/templates/admin_edit_book.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book creation view", function () {
4 | var exampleBook = {
5 | title : 'JavaScript effektiv',
6 | subtitle : '68 Dinge, die ein guter JavaScript-Entwickler wissen sollte',
7 | isbn : '978-3-86490-127-0',
8 | abstract : 'Wollen Sie JavaScript wirklich beherrschen?',
9 | numPages : 240,
10 | author : 'David Herman',
11 | publisher : {
12 | name: 'dpunkt.verlag',
13 | url : 'http://dpunkt.de/'
14 | }
15 | };
16 |
17 | beforeEach(function () {
18 | browser().navigateTo('/#/admin/reset');
19 | browser().navigateTo('/#/admin/books/new');
20 | browser().reload();
21 | enterExampleBook('button.bm-submit-btn');
22 | });
23 |
24 | var selector = 'table.bm-book-list tr';
25 |
26 | it('should navigate to admin list view containing the edited book entry', function () {
27 | element(selector + ' a.bm-edit-link').query(function (editLinks, done) {
28 | for (var i = 0, n = editLinks.length; i < n; i++) {
29 | if (angular.element(editLinks[i]).text() === exampleBook.title) {
30 | executeAndCheck(i);
31 | break;
32 | }
33 | }
34 |
35 | done();
36 | });
37 | });
38 |
39 | var executeAndCheck = function(editLinkIndex) {
40 | // specify selector for the edit link
41 | var editLink = selector + ':nth-child('+ (editLinkIndex+1) +') a.bm-edit-link';
42 |
43 | // click on edit link
44 | element(editLink).click();
45 |
46 | // in edit form enter 'TEST' into book title field
47 | input('book.title').enter('TEST');
48 |
49 | // click on submit button
50 | element('button.bm-submit-btn').click();
51 |
52 | // admin list view should show up
53 | expect(
54 | browser().location().path()
55 | ).toEqual('/admin/books');
56 |
57 | // there should be a 'TEST' entry
58 | expect(
59 | repeater(selector).column('book.title')
60 | ).toContain('TEST');
61 |
62 | // list shouldn't contain entry with old title
63 | expect(
64 | repeater(selector).column('book.title')
65 | ).not().toContain(exampleBook.title);
66 | };
67 |
68 | var enterExampleBook = function(clickSelector) {
69 | input('book.title').enter(exampleBook.title);
70 | input('book.subtitle').enter(exampleBook.subtitle);
71 | input('book.isbn').enter(exampleBook.isbn);
72 | input('book.abstract').enter(exampleBook.abstract);
73 | input('book.numPages').enter(exampleBook.numPages);
74 | input('book.author').enter(exampleBook.author);
75 | input('book.publisher.name').enter(exampleBook.publisher.name);
76 | input('book.publisher.url').enter(exampleBook.publisher.url);
77 |
78 | element(clickSelector).click();
79 | };
80 | });
81 |
--------------------------------------------------------------------------------
/ch04_03_http/test/e2e/templates/admin_new_book.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book creation view", function () {
4 | var exampleBook = {
5 | title : 'JavaScript effektiv',
6 | subtitle : '68 Dinge, die ein guter JavaScript-Entwickler wissen sollte',
7 | isbn : '978-3-86490-127-0',
8 | abstract : 'Wollen Sie JavaScript wirklich beherrschen?',
9 | numPages : 240,
10 | author : 'David Herman',
11 | publisher : {
12 | name: 'dpunkt.verlag',
13 | url : 'http://dpunkt.de/'
14 | }
15 | };
16 |
17 | beforeEach(function () {
18 | browser().navigateTo('/#/admin/reset');
19 | browser().navigateTo('/#/admin/books/new');
20 | browser().reload();
21 | });
22 |
23 | var selector = 'table.bm-book-list tr';
24 |
25 | it('should navigate to admin list view having one more book entry', function () {
26 | enterExampleBook('button.bm-submit-btn');
27 |
28 | expect(
29 | browser().location().path()
30 | ).toEqual('/admin/books');
31 |
32 | expect(
33 | repeater(selector).column('book.title')
34 | ).toContain(exampleBook.title);
35 | });
36 |
37 | it('should navigate to admin list view without having one more book entry', function () {
38 | enterExampleBook('button.bm-cancel-btn');
39 |
40 | expect(
41 | browser().location().path()
42 | ).toEqual('/admin/books');
43 |
44 | expect(
45 | repeater(selector).column('book.title')
46 | ).not().toContain(exampleBook.title);
47 | });
48 |
49 | var enterExampleBook = function(clickSelector) {
50 | input('book.title').enter(exampleBook.title);
51 | input('book.subtitle').enter(exampleBook.subtitle);
52 | input('book.isbn').enter(exampleBook.isbn);
53 | input('book.abstract').enter(exampleBook.abstract);
54 | input('book.numPages').enter(exampleBook.numPages);
55 | input('book.author').enter(exampleBook.author);
56 | input('book.publisher.name').enter(exampleBook.publisher.name);
57 | input('book.publisher.url').enter(exampleBook.publisher.url);
58 |
59 | element(clickSelector).click();
60 | };
61 | });
62 |
--------------------------------------------------------------------------------
/ch04_03_http/test/e2e/templates/book_details.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book details view", function() {
4 |
5 | beforeEach(function() {
6 | browser().navigateTo('/');
7 | });
8 |
9 | it('should show the correct book details', function() {
10 | browser().navigateTo('#/books/978-3-89864-728-1');
11 |
12 | expect(element('.bm-book-title').html()).toBe(
13 | 'JavaScript für Enterprise-Entwickler'
14 | );
15 | expect(element('.bm-book-subtitle').html()).toBe(
16 | 'Professionell programmieren im Browser und auf dem Server'
17 | );
18 | expect(element('.bm-book-isbn').html()).toBe(
19 | 'ISBN: 978-3-89864-728-1'
20 | );
21 | expect(element('.bm-book-num-pages').html()).toBe(
22 | 'Seiten: 302'
23 | );
24 | expect(element('.bm-book-author').html()).toBe(
25 | 'Autor: Oliver Ochs'
26 | );
27 | expect(element('.bm-book-publisher-name').html()).toBe(
28 | 'dpunkt.verlag'
29 | );
30 | expect(element('.bm-book-publisher-name').attr('href')).toBe(
31 | 'http://dpunkt.de/'
32 | );
33 | expect(element('.bm-book-abstract').html()).toBe(
34 | 'JavaScript ist längst nicht mehr nur für' +
35 | ' klassische Webprogrammierer interessant.'
36 | );
37 | });
38 |
39 | it('should appropriately navigate to list view', function() {
40 | browser().navigateTo('#/books/978-3-89864-728-1');
41 |
42 | element('.bm-list-view-btn').click();
43 |
44 | expect(
45 | browser().location().path()
46 | ).toEqual('/books');
47 | });
48 |
49 | });
--------------------------------------------------------------------------------
/ch04_03_http/test/unit/directives/tokenfield.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Directive: tokenfield', function () {
4 |
5 | // just to match the baseUrl in the BookDataService
6 | var baseUrl = 'http://localhost:4730';
7 |
8 | var $compile,
9 | $rootScope,
10 | $httpBackend,
11 | element,
12 | scope;
13 |
14 | var testTags = ['test1', 'test2', 'test3'];
15 |
16 | // load the application module
17 | beforeEach(module('bmApp'));
18 |
19 | // get a reference to all used services
20 | beforeEach(inject(function (_$compile_, _$rootScope_, _$httpBackend_) {
21 | $compile = _$compile_;
22 | $rootScope = _$rootScope_;
23 | $httpBackend= _$httpBackend_;
24 | }));
25 |
26 | // define trained responses
27 | beforeEach(function() {
28 | $httpBackend.when('GET', baseUrl + '/api/tags').respond(
29 | testTags
30 | );
31 | });
32 |
33 | // actual init logic
34 | beforeEach(function() {
35 | scope = $rootScope.$new();
36 |
37 | scope.book = {
38 | tags: angular.copy(testTags)
39 | };
40 |
41 | element = $compile('')(scope);
42 | $httpBackend.flush();
43 | });
44 |
45 | it('should properly create available tokens on initialization', function () {
46 | var tokens = element.parent().find('div.token');
47 |
48 | expect(tokens.length).toBe(testTags.length);
49 |
50 | tokens.each(function(index, token) {
51 | expect(angular.element(token).data('value')).toEqual(testTags[index]);
52 | });
53 | });
54 |
55 | it('should properly add new tokens', function () {
56 | var tokenCount = element.parent().find('div.token').length,
57 | tokenInput = element.parent().find('input.token-input'),
58 | testToken = 'test4';
59 |
60 | tokenInput.focus();
61 | tokenInput.val(testToken);
62 | tokenInput.blur();
63 |
64 | var tokenCountAfter = element.parent().find('div.token').length;
65 | expect(tokenCountAfter).toBe(tokenCount + 1);
66 | expect(scope.book.tags.length).toBe(tokenCountAfter);
67 | expect(element.parent().html()).toContain(testToken);
68 | });
69 |
70 | it('should properly remove new tokens', function () {
71 | var indexToRemove = 0;
72 |
73 | angular.element(
74 | element.parent().find('div.token')[indexToRemove]
75 | ).find('a.close').click();
76 |
77 | expect(element.parent().find('div.token').length).toBe(testTags.length - 1);
78 | expect(scope.book.tags.length).toBe(testTags.length - 1);
79 | expect(element.parent().html()).not.toContain(testTags[indexToRemove]);
80 | });
81 | });
82 |
--------------------------------------------------------------------------------
/ch07_01_requirejs/app/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 |
--------------------------------------------------------------------------------
/ch07_01_requirejs/app/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bookmonkey-requirejs",
3 | "version": "0.0.1",
4 | "homepage": "https://github.com/angularjs-de/dpunkt-buch-beispiele",
5 | "authors": [
6 | "Robin Böhm ",
7 | "Philipp Tarasiewicz "
8 | ],
9 | "license": "MIT",
10 | "ignore": [
11 | "**/.*",
12 | "node_modules",
13 | "bower_components",
14 | "test",
15 | "tests"
16 | ],
17 | "dependencies": {
18 | "requirejs": "~2.1.9",
19 | "angular": "~1.2.0",
20 | "angular-route": "~1.2.0"
21 | },
22 | "devDependencies": {
23 | "angular-mocks": "~1.2.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ch07_01_requirejs/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BookMonkey
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ch07_01_requirejs/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'angular',
3 | 'angularRoute'
4 | ], function (angular) {
5 | 'use strict';
6 | return angular.module('bmApp', ['ngRoute']);
7 | });
--------------------------------------------------------------------------------
/ch07_01_requirejs/app/scripts/config/routes.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'app',
3 | 'controllers/book_list',
4 | 'controllers/book_details'
5 | ], function (app) {
6 | 'use strict';
7 | app.config(function ($routeProvider) {
8 | $routeProvider.when('/books/:isbn', {
9 | //templateUrl: 'templates/book_details_with_expressions.html',
10 | templateUrl: 'templates/book_details.html',
11 | controller : 'BookDetailsCtrl'
12 | })
13 | .when('/books', {
14 | templateUrl: 'templates/book_list.html',
15 | controller : 'BookListCtrl'
16 | })
17 | .otherwise({
18 | redirectTo: '/books'
19 | });
20 | });
21 | });
--------------------------------------------------------------------------------
/ch07_01_requirejs/app/scripts/controllers/book_details.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'app',
3 | 'services/book_data'
4 | ], function (app) {
5 | "use strict";
6 | app.controller('BookDetailsCtrl',
7 | function ($scope, $location, $routeParams, BookDataService) {
8 | var isbn = $routeParams.isbn;
9 | $scope.book = BookDataService.getBookByIsbn(isbn);
10 |
11 | $scope.goToListView = function () {
12 | $location.path('/books');
13 | };
14 | });
15 | });
--------------------------------------------------------------------------------
/ch07_01_requirejs/app/scripts/controllers/book_list.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'app',
3 | 'services/book_data'
4 | ], function (app) {
5 | "use strict";
6 | app.controller('BookListCtrl', function ($scope, BookDataService) {
7 | $scope.getBookOrder = function (book) {
8 | return book.title;
9 | };
10 |
11 | $scope.books = BookDataService.getBooks();
12 | });
13 | });
14 |
15 |
--------------------------------------------------------------------------------
/ch07_01_requirejs/app/scripts/requireConfig.js:
--------------------------------------------------------------------------------
1 | require.config({
2 | paths: {
3 | angular: '../bower_components/angular/angular',
4 | angularRoute: '../bower_components/angular-route/angular-route'
5 | },
6 | shim: {
7 | 'angular' : {'exports' : 'angular'},
8 | 'angularRoute': ['angular']
9 | },
10 | priority: [
11 | "angular"
12 | ]
13 | });
14 |
15 | require( [
16 | 'angular',
17 | 'app',
18 | 'config/routes'
19 | ], function(angular, app) {
20 | 'use strict';
21 | var $html = angular.element(document.getElementsByTagName('html')[0]);
22 |
23 | angular.element().ready(function() {
24 | $html.addClass('ng-app');
25 | angular.bootstrap($html, [app['name']]);
26 | });
27 | });
--------------------------------------------------------------------------------
/ch07_01_requirejs/app/scripts/services/book_data.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'angular',
3 | 'app'
4 | ], function (angular, app) {
5 | "use strict";
6 | app.factory('BookDataService', function () {
7 | var srv = {};
8 |
9 | srv._books = [
10 | {
11 | title : 'JavaScript für Enterprise-Entwickler',
12 | subtitle : 'Professionell programmieren im Browser und auf dem Server',
13 | isbn : '978-3-89864-728-1',
14 | abstract : 'JavaScript ist längst nicht mehr nur für klassische Webprogrammierer interessant.',
15 | numPages : 302,
16 | author : 'Oliver Ochs',
17 | publisher: {
18 | name: 'dpunkt.verlag',
19 | url : 'http://dpunkt.de/'
20 | }
21 | },
22 | {
23 | title : 'Node.js & Co.',
24 | subtitle : 'Skalierbare, hochperformante und echtzeitfähige Webanwendungen professionell in JavaScript entwickeln',
25 | isbn : '978-3-89864-829-5',
26 | abstract : 'Nach dem Webbrowser erobert JavaScript nun auch den Webserver.',
27 | numPages : 334,
28 | author : 'Golo Roden',
29 | publisher: {
30 | name: 'dpunkt.verlag',
31 | url : 'http://dpunkt.de/'
32 | }
33 | },
34 | {
35 | title : 'CoffeeScript',
36 | subtitle : 'Einfach JavaScript',
37 | isbn : '978-3-86490-050-1',
38 | abstract : 'CoffeeScript ist eine junge, kleine Programmiersprache, die nach JavaScript übersetzt wird.',
39 | numPages : 200,
40 | author : 'Andreas Schubert',
41 | publisher: {
42 | name: 'dpunkt.verlag',
43 | url : 'http://dpunkt.de/'
44 | }
45 | }
46 | ];
47 |
48 | // Service implementation
49 | srv.getBookByIsbn = function (isbn) {
50 | for (var i = 0, n = srv._books.length; i < n; i++) {
51 | if (isbn === srv._books[i].isbn) {
52 | return srv._books[i];
53 | }
54 | }
55 |
56 | return null;
57 | };
58 |
59 | srv.getBooks = function () {
60 | // Copy the array in order not to expose internal data structures
61 | return angular.copy(srv._books);
62 | };
63 |
64 | // Public API
65 | return {
66 | getBookByIsbn: function (isbn) {
67 | return srv.getBookByIsbn(isbn);
68 | },
69 | getBooks : function () {
70 | return srv.getBooks();
71 | }
72 | };
73 | });
74 | });
--------------------------------------------------------------------------------
/ch07_01_requirejs/app/templates/book_details.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
9 |
11 | -
12 | Verlag:
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Zurück
25 |
--------------------------------------------------------------------------------
/ch07_01_requirejs/app/templates/book_details_with_expressions.html:
--------------------------------------------------------------------------------
1 | {{ book.title }}
2 | {{ book.subtitle }}
3 |
4 |
5 | - ISBN: {{ book.isbn }}
6 | - Seiten: {{ book.numPages }}
7 | - Autor: {{ book.author }}
8 | -
9 | Verlag:
10 | {{ book.publisher.name }}
13 |
14 |
15 |
16 |
17 | {{ book.abstract }}
--------------------------------------------------------------------------------
/ch07_01_requirejs/app/templates/book_list.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 | |
7 | |
8 | |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ch07_01_requirejs/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for e2e tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | basePath: '',
7 |
8 | files: [
9 | 'test/e2e/**/*.js'
10 | ],
11 |
12 | logLevel: config.LOG_DEBUG,
13 |
14 | autoWatch: false,
15 |
16 | browsers: ['Chrome'],
17 |
18 | frameworks: ['ng-scenario'],
19 |
20 | singleRun: true,
21 |
22 | urlRoot: '/_karma_/',
23 |
24 | proxies: {
25 | '/': 'http://localhost:8080/'
26 | }
27 |
28 | });
29 | };
30 |
--------------------------------------------------------------------------------
/ch07_01_requirejs/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for unit tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 |
10 | // frameworks to use
11 | frameworks: ['jasmine','requirejs'],
12 |
13 |
14 |
15 | // list of files / patterns to load in the browser
16 | files: [
17 | {pattern: 'app/bower_components/angular/angular.js', included: false},
18 | {pattern: 'app/bower_components/angular-route/angular-route.js', included: false},
19 | {pattern: 'app/bower_components/angular-mocks/angular-mocks.js', included: false},
20 | {pattern: 'app/scripts/app.js', included: false},
21 | {pattern: 'app/scripts/**/*.js', included: false},
22 | {pattern: 'test/unit/**/*.js', included: false},
23 |
24 | 'test/testRequireConfig.js'
25 |
26 | ],
27 |
28 |
29 | // list of files to exclude
30 | exclude: [
31 |
32 | ],
33 |
34 |
35 | // test results reporter to use
36 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
37 | reporters: ['progress'],
38 |
39 |
40 | // web server port
41 | port: 9876,
42 |
43 |
44 | // enable / disable colors in the output (reporters and logs)
45 | colors: true,
46 |
47 |
48 | // level of logging
49 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
50 | logLevel: config.LOG_DEBUG,
51 |
52 |
53 | // enable / disable watching file and executing tests whenever any file changes
54 | autoWatch: false,
55 |
56 |
57 | // Start these browsers, currently available:
58 | // - Chrome
59 | // - ChromeCanary
60 | // - Firefox
61 | // - Opera
62 | // - Safari (only Mac)
63 | // - PhantomJS
64 | // - IE (only Windows)
65 | browsers: ['Chrome'],
66 |
67 |
68 | // If browser does not capture in given timeout [ms], kill it
69 | captureTimeout: 60000,
70 |
71 |
72 | // Continuous Integration mode
73 | // if true, it capture browsers, run tests and exit
74 | singleRun: true
75 | });
76 | };
77 |
--------------------------------------------------------------------------------
/ch07_01_requirejs/test/e2e/templates/book_details.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book details view", function() {
4 |
5 | beforeEach(function() {
6 | browser().navigateTo('/');
7 | });
8 |
9 | it('should show the correct book details', function() {
10 | browser().navigateTo('#/books/978-3-89864-728-1');
11 |
12 | expect(element('.bm-book-title').html()).toBe(
13 | 'JavaScript für Enterprise-Entwickler'
14 | );
15 | expect(element('.bm-book-subtitle').html()).toBe(
16 | 'Professionell programmieren im Browser und auf dem Server'
17 | );
18 | expect(element('.bm-book-isbn').html()).toBe(
19 | 'ISBN: 978-3-89864-728-1'
20 | );
21 | expect(element('.bm-book-num-pages').html()).toBe(
22 | 'Seiten: 302'
23 | );
24 | expect(element('.bm-book-author').html()).toBe(
25 | 'Autor: Oliver Ochs'
26 | );
27 | expect(element('.bm-book-publisher-name').html()).toBe(
28 | 'dpunkt.verlag'
29 | );
30 | expect(element('.bm-book-publisher-name').attr('href')).toBe(
31 | 'http://dpunkt.de/'
32 | );
33 | expect(element('.bm-book-abstract').html()).toBe(
34 | 'JavaScript ist längst nicht mehr nur für' +
35 | ' klassische Webprogrammierer interessant.'
36 | );
37 | });
38 |
39 | it('should appropriately navigate to list view', function() {
40 | browser().navigateTo('#/books/978-3-89864-728-1');
41 |
42 | element('.bm-list-view-btn').click();
43 |
44 | expect(
45 | browser().location().path()
46 | ).toEqual('/books');
47 | });
48 |
49 | });
--------------------------------------------------------------------------------
/ch07_01_requirejs/test/e2e/templates/book_list.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("E2E: book list view", function () {
4 |
5 | // Define the array of books in the expected order.
6 | // Sorted by title.
7 | var expectedBooks = [
8 | {
9 | title: 'CoffeeScript',
10 | isbn: '978-3-86490-050-1',
11 | author: 'Andreas Schubert'
12 | },
13 | {
14 | title: 'JavaScript für Enterprise-Entwickler',
15 | isbn: '978-3-89864-728-1',
16 | author: 'Oliver Ochs'
17 | },
18 | {
19 | title: 'Node.js & Co.',
20 | isbn: '978-3-89864-829-5',
21 | author: 'Golo Roden'
22 | }
23 | ];
24 |
25 | // Derive an array that only contains titles
26 | // for easier expectation checks.
27 | var orderedTitles = expectedBooks.map(function(book) {
28 | return book.title;
29 | });
30 |
31 | beforeEach(function () {
32 | browser().navigateTo('#/books');
33 | browser().reload();
34 | });
35 |
36 | var selector = 'table.bm-book-list tr';
37 |
38 | it('should show the correct number of books', function () {
39 | expect(repeater(selector).count()).toEqual(expectedBooks.length);
40 | });
41 |
42 | it('should show the books in the proper order', function() {
43 | // Are they in the expected order (ascending sorted by title)?
44 | expect(repeater(selector).column('book.title')).toEqual(orderedTitles);
45 | });
46 |
47 | it('should show the correct book information', function() {
48 | // Do the other book details (isbn, author) match?
49 | for (var i = 0, n = expectedBooks.length; i < n; i++) {
50 | expect(repeater(selector).row(i))
51 | .toEqual(
52 | [
53 | expectedBooks[i].title,
54 | expectedBooks[i].author,
55 | expectedBooks[i].isbn
56 | ]
57 | );
58 | }
59 | });
60 |
61 | it('should allow filtering by book title', function() {
62 | // Coffee
63 | var searchText = orderedTitles[0].substr(0, 6);
64 | input('searchText').enter(searchText);
65 | expect(
66 | repeater(selector).column('book.title')
67 | ).toEqual([orderedTitles[0]]);
68 | });
69 |
70 | it('should allow filtering by author', function() {
71 | // Andreas
72 | var searchText = expectedBooks[0].author.substr(0, 7);
73 | input('searchText').enter(searchText);
74 | expect(
75 | repeater(selector).column('book.title')
76 | ).toEqual([orderedTitles[0]]);
77 | });
78 |
79 | it('should allow filtering by isbn', function() {
80 | // 050-1
81 | var searchText = expectedBooks[0].isbn.substr(-5, 5);
82 | input('searchText').enter(searchText);
83 | expect(
84 | repeater(selector).column('book.title')
85 | ).toEqual([orderedTitles[0]]);
86 | });
87 |
88 | it('should appropriately navigate to details view', function() {
89 | var i = 0,
90 | detailsLink = selector + ':nth-child('+ (i+1) +') a';
91 | element(detailsLink).click();
92 |
93 | expect(
94 | browser().location().path()
95 | ).toEqual('/books/' + expectedBooks[i].isbn);
96 | });
97 |
98 | });
--------------------------------------------------------------------------------
/ch07_01_requirejs/test/testRequireConfig.js:
--------------------------------------------------------------------------------
1 | // we get all the test files automatically
2 | var tests = [];
3 | for (var file in window.__karma__.files) {
4 | if (window.__karma__.files.hasOwnProperty(file)) {
5 | if (/spec\.js$/i.test(file)) {
6 | tests.push(file);
7 | }
8 | }
9 | }
10 |
11 | require.config({
12 | paths: {
13 | angular: '/base/app/bower_components/angular/angular',
14 | angularRoute: '/base/app/bower_components/angular-route/angular-route',
15 | angularMocks: '/base/app/bower_components/angular-mocks/angular-mocks'
16 |
17 | },
18 | baseUrl: '/base/app/scripts/',
19 | shim: {
20 | 'angular' : {'exports' : 'angular'},
21 | 'angularRoute': ['angular'],
22 | 'angularMocks': {
23 | deps:['angular'],
24 | 'exports':'angular.mock'
25 | }
26 | },
27 | deps: tests,
28 | callback: window.__karma__.start
29 | });
--------------------------------------------------------------------------------
/ch07_01_requirejs/test/unit/services/book_data.spec.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'angular',
3 | 'angularMocks',
4 | 'services/book_data'], function (angular, mock) {
5 |
6 |
7 | 'use strict';
8 |
9 | describe('Service: BookDataService', function () {
10 |
11 | var BookDataService;
12 |
13 | // load the application module
14 | beforeEach(module('bmApp'));
15 |
16 | // get a reference to the service
17 | beforeEach(inject(function (_BookDataService_) {
18 | BookDataService = _BookDataService_;
19 | }));
20 |
21 | describe('Public API', function () {
22 | it('should include a getBookByIsbn() function', function () {
23 | expect(BookDataService.getBookByIsbn).toBeDefined();
24 | });
25 |
26 | it('should include a getBooks() function', function () {
27 | expect(BookDataService.getBooks).toBeDefined();
28 | });
29 | });
30 |
31 | });
32 |
33 | });
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/app/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 |
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BookMonkey
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'angular',
3 | 'angularRoute'
4 | ], function (angular) {
5 | 'use strict';
6 | return angular.module('bmApp', ['ngRoute']);
7 | });
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/app/scripts/config/routes.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'app',
3 | 'controllers/book_list',
4 | 'controllers/book_details'
5 | ], function (app) {
6 | 'use strict';
7 | app.config(function ($routeProvider) {
8 | $routeProvider.when('/books/:isbn', {
9 | //templateUrl: 'templates/book_details_with_expressions.html',
10 | templateUrl: 'templates/book_details.html',
11 | controller : 'BookDetailsCtrl'
12 | })
13 | .when('/books', {
14 | templateUrl: 'templates/book_list.html',
15 | controller : 'BookListCtrl'
16 | })
17 | .otherwise({
18 | redirectTo: '/books'
19 | });
20 | });
21 | });
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/app/scripts/controllers/book_details.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'angular',
3 | 'app',
4 | 'services/book_data'
5 | ], function (angular, app) {
6 | "use strict";
7 |
8 | app.controller('BookDetailsCtrl',
9 | function ($scope, $location, $routeParams, BookDataService) {
10 | var isbn = $routeParams.isbn;
11 | $scope.book = BookDataService.getBookByIsbn(isbn);
12 |
13 | $scope.goToListView = function () {
14 | $location.path('/books');
15 | };
16 | });
17 | });
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/app/scripts/controllers/book_list.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'angular',
3 | 'app',
4 | 'services/book_data'
5 | ], function (angular, app) {
6 | "use strict";
7 |
8 | app.controller('BookListCtrl', function ($scope, BookDataService) {
9 | $scope.getBookOrder = function (book) {
10 | return book.title;
11 | };
12 |
13 | $scope.books = BookDataService.getBooks();
14 | });
15 | });
16 |
17 |
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/app/scripts/requireConfig.js:
--------------------------------------------------------------------------------
1 | require.config({
2 | paths: {
3 | angular: '//ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular',
4 | angularRoute: '//ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular-route'
5 | },
6 | shim: {
7 | 'angular' : {'exports' : 'angular'},
8 | 'angularRoute': ['angular']
9 | },
10 | priority: [
11 | "angular"
12 | ]
13 | });
14 |
15 | require( [
16 | 'angular',
17 | 'app',
18 | 'config/routes'
19 | ], function(angular, app) {
20 | 'use strict';
21 | var $html = angular.element(document.getElementsByTagName('html')[0]);
22 |
23 | angular.element().ready(function() {
24 | $html.addClass('ng-app');
25 | angular.bootstrap($html, [app['name']]);
26 | });
27 | });
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/app/scripts/services/book_data.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'angular',
3 | 'app'
4 | ], function (angular, app) {
5 | "use strict";
6 |
7 | app.factory('BookDataService', function () {
8 | var srv = {};
9 |
10 | srv._books = [
11 | {
12 | title : 'JavaScript für Enterprise-Entwickler',
13 | subtitle : 'Professionell programmieren im Browser und auf dem Server',
14 | isbn : '978-3-89864-728-1',
15 | abstract : 'JavaScript ist längst nicht mehr nur für klassische Webprogrammierer interessant.',
16 | numPages : 302,
17 | author : 'Oliver Ochs',
18 | publisher: {
19 | name: 'dpunkt.verlag',
20 | url : 'http://dpunkt.de/'
21 | }
22 | },
23 | {
24 | title : 'Node.js & Co.',
25 | subtitle : 'Skalierbare, hochperformante und echtzeitfähige Webanwendungen professionell in JavaScript entwickeln',
26 | isbn : '978-3-89864-829-5',
27 | abstract : 'Nach dem Webbrowser erobert JavaScript nun auch den Webserver.',
28 | numPages : 334,
29 | author : 'Golo Roden',
30 | publisher: {
31 | name: 'dpunkt.verlag',
32 | url : 'http://dpunkt.de/'
33 | }
34 | },
35 | {
36 | title : 'CoffeeScript',
37 | subtitle : 'Einfach JavaScript',
38 | isbn : '978-3-86490-050-1',
39 | abstract : 'CoffeeScript ist eine junge, kleine Programmiersprache, die nach JavaScript übersetzt wird.',
40 | numPages : 200,
41 | author : 'Andreas Schubert',
42 | publisher: {
43 | name: 'dpunkt.verlag',
44 | url : 'http://dpunkt.de/'
45 | }
46 | }
47 | ];
48 |
49 | // Service implementation
50 | srv.getBookByIsbn = function (isbn) {
51 | for (var i = 0, n = srv._books.length; i < n; i++) {
52 | if (isbn === srv._books[i].isbn) {
53 | return srv._books[i];
54 | }
55 | }
56 |
57 | return null;
58 | };
59 |
60 | srv.getBooks = function () {
61 | // Copy the array in order not to expose internal data structures
62 | return angular.copy(srv._books);
63 | };
64 |
65 | // Public API
66 | return {
67 | getBookByIsbn: function (isbn) {
68 | return srv.getBookByIsbn(isbn);
69 | },
70 | getBooks : function () {
71 | return srv.getBooks();
72 | }
73 | };
74 | });
75 | });
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/app/templates/book_details.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
9 |
11 | -
12 | Verlag:
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Zurück
25 |
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/app/templates/book_details_with_expressions.html:
--------------------------------------------------------------------------------
1 | {{ book.title }}
2 | {{ book.subtitle }}
3 |
4 |
5 | - ISBN: {{ book.isbn }}
6 | - Seiten: {{ book.numPages }}
7 | - Autor: {{ book.author }}
8 | -
9 | Verlag:
10 | {{ book.publisher.name }}
13 |
14 |
15 |
16 |
17 | {{ book.abstract }}
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/app/templates/book_list.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 | |
7 | |
8 | |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration for unit tests
2 |
3 | module.exports = function (config) {
4 | config.set({
5 |
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 |
10 | // frameworks to use
11 | frameworks: ['jasmine','requirejs'],
12 |
13 |
14 | // list of files / patterns to load in the browser
15 | files: [
16 | {pattern: 'app/scripts/**/*.js', included: false},
17 | {pattern: 'test/unit/**/*.js', included: false},
18 |
19 | 'test/testRequireConfig.js'
20 |
21 | ],
22 |
23 |
24 | // list of files to exclude
25 | exclude: [
26 |
27 | ],
28 |
29 |
30 | // test results reporter to use
31 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
32 | reporters: ['progress'],
33 |
34 |
35 | // web server port
36 | port: 9876,
37 |
38 |
39 | // enable / disable colors in the output (reporters and logs)
40 | colors: true,
41 |
42 |
43 | // level of logging
44 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
45 | logLevel: config.LOG_ERROR,
46 |
47 |
48 | // enable / disable watching file and executing tests whenever any file changes
49 | autoWatch: false,
50 |
51 |
52 | // Start these browsers, currently available:
53 | // - Chrome
54 | // - ChromeCanary
55 | // - Firefox
56 | // - Opera
57 | // - Safari (only Mac)
58 | // - PhantomJS
59 | // - IE (only Windows)
60 | browsers: ['Chrome'],
61 |
62 |
63 | // If browser does not capture in given timeout [ms], kill it
64 | captureTimeout: 60000,
65 |
66 |
67 | // Continuous Integration mode
68 | // if true, it capture browsers, run tests and exit
69 | singleRun: true
70 | });
71 | };
72 |
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/test/testRequireConfig.js:
--------------------------------------------------------------------------------
1 | // we get all the test files automatically
2 | var tests = [];
3 | for (var file in window.__karma__.files) {
4 | if (window.__karma__.files.hasOwnProperty(file)) {
5 | if (/spec\.js$/i.test(file)) {
6 | tests.push(file);
7 | }
8 | }
9 | }
10 |
11 | require.config({
12 | paths: {
13 | angular: '//ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular',
14 | angularRoute: '//ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular-route',
15 | angularMocks: '//ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular-mocks'
16 |
17 | },
18 | baseUrl: '/base/app/scripts/',
19 | shim: {
20 | 'angular' : {'exports' : 'angular'},
21 | 'angularRoute': ['angular'],
22 | 'angularMocks': {
23 | deps:['angular'],
24 | 'exports':'angular.mock'
25 | }
26 | },
27 | deps: tests,
28 | callback: window.__karma__.start
29 | });
--------------------------------------------------------------------------------
/ch07_02_requirejs_cdn/test/unit/services/book_data.spec.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'angular',
3 | 'angularMocks',
4 | 'services/book_data'], function () {
5 |
6 |
7 | 'use strict';
8 |
9 | describe('Service: BookDataService', function () {
10 |
11 | var BookDataService;
12 |
13 | // load the application module
14 | beforeEach(module('bmApp'));
15 |
16 | // get a reference to the service
17 | beforeEach(inject(function (_BookDataService_) {
18 | BookDataService = _BookDataService_;
19 | }));
20 |
21 | describe('Public API', function () {
22 | it('should include a getBookByIsbn() function', function () {
23 | expect(BookDataService.getBookByIsbn).toBeDefined();
24 | });
25 |
26 | it('should include a getBooks() function', function () {
27 | expect(BookDataService.getBooks).toBeDefined();
28 | });
29 | });
30 |
31 | describe('Public API usage', function () {
32 | describe('getBookByIsbn()', function () {
33 | it('should return the proper book object (valid isbn)', function () {
34 | var isbn = '978-3-86490-050-1',
35 | book = BookDataService.getBookByIsbn(isbn);
36 | expect(book.title).toBe('CoffeeScript');
37 | });
38 |
39 | it('should return null (invalid isbn)', function () {
40 | var isbn = 'test',
41 | book = BookDataService.getBookByIsbn(isbn);
42 | expect(book).toBeNull();
43 | });
44 | });
45 |
46 | describe('getBooks()', function () {
47 | it('should return a proper array of book objects', function () {
48 | var books = BookDataService.getBooks();
49 | expect(books.length).toBe(3);
50 | });
51 | });
52 | });
53 |
54 | });
55 |
56 | });
--------------------------------------------------------------------------------
/ch07_03_mobile/app/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components
2 |
--------------------------------------------------------------------------------
/ch07_03_mobile/app/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simple-ng-touch-example",
3 | "version": "0.0.1",
4 | "homepage": "https://github.com/angularjs-de/dpunkt-buch-beispiele",
5 | "authors": [
6 | "Robin Böhm ",
7 | "Philipp Tarasiewicz "
8 | ],
9 | "license": "MIT",
10 | "ignore": [
11 | "**/.*",
12 | "node_modules",
13 | "bower_components",
14 | "test",
15 | "tests"
16 | ],
17 | "dependencies": {
18 | "angular": "~1.2.0",
19 | "angular-touch": "~1.2.0"
20 | },
21 | "devDependencies": {
22 | "angular-mocks": "~1.2.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ch07_03_mobile/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | AngularJS ngTouch Example
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
Click: {{click}}
17 |
18 |
19 |
20 |
Swipe-right: {{right}}
21 |
Swipe-left: {{left}}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/ch07_03_mobile/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | angular.module('app', ['ngTouch']);
4 |
--------------------------------------------------------------------------------
/ch07_03_mobile/app/scripts/directives/draggable.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('app').directive('draggable', function ($swipe) {
4 | return {
5 | restrict: 'A',
6 | link: function (scope, element) {
7 | var width = element[0].offsetWidth,
8 | height = element[0].offsetHeight;
9 |
10 | var toggleActive = function () {
11 | element.toggleClass('swipe-active');
12 | };
13 |
14 | $swipe.bind(element, {
15 | 'start': toggleActive,
16 | 'move': function (coords) {
17 | element.css('left', coords.x-(width/2) + "px");
18 | element.css('top', coords.y-(height/2) + "px");
19 | },
20 | 'end': toggleActive,
21 | 'cancel': toggleActive
22 | });
23 | }
24 | };
25 | });
26 |
--------------------------------------------------------------------------------
/ch07_03_mobile/app/styles/draggable.css:
--------------------------------------------------------------------------------
1 | body > div {
2 | width: 200px;
3 | height: 100px;
4 | border: 1px solid black;
5 | }
6 |
7 | .draggable {
8 | background-color: red;
9 | position: absolute;
10 | width: 100px;
11 | height: 100px;
12 | border-radius: 50%;
13 | left: 300px;
14 | }
15 |
16 | .swipe-active {
17 | opacity: 0.5;
18 | }
19 |
20 | .ng-click-active {
21 | background-color: green;
22 | }
23 |
--------------------------------------------------------------------------------