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 приятным и продуктивным языком для написания программ. Если вы еще не рассматривали возможность его использования, перейдите на официальный сайт за подробностями.
    • +11
    • 17,9k
    • 6
    Поделиться публикацией

    Похожие публикации

    Комментарии 6
      +6
      По-моему, статья ни о чем. CoffeeScript — отличный компаньон для любой JS библиотеки, а AngularJS прекрасно работает и с Coffee- и с JavaScript. В чем суть?
        +2
        В рекламе AngularJS же.
          +2
          На меня больше реклама кофескрипта подействовала.
        0
        В ролике к WebStorm автор виртуозно пользуется связкой AngularJS + CoffeeScript. Во второй половине
        www.youtube.com/watch?v=sa4jP9NJCeE&feature=player_embedded
          0
          Непонятен смысл статьи. CoffeeScript это просто упрощенный синтаксис для JS'а, который во многих случаях ускоряет написание кода, но привязывать его к конкретной библиотеке..? Непонятно. Я использую CS и для писанины на jQuery, и для EmberJS'а, иногда, и для чистого JS'а.

          А просто сравнение синтаксиса JS vs Coffee можно отлично увидеть на офсайте
            +1
            Я тоже недавно думал, как поинтересней задействовать все удобства CoffeeScript при работе с AngularJS.
            Наткнулся на такой вот пример http://jsfiddle.net/g/bXFdM/5/
            В нем демонстрируется не просто применение классов для создания контроллеров AngularJS, но еще и наследование одного контроллера от другого. Это вот, на мой взгляд, действительно интересно.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое