CoffeeScript и AngularJS

от автора

Перевод статьи Александра Хилла CoffeeScript and AngularJS. Это мой первый перевод и буду рад получить любые замечания и исправления.

AngularJS и CoffeeScript это отличная комбинация, не смотря на то, что CoffeeScript не пользуется большой популярностью в комьюнити AngularJS. В статье будут представлены несколько приемов, которые «облегчат» ваш код на AngularJS.

Короткая запись определения функции

В Angular используется большое число анонимных функций, и как следствие, используя короткую запись определения функции, можно сэкономить уйму времени. Как пример, давайте создадим директиву ‘enter key’, которая вычислияет выражение по нажатии на клавишу Enter с задержкой. Конечно, эта директива не несет в себе практической значимости, но должна дать представление о том, как взаимодействуют AngularJS и CoffeeScript.
Итак, как используем:

<input enter-key="submit()" enter-key-delay="10" /> 

JavaScript код:

app.directive('enterKey', function ($timeout) {   return function (scope, elem, attrs) {     elem.bind('keydown', function (e) {       if (e.keyCode === 13) {         $timeout(function () {           scope.$apply(attrs.enterKey);         }, +attrs.enterKeyDelay);       }     });   } }); 

сравните его с CoffeeScript:

app.directive 'enterKey', ($timeout) ->   (scope, elem, attrs) ->     elem.bind 'keydown', (e) ->       if e.keyCode is 13         $timeout ->           scope.$apply attrs.enterKey         , +attrs.enterKeyDelay 

В CoffeeScript символ -> используется для определения функции с параметрами, заключенными в круглые скобки перед ней. Если же функция не имеет параметров, то скобки не нужны, примером служит функция переданная в $timeout.
Мы избежали использования ключевого слова function 4 раза в 10 строчках кода, при этом код стал более читаемым и сократилось время для его написания. Также нет необходимости использования оператора return, т.к. CoffeeScript(как и Ruby) автоматически возвращает значение последнего выражения в функции.

Automatic Returns

Автоматический (или неявный) возврат — это еще одно преимущество, которое делает написание Angular кода быстрее и проще. Ниже представлены несколько примеров где видно полезность автоматического возврата. Разница, конечно, не большая, но в большом проекте будет заметна.

Фильтры

app.filter('capitalise', function (str) {   return str.charAt(0).toUpperCase() + str.slice(1); }); 

app.filter 'capitalise', (str) -> str.charAt(0) + str[1..] 

Бонусом в этом примере является синтаксис получение части массива str.slice(1) vs. str[1..].

Фабрики

app.factory('urlify', function (text) {   // nb: не используйте это в реальных приложениях   return text.toLowerCase().replace(" ", ""); }); 

app.factory 'urlify', (text) -> text.toLowerCase().replace " ", "" 

Директивы (опять)

В этом случае мы используем директиву определения объекта. Использование автоматического возврата CoffeeScript, синтаксиса объектного стиля YAML и короткую запись определения функции делает код намного более читаемым. Следующий пример взят с документации AngularJS.

app.directive('directiveName', function factory(injectables) {   return {     priority: 0,     template: '<div></div>',     templateUrl: 'directive.html',     replace: false,     transclude: false,     restrict: 'A',     scope: false,     controller: function ($scope, $element, $attrs, $transclude, otherInjectables) { ... },     compile: function compile(tElement, tAttrs, transclude) {       return {         pre: function preLink(scope, iElement, iAttrs, controller) { ... },         post: function postLink(scope, iElement, iAttrs, controller) { ... }       }     },     link: function postLink(scope, iElement, iAttrs) { ... }   } }); 

app.directive 'directiveName', (injectables) ->   priority: 0   template: '<div></div>'   templateUrl: 'directive.html'   replace: false   transclude: false   restrict: 'A'   scope: false   controller: ($scope, $element, $attrs, $transclude, otherInjectables) -> ...   compile: (tElement, tAttrs, transclude) ->     pre: (scope, iElement, iAttrs, controller) -> ...     post: (scope, iElement, iAttrs, controller) -> ...   link: (scope, iElement, iAttrs) -> ... 

Автоматический возврат и объектный синтаксис используются как для директивы определения объекта, так и внутри функции compile.

Классы

В CoffeeScript классы являются наиболее приятными включениями в возможности языка. Вот как они выглядят:

class Animal   constructor: (@name) ->    move: (meters) ->     alert @name + " moved #{meters}m."  snake = new Animal('snake')  snake.move(10) # alerts "snake moved 10m." 

В CoffeeScript символ @ является короткой записью для this. Поэтому @name станет this.name. Кроме того, добавление символа @ к параметру функции автоматически добавляет его к this, как видно в конструкторе Animal.
Вот пример аналогичного кода на JavaScript. Он, конечно, не идентичен скомпилированному коду CoffeeScript, но функционально эквивалентен.

function Animal(name) {   this.name = name; }  Animal.prototype.move = function (meters) {   alert(this.name + "moved" + meters + "m.") }  var snake = new Animal('snake')  snake.move(10) // alerts "snake moved 10m.", as before 

CoffeeScript создает именованную функцию и добавляет методы к ее prototype. В Angular это полезно в двух случаях: в сервисах и с синтаксисом new Controller.

Сервисы

В то время как функция фабрика будет непосредственно добавлена в другую функцию, в случае сервиса вначале будет создан его экземпляр перед добавлением. Более подробное объяснение различий можно найти в этой статье.
Из статьи, упомянутой выше, взят пример сервиса:

var gandalf = angular.module('gandalf', []);  function Gandalf() {   this.color = 'grey'; } Gandalf.prototype.comeBack = function () {   this.color = 'white'; }  gandalf.service('gandalfService', Gandalf);  var injector = angular.injector(['gandalf', 'ng']);  injector.invoke(function (gandalfService) {   console.log(gandalfService.color);   gandalfService.comeBack()   console.log(gandalfService.color); }); 

Выглядит как JavaScript код, скомпилированный из класса CoffeeScript. Поэтому, вместо редактирования prototype функции напрямую, мы можем использовать класс CoffeeScript:

gandalf = angular.module 'gandalf', []  gandalf.service 'gandalfService',   class Gandalf     constructor: -> @color = 'grey'     comeBack: -> @color = 'white'  injector = angular.injector ['gandalf', 'ng']  injector.invoke (gandalfService) ->   console.log gandalfService.color   gandalfService.comeBack()   console.log gandalfService.color 

Здесь мы передаем класс в сервис функцию. Вы можете определить его до сервиса и уже затем передать в сервис, но я предпочитаю чтобы класс был доступен исключительно через сервис.
Для того чтобы показать внедрение зависимостей, давайте создадим gandalf, который «возвращается» с задержкой, используя $timeout сервис.

gandalf.service 'gandalfService',   class Gandalf     constructor: (@$timeout) -> @color = 'grey'     comeBack: (time) ->       # 'двойная стрелка' в coffeescript связывает функцию с уже существующим       # значением 'this', поэтому @color - это та же сама переменная, что и выше.       @$timeout =>         @color = 'white'       , time 

Имя переменной в аргументах будет $timeout, поэтому внедрение зависимостей Angular будет продолжать работать. Добавление зависимостей к объекту позволяет нам иметь доступ к ним в методах, например, в comeBack.

Контроллеры

В AngularJS 1.1.5 представлен новый синтаксис контроллеров. Вы можете посмотреть видео для подробностей, но по сути новый синтаксис позволяет классу быть переданным как контроллер, а не функция с областью видимости. Поэтому html-код примера todoList на странице AngularJS будет выглядеть следующим образом:

<body ng-app="todoApp">   <div ng-controller="TodoCtrl as todos">     <span>{{todos.remaining()}} of {{todos.list.length}} remaining</span>     [<a href="" ng-click="todos.archive()">archive</a>]     <ul>       <li ng-repeat="todo in todos.list">         <input type="checkbox" ng-model="todo.done">         <span class="done-{{todo.done}}">{{todo.text}}</span>       </li>     </ul>     <form ng-submit="todos.addTodo()">       <input type="text" ng-model="todos.input" placeholder="add a new todo">       <input type="submit" value="add">     </form>   </div> </body> 

и использовать этот CoffeeScript класс в качестве контроллера:

app = angular.module 'todoApp', []  app.controller 'TodoCtrl',   class TodoCtrl     list: [       text: "learn coffescript"       done: false     ,       text: "learn angular"       done: true     ]      addTodo: ->       @list.push         text: @input         done: false       @input = ''      remaining: ->       count = 0       for todo in @list         count += if todo.done then 0 else 1       count      archive: ->       oldList = @list       @list = []       for todo in oldList         unless todo.done           @list.push todo 

Заключение

Как вы могли убедиться, CoffeeScript отличный компаньон для AngularJS. Хотя не для всех удобство записи CoffeeScript перевешивает его недостатки.
Есть целый ряд и других полезных функций, о которых я не упомянул в этой статье, таких как: параметры по умолчанию, экзистенциальный оператор, массивы и объекты. Все это вместе делает CoffeeScript приятным и продуктивным языком для написания программ. Если вы еще не рассматривали возможность его использования, перейдите на официальный сайт за подробностями.

ссылка на оригинал статьи http://habrahabr.ru/post/188406/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *