Angular JS

Angular JS

Introduction to Angular JS, I gave at PlovdivConf 2014

7a0e72a6f55811246bb5d9a946fd2e49?s=128

Radoslav Stankov

May 17, 2014
Tweet

Transcript

  1. Radoslav Stankov PlovdivConf 17/05/2014

  2. Who I am? @rstankov ! ! ! ! http://rstankov.com

  3. None
  4. None
  5. Radoslav Stankov! JavaScript event-driven architecture OpenFest 2010

  6. Radoslav Stankov OpenFest 05/11/2011

  7. None
  8. None
  9. None
  10. Backbone.js problems

  11. Backbone.js problems

  12. Backbone.js problems • Application lifecycle

  13. Backbone.js problems • Application lifecycle • Conventions

  14. Backbone.js problems • Application lifecycle • Conventions • Plugin/module system

  15. Backbone.js problems • Application lifecycle • Conventions • Plugin/module system

    • Template engine
  16. Backbone.js problems • Application lifecycle • Conventions • Plugin/module system

    • Template engine • Subviews handling
  17. Backbone.js problems • Application lifecycle • Conventions • Plugin/module system

    • Template engine • Subviews handling • Routing
  18. Backbone.js problems • Application lifecycle • Conventions • Plugin/module system

    • Template engine • Subviews handling • Routing • Testability
  19. None
  20. None
  21. • Application lifecycle • Conventions • Plugin/module system • Template

    engine • Subviews handling • Routing • Testability Недостатъци на Backbone.js
  22. • Application lifecycle • Conventions • Plugin/module system • Template

    engine • Subviews handling • Routing • Testability Недостатъци на Backbone.js Angular
  23. • Application lifecycle! • Conventions • Plugin/module system • Template

    engine • Subviews handling • Routing • Testability Angular
  24. None
  25. • Application lifecycle • Conventions • Plugin/module system • Template

    engine • Subviews handling • Routing • Testability Angular
  26. • Application lifecycle • Conventions! • Plugin/module system • Template

    engine • Subviews handling • Routing • Testability Angular
  27. Backbone

  28. Angular

  29. • Application lifecycle • Conventions • Plugin/module system • Template

    engine • Subviews handling • Routing • Testability Angular
  30. • Application lifecycle • Conventions • Plugin/module system! • Template

    engine • Subviews handling • Routing • Testability Angular
  31. Plugin/module system

  32. app = angular.module('Demo', []);

  33. app = angular.module('Demo', []);

  34. app = angular.module('Demo', []);

  35. app = angular.module('Demo', ['ngResource', 'simpleFormat']);

  36. app = angular.module('Demo', ['ngResource', 'simpleFormat']);

  37. app = angular.module('Demo', ['ngResource', 'simpleFormat']);

  38. app.controller('demoCtrl', function($scope, Calculator) { $scope.calculator = new Calculator(0); });

  39. app.controller('demoCtrl', function($scope, Calculator) { $scope.calculator = new Calculator(0); });

  40. app.controller('demoCtrl', function($scope, Calculator) { $scope.calculator = new Calculator(0); });

  41. app.controller('demoCtrl', function($scope, Calculator) { $scope.calculator = new Calculator(0); });

  42. app.controller('demoCtrl', function($scope, Calculator) { $scope.calculator = new Calculator(0); });

  43. app.controller('demoCtrl', function($scope, Calculator) { $scope.calculator = new Calculator(0); });

  44. app.controller('demoCtrl', function($scope, Calculator) { $scope.calculator = new Calculator(0); });

  45. app.controller('demoCtrl', function($scope, Calculator) { $scope.calculator = new Calculator(0); });

  46. app.controller('demoCtrl', function($scope, Calculator) { $scope.calculator = new Calculator(0); });

  47. app.controller('demoCtrl', function($scope, Calculator) { $scope.calculator = new Calculator(0); });

  48. app.controller('demoCtrl', function($scope, Calculator) { $scope.calculator = new Calculator(0); });

  49. app.service('Product', function($resource) { return $resource("/api/products/:id", {id: '@id'}); });

  50. app.service('Product', function($resource) { return $resource("/api/products/:id", {id: '@id'}); });

  51. app.service('Product', function($resource) { return $resource("/api/products/:id", {id: '@id'}); });

  52. app.service('Product', function($resource) { return $resource("/api/products/:id", {id: '@id'}); });

  53. app.service('Product', function($resource) { return $resource("/api/products/:id", {id: '@id'}); });

  54. app.service('Product', function($resource) { return $resource("/api/products/:id", {id: '@id'}); });

  55. app.service('Product', function($resource) { return $resource("/api/products/:id", {id: '@id'}); });

  56. app.service('Product', function($resource) { return $resource("/api/products/:id", {id: '@id'}); });

  57. app.service('Product', function($resource) { return $resource("/api/products/:id", {id: '@id'}); });

  58. app.controller('demoController', function($scope) { $scope.sayHi = function() { window.alert('Hi'); } });

  59. app.controller('demoController', function($scope) { $scope.sayHi = function() { window.alert('Hi'); } });

  60. app.controller('demoController', function($scope) { $scope.sayHi = function() { window.alert('Hi'); } });

  61. app.controller('demoController', function($scope) { $scope.sayHi = function() { window.alert('Hi'); } });

  62. app.controller('demoController', function($scope) { $scope.sayHi = function() { window.alert('Hi'); } });

  63. app.controller('demoController', function($scope, $window) { $scope.sayHi = function() { $window.alert('Hi'); }

    });
  64. app.controller('demoController', function($scope, $window) { $scope.sayHi = function() { $window.alert('Hi'); }

    });
  65. app.controller('demoController', function($scope, $window) { $scope.sayHi = function() { $window.alert('Hi'); }

    });
  66. app.controller('demoController', function($scope, $window) { $scope.sayHi = function() { $window.alert('Hi'); }

    });
  67. app.controller('demoController', function($scope, $window) { $scope.sayHi = function() { $window.alert('Hi'); }

    });
  68. app.service('Product', function($resource) { return $resource("/api/products/:id", {id: '@id'}); });

  69. app.service('Product', function($resource) { return $resource("/api/products/:id", {id: '@id'}); });

  70. app.service('Product', function($resource) { return $resource("/api/products/:id", {id: '@id'}); });

  71. app.service('Product', ['$resource', function($resource) { return $resource("/api/products/:id", {id: '@id'}); }]);

  72. app.service('Product', ['$resource', function($resource) { return $resource("/api/products/:id", {id: '@id'}); }]);

  73. app.service('Product', ['$resource', function($resource) { return $resource("/api/products/:id", {id: '@id'}); }]);

  74. app.service('Product', ['$resource', function($resource) { return $resource("/api/products/:id", {id: '@id'}); }]);

  75. app.service('Product', ['$resource', function($resource) { return $resource("/api/products/:id", {id: '@id'}); }]);

  76. (function() { function Product($resource) { return $resource("/api/products/:id", {id: '@id'}); }

    ! Product.$inject = ['$resource'] ! app.service('Product', Product); })();
  77. (function() { function Product($resource) { return $resource("/api/products/:id", {id: '@id'}); }

    ! Product.$inject = ['$resource'] ! app.service('Product', Product); })();
  78. (function() { function Product($resource) { return $resource("/api/products/:id", {id: '@id'}); }

    ! Product.$inject = ['$resource'] ! app.service('Product', Product); })();
  79. (function() { function Product($resource) { return $resource("/api/products/:id", {id: '@id'}); }

    ! Product.$inject = ['$resource'] ! app.service('Product', Product); })();
  80. (function() { function Product($resource) { return $resource("/api/products/:id", {id: '@id'}); }

    ! Product.$inject = ['$resource'] ! app.service('Product', Product); })();
  81. (function() { function Product($resource) { return $resource("/api/products/:id", {id: '@id'}); }

    ! Product.$inject = ['$resource'] ! app.service('Product', Product); })();
  82. (function() { function Product($resource) { return $resource("/api/products/:id", {id: '@id'}); }

    ! Product.$inject = ['$resource'] ! app.service('Product', Product); })();
  83. (function() { function Product($resource) { return $resource("/api/products/:id", {id: '@id'}); }

    ! Product.$inject = ['$resource'] ! app.service('Product', Product); })();
  84. (function() { function Product($resource) { return $resource("/api/products/:id", {id: '@id'}); }

    ! Product.$inject = ['$resource'] ! app.service('Product', Product); })();
  85. value service factory provider injector.get(‘Person’) Person

  86. app.value('Person', { name: 'Rado', });

  87. app.value('Person', { name: 'Rado', });

  88. app.value('Person', { name: 'Rado', });

  89. app.value('Person', { name: 'Rado', });

  90. app.value('Person', { name: 'Rado', });

  91. app.controller(function($scope, Person, $window) { $window.alert(Person.name + ' - hi'); });

  92. app.controller(function($scope, Person, $window) { $window.alert(Person.name + ' - hi'); });

  93. app.controller(function($scope, Person, $window) { $window.alert(Person.name + ' - hi'); });

  94. app.controller(function($scope, Person, $window) { $window.alert(Person.name + ' - hi'); });

  95. app.controller(function($scope, Person, $window) { $window.alert(Person.name + ' - hi'); });

  96. injector.get(‘Person’) Person

  97. app.factory('Person', function($window) { var name = ''; ! return {

    setName: function(newName) { name = newName; }, getName: function() { return name; }, say: function(text) { $window.alert(name + ' - ' + text); } } });
  98. app.factory('Person', function($window) { var name = ''; ! return {

    setName: function(newName) { name = newName; }, getName: function() { return name; }, say: function(text) { $window.alert(name + ' - ' + text); } } });
  99. app.factory('Person', function($window) { var name = ''; ! return {

    setName: function(newName) { name = newName; }, getName: function() { return name; }, say: function(text) { $window.alert(name + ' - ' + text); } } });
  100. app.factory('Person', function($window) { var name = ''; ! return {

    setName: function(newName) { name = newName; }, getName: function() { return name; }, say: function(text) { $window.alert(name + ' - ' + text); } } });
  101. app.factory('Person', function($window) { var name = ''; ! return {

    setName: function(newName) { name = newName; }, getName: function() { return name; }, say: function(text) { $window.alert(name + ' - ' + text); } } });
  102. app.factory('Person', function($window) { var name = ''; ! return {

    setName: function(newName) { name = newName; }, getName: function() { return name; }, say: function(text) { $window.alert(name + ' - ' + text); } } });
  103. app.factory('Person', function($window) { var name = ''; ! return {

    setName: function(newName) { name = newName; }, getName: function() { return name; }, say: function(text) { $window.alert(name + ' - ' + text); } } });
  104. app.factory('Person', function($window) { var name = ''; ! return {

    setName: function(newName) { name = newName; }, getName: function() { return name; }, say: function(text) { $window.alert(name + ' - ' + text); } } });
  105. app.factory('Person', function($window) { var name = ''; ! return {

    setName: function(newName) { name = newName; }, getName: function() { return name; }, say: function(text) { $window.alert(name + ' - ' + text); } } });
  106. app.factory('Person', function($window) { var name = ''; ! return {

    setName: function(newName) { name = newName; }, getName: function() { return name; }, say: function(text) { $window.alert(name + ' - ' + text); } } });
  107. app.factory('Person', function($window) { var name = ''; ! return {

    setName: function(newName) { name = newName; }, getName: function() { return name; }, say: function(text) { $window.alert(name + ' - ' + text); } } });
  108. app.factory('Person', function($window) { var name = ''; ! return {

    setName: function(newName) { name = newName; }, getName: function() { return name; }, say: function(text) { $window.alert(name + ' - ' + text); } } });
  109. app.factory('Person', function($window) { var name = ''; ! return {

    setName: function(newName) { name = newName; }, getName: function() { return name; }, say: function(text) { $window.alert(name + ' - ' + text); } } });
  110. app.controller(function($scope, Person) { Person.setName('Radoslav Stankov'); Person.say('Hi'); });

  111. app.controller(function($scope, Person) { Person.setName('Radoslav Stankov'); Person.say('Hi'); });

  112. app.controller(function($scope, Person) { Person.setName('Radoslav Stankov'); Person.say('Hi'); });

  113. app.controller(function($scope, Person) { Person.setName('Radoslav Stankov'); Person.say('Hi'); });

  114. app.controller(function($scope, Person) { Person.setName('Radoslav Stankov'); Person.say('Hi'); });

  115. Cache? Cache = factory(…) false injector.get(‘Person’) Person true

  116. app.service('Person', function($window) { var name = ''; ! this.setName =

    function(newName) { name = newName; }; ! this.getName = function() { return name; }; ! this.say = function(text) { $window.alert(name + ' - ' + text); }; });
  117. app.service('Person', function($window) { var name = ''; ! this.setName =

    function(newName) { name = newName; }; ! this.getName = function() { return name; }; ! this.say = function(text) { $window.alert(name + ' - ' + text); }; });
  118. app.service('Person', function($window) { var name = ''; ! this.setName =

    function(newName) { name = newName; }; ! this.getName = function() { return name; }; ! this.say = function(text) { $window.alert(name + ' - ' + text); }; });
  119. app.service('Person', function($window) { var name = ''; ! this.setName =

    function(newName) { name = newName; }; ! this.getName = function() { return name; }; ! this.say = function(text) { $window.alert(name + ' - ' + text); }; });
  120. app.service('Person', function($window) { var name = ''; ! this.setName =

    function(newName) { name = newName; }; ! this.getName = function() { return name; }; ! this.say = function(text) { $window.alert(name + ' - ' + text); }; });
  121. app.controller(function($scope, Person) { Person.setName('Radoslav Stankov'); Person.say('Hi'); });

  122. Cache? Cache = new service(…) true false injector.get(‘Person’) Person

  123. app.provider('Person', function() { this.name = ''; ! this.$get = function($window)

    { var self = this; return { setName: function(newName) { self.name = newName; }, getName: function() { return self.name; }, say: function(text) { $window.alert(self.name + ' - ' + text); } }; } });
  124. app.provider('Person', function() { this.name = ''; ! this.$get = function($window)

    { var self = this; return { setName: function(newName) { self.name = newName; }, getName: function() { return self.name; }, say: function(text) { $window.alert(self.name + ' - ' + text); } }; } });
  125. app.provider('Person', function() { this.name = ''; ! this.$get = function($window)

    { var self = this; return { setName: function(newName) { self.name = newName; }, getName: function() { return self.name; }, say: function(text) { $window.alert(self.name + ' - ' + text); } }; } });
  126. app.provider('Person', function() { this.name = ''; ! this.$get = function($window)

    { var self = this; return { setName: function(newName) { self.name = newName; }, getName: function() { return self.name; }, say: function(text) { $window.alert(self.name + ' - ' + text); } }; } });
  127. app.provider('Person', function() { this.name = ''; ! this.$get = function($window)

    { var self = this; return { setName: function(newName) { self.name = newName; }, getName: function() { return self.name; }, say: function(text) { $window.alert(self.name + ' - ' + text); } }; } });
  128. app.provider('Person', function() { this.name = ''; ! this.$get = function($window)

    { var self = this; return { setName: function(newName) { self.name = newName; }, getName: function() { return self.name; }, say: function(text) { $window.alert(self.name + ' - ' + text); } }; } });
  129. app.provider('Person', function() { this.name = ''; ! this.$get = function($window)

    { var self = this; return { setName: function(newName) { self.name = newName; }, getName: function() { return self.name; }, say: function(text) { $window.alert(self.name + ' - ' + text); } }; } });
  130. app.provider('Person', function() { this.name = ''; ! this.$get = function($window)

    { var self = this; return { setName: function(newName) { self.name = newName; }, getName: function() { return self.name; }, say: function(text) { $window.alert(self.name + ' - ' + text); } }; } });
  131. app.provider('Person', function() { this.name = ''; ! this.$get = function($window)

    { var self = this; return { setName: function(newName) { self.name = newName; }, getName: function() { return self.name; }, say: function(text) { $window.alert(self.name + ' - ' + text); } }; } });
  132. app.config(function(PersonProvider){ PersonProvider.name = 'Rado'; });

  133. app.config(function(PersonProvider){ PersonProvider.name = 'Rado'; });

  134. app.config(function(PersonProvider){ PersonProvider.name = 'Rado'; });

  135. app.config(function(PersonProvider){ PersonProvider.name = 'Rado'; });

  136. app.config(function(PersonProvider){ PersonProvider.name = 'Rado'; });

  137. app.config(function(PersonProvider){ PersonProvider.name = 'Rado'; });

  138. app.config(function(PersonProvider){ PersonProvider.name = 'Rado'; });

  139. app.controller(function($scope, Person) { Person.say('Hi'); });

  140. app.controller(function($scope, Person) { Person.say('Hi'); });

  141. app.controller(function($scope, Person) { Person.say('Hi'); });

  142. Cache? Cache = new provider(…) true false injector.get(‘PersonProvider’) PersonProvider

  143. Cache? Cache = PersonProvider.$get(…) true false injector.get(‘Person’) Person injector.get(‘PersonProvider’)

  144. • Application lifecycle • Conventions • Plugin/module system • Template

    engine • Subviews handling • Routing • Testability Angular
  145. • Application lifecycle • Conventions • Plugin/module system • Template

    engine! • Subviews handling • Routing • Testability Angular
  146. Template engine

  147. <html ng-app> <body>
 <header> <input type="text" ng-model="value" /> </header>
 <article>

    <p>{{value}}</p> </article> </body> </html> 1
  148. <html ng-app> <body>
 <header> <input type="text" ng-model="value" /> </header>
 <article>

    <p>{{value}}</p> </article> </body> </html> 1
  149. <html ng-app> <body>
 <header> <input type="text" ng-model="value" /> </header>
 <article>

    <p>{{value}}</p> </article> </body> </html> 1
  150. <html ng-app> <body>
 <header> <input type="text" ng-model="value" /> </header>
 <article>

    <p>{{value}}</p> </article> </body> </html> 1
  151. <html ng-app> <body>
 <header> <input type="text" ng-model="value" /> </header>
 <article>

    <p>{{value}}</p> </article> </body> </html> 1
  152. <html ng-app> <body>
 <header> <input type="text" ng-model="value" /> </header>
 <article>

    <p>{{value}}</p> </article> </body> </html> 1
  153. <html ng-app> <body>
 <header> <input type="text" ng-model="value" /> </header>
 <article>

    <p>{{value}}</p> </article> </body> </html> 1
  154. <html ng-app="demo"> <body ng-controller="demoCtrl"> <header> <button ng-click="increase();">+</button> <button ng-click="decrease();">-</button> </header>

    <article> <p>{{value}}</p> </article> </body> </html>
  155. <html ng-app="demo"> <body ng-controller="demoCtrl"> <header> <button ng-click="increase();">+</button> <button ng-click="decrease();">-</button> </header>

    <article> <p>{{value}}</p> </article> </body> </html>
  156. <html ng-app="demo"> <body ng-controller="demoCtrl"> <header> <button ng-click="increase();">+</button> <button ng-click="decrease();">-</button> </header>

    <article> <p>{{value}}</p> </article> </body> </html>
  157. <html ng-app="demo"> <body ng-controller="demoCtrl"> <header> <button ng-click="increase();">+</button> <button ng-click="decrease();">-</button> </header>

    <article> <p>{{value}}</p> </article> </body> </html>
  158. <html ng-app="demo"> <body ng-controller="demoCtrl"> <header> <button ng-click="increase();">+</button> <button ng-click="decrease();">-</button> </header>

    <article> <p>{{value}}</p> </article> </body> </html>
  159. <html ng-app="demo"> <body ng-controller="demoCtrl"> <header> <button ng-click="increase();">+</button> <button ng-click="decrease();">-</button> </header>

    <article> <p>{{value}}</p> </article> </body> </html>
  160. <html ng-app="demo"> <body ng-controller="demoCtrl"> <header> <button ng-click="increase();">+</button> <button ng-click="decrease();">-</button> </header>

    <article> <p>{{value}}</p> </article> </body> </html>
  161. <html ng-app="demo"> <body ng-controller="demoCtrl"> <header> <button ng-click="increase();">+</button> <button ng-click="decrease();">-</button> </header>

    <article> <p>{{value}}</p> </article> </body> </html>
  162. <html ng-app="demo"> <body ng-controller="demoCtrl"> <header> <button ng-click="increase();">+</button> <button ng-click="decrease();">-</button> </header>

    <article> <p>{{value}}</p> </article> </body> </html>
  163. app.controller('demoCtrl', function($scope) { $scope.value = 0; $scope.increase = function() {

    $scope.value += 1; }; $scope.decrease = function() { $scope.value -= 1; }; }); 2
  164. app.controller('demoCtrl', function($scope) { $scope.value = 0; $scope.increase = function() {

    $scope.value += 1; }; $scope.decrease = function() { $scope.value -= 1; }; }); 2
  165. app.controller('demoCtrl', function($scope) { $scope.value = 0; $scope.increase = function() {

    $scope.value += 1; }; $scope.decrease = function() { $scope.value -= 1; }; }); 2
  166. app.controller('demoCtrl', function($scope) { $scope.value = 0; $scope.increase = function() {

    $scope.value += 1; }; $scope.decrease = function() { $scope.value -= 1; }; }); 2
  167. app.controller('demoCtrl', function($scope) { $scope.value = 0; $scope.increase = function() {

    $scope.value += 1; }; $scope.decrease = function() { $scope.value -= 1; }; }); 2
  168. app.controller('demoCtrl', function($scope) { $scope.value = 0; $scope.increase = function() {

    $scope.value += 1; }; $scope.decrease = function() { $scope.value -= 1; }; }); 2
  169. app.controller('demoCtrl', function($scope) { $scope.value = 0; $scope.increase = function() {

    $scope.value += 1; }; $scope.decrease = function() { $scope.value -= 1; }; }); 2
  170. <html ng-app="demo"> <body ng-controller="demoCtrl"> <header> <button ng-click="increase();">+</button> <button ng-click=“decrease();">-</button> </header>

    <article> <p>{{value}}</p> </article> </body> </html>
  171. <html ng-app="demo"> <body> <header ng-controller="actionsCtrl"> <button ng-click="increase();">+</button> <button ng-click=“decrease();">-</button> </header>

    <article ng-controller="displayCtrl"> <p>{{calculator.value}}</p> </article> </body> </html>
  172. <html ng-app="demo"> <body> <header ng-controller="actionsCtrl"> <button ng-click="increase();">+</button> <button ng-click=“decrease();">-</button> </header>

    <article ng-controller="displayCtrl"> <p>{{calculator.value}}</p> </article> </body> </html>
  173. <html ng-app="demo"> <body> <header ng-controller="actionsCtrl"> <button ng-click="increase();">+</button> <button ng-click=“decrease();">-</button> </header>

    <article ng-controller="displayCtrl"> <p>{{calculator.value}}</p> </article> </body> </html>
  174. <html ng-app="demo"> <body> <header ng-controller="actionsCtrl"> <button ng-click="increase();">+</button> <button ng-click=“decrease();">-</button> </header>

    <article ng-controller="displayCtrl"> <p>{{calculator.value}}</p> </article> </body> </html>
  175. <html ng-app="demo"> <body> <header ng-controller="actionsCtrl"> <button ng-click="increase();">+</button> <button ng-click=“decrease();">-</button> </header>

    <article ng-controller="displayCtrl"> <p>{{calculator.value}}</p> </article> </body> </html>
  176. <html ng-app="demo"> <body> <header ng-controller="actionsCtrl"> <button ng-click="increase();">+</button> <button ng-click=“decrease();">-</button> </header>

    <article ng-controller="displayCtrl"> <p>{{calculator.value}}</p> </article> </body> </html>
  177. <html ng-app="demo"> <body> <header ng-controller="actionsCtrl"> <button ng-click="increase();">+</button> <button ng-click=“decrease();">-</button> </header>

    <article ng-controller="displayCtrl"> <p>{{calculator.value}}</p> </article> </body> </html>
  178. app.value('Calculator', { value: 0 });

  179. app.controller('actionsCtrl', function($scope, Calculator) { $scope.increase = function() { Calculator.value +=

    1; }; $scope.decrease = function() { Calculator.value -= 1; }; });
  180. app.controller('actionsCtrl', function($scope, Calculator) { $scope.increase = function() { Calculator.value +=

    1; }; $scope.decrease = function() { Calculator.value -= 1; }; });
  181. app.controller('actionsCtrl', function($scope, Calculator) { $scope.increase = function() { Calculator.value +=

    1; }; $scope.decrease = function() { Calculator.value -= 1; }; });
  182. app.controller('actionsCtrl', function($scope, Calculator) { $scope.increase = function() { Calculator.value +=

    1; }; $scope.decrease = function() { Calculator.value -= 1; }; });
  183. app.controller('actionsCtrl', function($scope, Calculator) { $scope.increase = function() { Calculator.value +=

    1; }; $scope.decrease = function() { Calculator.value -= 1; }; });
  184. app.controller('actionsCtrl', function($scope, Calculator) { $scope.increase = function() { Calculator.value +=

    1; }; $scope.decrease = function() { Calculator.value -= 1; }; });
  185. app.controller('actionsCtrl', function($scope, Calculator) { $scope.increase = function() { Calculator.value +=

    1; }; $scope.decrease = function() { Calculator.value -= 1; }; });
  186. app.controller('displayCtrl', function($scope, Calculator) { $scope.calculator = Calculator }); 3

  187. app.controller('displayCtrl', function($scope, Calculator) { $scope.calculator = Calculator }); 3

  188. app.controller('displayCtrl', function($scope, Calculator) { $scope.calculator = Calculator }); 3

  189. app.controller('displayCtrl', function($scope, Calculator) { $scope.calculator = Calculator }); 3

  190. app.controller('displayCtrl', function($scope, Calculator) { $scope.calculator = Calculator }); 3

  191. Demo

  192. • Application lifecycle • Conventions • Plugin/module system • Template

    engine • Subviews handling • Routing • Testability Angular
  193. • Application lifecycle • Conventions • Plugin/module system • Template

    engine • Subviews handling! • Routing • Testability Angular
  194. Subviews handling

  195. <body ng-controller="demoCtrl"> <article> <ul> <li ng-repeat="task in tasks">{{task.text}}</li> <li> <form

    ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body>
  196. <body ng-controller="demoCtrl"> <article> <ul> <li ng-repeat="task in tasks">{{task.text}}</li> <li> <form

    ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body>
  197. <body ng-controller="demoCtrl"> <article> <ul> <li ng-repeat="task in tasks">{{task.text}}</li> <li> <form

    ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body>
  198. <body ng-controller="demoCtrl"> <article> <ul> <li ng-repeat="task in tasks">{{task.text}}</li> <li> <form

    ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body>
  199. <body ng-controller="demoCtrl"> <article> <ul> <li ng-repeat="task in tasks">{{task.text}}</li> <li> <form

    ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body>
  200. <body ng-controller="demoCtrl"> <article> <ul> <li ng-repeat="task in tasks">{{task.text}}</li> <li> <form

    ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body>
  201. <body ng-controller="demoCtrl"> <article> <ul> <li ng-repeat="task in tasks">{{task.text}}</li> <li> <form

    ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body>
  202. <body ng-controller="demoCtrl"> <article> <ul> <li ng-repeat="task in tasks">{{task.text}}</li> <li> <form

    ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body>
  203. <body ng-controller="demoCtrl"> <article> <ul> <li ng-repeat="task in tasks">{{task.text}}</li> <li> <form

    ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body>
  204. app.controller('demoCtrl', function($scope) { $scope.tasks = []; $scope.newTask = ""; $scope.createTask

    = function() { if ($scope.newTask.length > 0) { $scope.tasks.push({name: $scope.newTask}); $scope.newTask = ""; } }; }); 4
  205. app.controller('demoCtrl', function($scope) { $scope.tasks = []; $scope.newTask = ""; $scope.createTask

    = function() { if ($scope.newTask.length > 0) { $scope.tasks.push({name: $scope.newTask}); $scope.newTask = ""; } }; }); 4
  206. app.controller('demoCtrl', function($scope) { $scope.tasks = []; $scope.newTask = ""; $scope.createTask

    = function() { if ($scope.newTask.length > 0) { $scope.tasks.push({name: $scope.newTask}); $scope.newTask = ""; } }; }); 4
  207. app.controller('demoCtrl', function($scope) { $scope.tasks = []; $scope.newTask = ""; $scope.createTask

    = function() { if ($scope.newTask.length > 0) { $scope.tasks.push({name: $scope.newTask}); $scope.newTask = ""; } }; }); 4
  208. app.controller('demoCtrl', function($scope) { $scope.tasks = []; $scope.newTask = ""; $scope.createTask

    = function() { if ($scope.newTask.length > 0) { $scope.tasks.push({name: $scope.newTask}); $scope.newTask = ""; } }; }); 4
  209. app.controller('demoCtrl', function($scope) { $scope.tasks = []; $scope.newTask = ""; $scope.createTask

    = function() { if ($scope.newTask.length > 0) { $scope.tasks.push({name: $scope.newTask}); $scope.newTask = ""; } }; }); 4
  210. app.controller('demoCtrl', function($scope) { $scope.tasks = []; $scope.newTask = ""; $scope.createTask

    = function() { if ($scope.newTask.length > 0) { $scope.tasks.push({name: $scope.newTask}); $scope.newTask = ""; } }; }); 4
  211. <body ng-controller="demoCtrl"> <article> <ul> <li ng-repeat="task in tasks">{{task.text}}</li> <li> <form

    ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body>
  212. <body ng-controller="demoCtrl"> <header> <input type="search" ng-model=“search" placeholder="filter" /> </header> <article>

    <ul> <li ng-repeat="task in tasks | filter:search”>{{task.text}}</ <li> <form ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body> 5
  213. <body ng-controller="demoCtrl"> <header> <input type="search" ng-model=“search" placeholder="filter" /> </header> <article>

    <ul> <li ng-repeat="task in tasks | filter:search”>{{task.text}}</ <li> <form ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body> 5
  214. <body ng-controller="demoCtrl"> <header> <input type="search" ng-model=“search" placeholder="filter" /> </header> <article>

    <ul> <li ng-repeat="task in tasks | filter:search”>{{task.text}}</ <li> <form ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body> 5
  215. <body ng-controller="demoCtrl"> <header> <input type="search" ng-model=“search" placeholder="filter" /> </header> <article>

    <ul> <li ng-repeat="task in tasks | filter:search”>{{task.text}}</ <li> <form ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body> 5
  216. <body ng-controller="demoCtrl"> <header> <input type="search" ng-model=“search" placeholder="filter" /> </header> <article>

    <ul> <li ng-repeat="task in tasks | filter:search”>{{task.text}}</ <li> <form ng-submit="createTask();"> <input type="text" ng-model="newTask" /> </form> </li> </ul> </article> </body> 5
  217. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p>{{description}}</p> </article>
  218. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p>{{description}}</p> </article>
  219. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p>{{description}}</p> </article>
  220. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p>{{description}}</p> </article>
  221. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p>{{description}}</p> </article>
  222. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p>{{description}}</p> </article>
  223. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p>{{description}}</p> </article>
  224. <article> <h1>Name</h1> <time>05.05.2014</time> <strong>Price: $10.00</strong> <strong>Weight: 1.2kg</strong> <p>Long description</p> </article>

  225. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p>{{description}}</p> </article>
  226. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p>{{description}}</p> </article>
  227. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p>{{description}}</p> </article>
  228. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p ng-bind-html="description"></p> </article>
  229. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p ng-bind-html="description"></p> </article>
  230. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p ng-bind-html="description"></p> </article>
  231. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p ng-bind-html=“description | simpleFormat"></p> </article>
  232. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p ng-bind-html=“description | simpleFormat"></p> </article>
  233. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p ng-bind-html=“description | simpleFormat"></p> </article>
  234. app.filter('simpleFormat', function() { return function(text) { return (text || '').replace(/\n/g,

    "\n<br>"); }; });
  235. app.filter('simpleFormat', function() { return function(text) { return (text || '').replace(/\n/g,

    "\n<br>"); }; });
  236. app.filter('simpleFormat', function() { return function(text) { return (text || '').replace(/\n/g,

    "\n<br>"); }; });
  237. app.filter('simpleFormat', function() { return function(text) { return (text || '').replace(/\n/g,

    "\n<br>"); }; });
  238. app.filter('simpleFormat', function() { return function(text) { return (text || '').replace(/\n/g,

    "\n<br>"); }; });
  239. <article> <h1>{{name}}</h1> <time>{{createdAt | date:'dd.MM.yyyy'}}</time> <strong>Price: {{price | currency:'$' }}</strong>

    <strong>Weight: {{price | number: 2 }}kg</strong> <p ng-bind-html=“description | simpleFormat"></p> </article>
  240. bower install angular-simple-format https://github.com/RStankov/angular-simple-format

  241. • Application lifecycle • Conventions • Plugin/module system • Template

    engine • Subviews handling • Routing • Testability Angular
  242. • Application lifecycle • Conventions • Plugin/module system • Template

    engine • Subviews handling • Routing! • Testability Angular
  243. Routing

  244. <div ng-view></div>

  245. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  246. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  247. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  248. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  249. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  250. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  251. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  252. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  253. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  254. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  255. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  256. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  257. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  258. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  259. app.config(function($routeProvider) { $routeProvider.when('/people/:id', { templateUrl: 'people/show.html', controller: 'peopleCtrl', resolve: {

    person: function($routeParams, People) { return People.get(id: $routeParams.id).$promise; } } }); });
  260. • Application lifecycle • Conventions • Plugin/module system • Template

    engine • Subviews handling • Routing • Testability Angular
  261. • Application lifecycle • Conventions • Plugin/module system • Template

    engine • Subviews handling • Routing • Testability Angular
  262. Testability

  263. describe('myDirective', function() { var $compile; var $rootScope; ! beforeEach(module('myApp')); !

    beforeEach(inject(function(_$compile_, _$rootScope_){ $compile = _$compile_; $rootScope = _$rootScope_; })); ! it('replaces the element with the appropriate content', function() { var element = $compile("<my-directive></my-directive>")($rootScope); ! $rootScope.$digest(); ! expect(element.html()).toContain('PlovdivConf Part 2'); }); });
  264. describe('myDirective', function() { var $compile; var $rootScope; ! beforeEach(module('myApp')); !

    beforeEach(inject(function(_$compile_, _$rootScope_){ $compile = _$compile_; $rootScope = _$rootScope_; })); ! it('replaces the element with the appropriate content', function() { var element = $compile("<my-directive></my-directive>")($rootScope); ! $rootScope.$digest(); ! expect(element.html()).toContain('PlovdivConf Part 2'); }); });
  265. describe('myDirective', function() { var $compile; var $rootScope; ! beforeEach(module('myApp')); !

    beforeEach(inject(function(_$compile_, _$rootScope_){ $compile = _$compile_; $rootScope = _$rootScope_; })); ! it('replaces the element with the appropriate content', function() { var element = $compile("<my-directive></my-directive>")($rootScope); ! $rootScope.$digest(); ! expect(element.html()).toContain('PlovdivConf Part 2'); }); });
  266. describe('myDirective', function() { var $compile; var $rootScope; ! beforeEach(module('myApp')); !

    beforeEach(inject(function(_$compile_, _$rootScope_){ $compile = _$compile_; $rootScope = _$rootScope_; })); ! it('replaces the element with the appropriate content', function() { var element = $compile("<my-directive></my-directive>")($rootScope); ! $rootScope.$digest(); ! expect(element.html()).toContain('PlovdivConf Part 2'); }); });
  267. describe('myDirective', function() { var $compile; var $rootScope; ! beforeEach(module('myApp')); !

    beforeEach(inject(function(_$compile_, _$rootScope_){ $compile = _$compile_; $rootScope = _$rootScope_; })); ! it('replaces the element with the appropriate content', function() { var element = $compile("<my-directive></my-directive>")($rootScope); ! $rootScope.$digest(); ! expect(element.html()).toContain('PlovdivConf Part 2'); }); });
  268. describe('myDirective', function() { var $compile; var $rootScope; ! beforeEach(module('myApp')); !

    beforeEach(inject(function(_$compile_, _$rootScope_){ $compile = _$compile_; $rootScope = _$rootScope_; })); ! it('replaces the element with the appropriate content', function() { var element = $compile("<my-directive></my-directive>")($rootScope); ! $rootScope.$digest(); ! expect(element.html()).toContain('PlovdivConf Part 2'); }); });
  269. describe('myDirective', function() { var $compile; var $rootScope; ! beforeEach(module('myApp')); !

    beforeEach(inject(function(_$compile_, _$rootScope_){ $compile = _$compile_; $rootScope = _$rootScope_; })); ! it('replaces the element with the appropriate content', function() { var element = $compile("<my-directive></my-directive>")($rootScope); ! $rootScope.$digest(); ! expect(element.html()).toContain('PlovdivConf Part 2'); }); });
  270. describe('myDirective', function() { var $compile; var $rootScope; ! beforeEach(module('myApp')); !

    beforeEach(inject(function(_$compile_, _$rootScope_){ $compile = _$compile_; $rootScope = _$rootScope_; })); ! it('replaces the element with the appropriate content', function() { var element = $compile("<my-directive></my-directive>")($rootScope); ! $rootScope.$digest(); ! expect(element.html()).toContain('PlovdivConf Part 2'); }); });
  271. describe('myDirective', function() { var $compile; var $rootScope; ! beforeEach(module('myApp')); !

    beforeEach(inject(function(_$compile_, _$rootScope_){ $compile = _$compile_; $rootScope = _$rootScope_; })); ! it('replaces the element with the appropriate content', function() { var element = $compile("<my-directive></my-directive>")($rootScope); ! $rootScope.$digest(); ! expect(element.html()).toContain('PlovdivConf Part 2'); }); });
  272. var app = angular.module('myApp', []); ! app.directive('myDirective', function () {

    return { restrict: 'E', replace: true, template: 'PlovdivConf Part {{1 + 1}}' }; });
  273. var app = angular.module('myApp', []); ! app.directive('myDirective', function () {

    return { restrict: 'E', replace: true, template: 'PlovdivConf Part {{1 + 1}}' }; });
  274. var app = angular.module('myApp', []); ! app.directive('myDirective', function () {

    return { restrict: 'E', replace: true, template: 'PlovdivConf Part {{1 + 1}}' }; });
  275. var app = angular.module('myApp', []); ! app.directive('myDirective', function () {

    return { restrict: 'E', replace: true, template: 'PlovdivConf Part {{1 + 1}}' }; });
  276. var app = angular.module('myApp', []); ! app.directive('myDirective', function () {

    return { restrict: 'E', replace: true, template: 'PlovdivConf Part {{1 + 1}}' }; });
  277. var app = angular.module('myApp', []); ! app.directive('myDirective', function () {

    return { restrict: 'E', replace: true, template: 'PlovdivConf Part {{1 + 1}}' }; });
  278. var app = angular.module('myApp', []); ! app.directive('myDirective', function () {

    return { restrict: 'E', replace: true, template: 'PlovdivConf Part {{1 + 1}}' }; });
  279. var app = angular.module('myApp', []); ! app.directive('myDirective', function () {

    return { restrict: 'E', replace: true, template: 'PlovdivConf Part {{1 + 1}}' }; });
  280. var app = angular.module('myApp', []); ! app.directive('myDirective', function () {

    return { restrict: 'E', replace: true, template: 'PlovdivConf Part {{1 + 1}}' }; });
  281. var app = angular.module('myApp', []); ! app.directive('myDirective', function () {

    return { restrict: 'E', replace: true, template: 'PlovdivConf Part {{1 + 1}}' }; });
  282. var app = angular.module('myApp', []); ! app.directive('myDirective', function () {

    return { restrict: 'E', replace: true, template: 'PlovdivConf Part {{1 + 1}}' }; });
  283. Angular • Application lifecycle • Conventions • Plugin/module system •

    Template engine • Subviews handling • Routing • Testability
  284. https://github.com/RStankov/talk-angular-js The code commit by commit

  285. … even more • angular-resource • angular-animation • angular-i18n •

    angular-ui-router • angular-hammer • angular-bindonce • …
  286. Angular problems

  287. Angular problems

  288. Angular problems • Много магия

  289. Angular problems • Много магия • Доста логика в HTML

  290. Angular problems • Много магия • Доста логика в HTML

    • Performance проблеми
  291. Angular problems • Много магия • Доста логика в HTML

    • Performance проблеми • Learning curve
  292. Angular problems • Много магия • Доста логика в HTML

    • Performance проблеми • Learning curve • Добри практики
  293. И сега на къде? https://angularjs.org/

  294. None
  295. @rstankov Thanks :)

  296. Questions?