@drawoharaя ❤️ це! << натисніть мене 🐛 🫖 🧚
/angularjs-service-rails-api
опубліковано: 2014-06-10

Нещодавно мені довелося пірнути в AngularJS на сайті Rails, який ми будували для клієнта. Крім вмісту сторінок, клієнт має певні сторінки, які відображають заголовки, що посилаються на зовнішні джерела, але завантажуються у наш стек Rails за допомогою періодичної задачі rake. Сайт повинен бути сильно кешований через використання CDN, наприклад, CloudFront, але щоб заголовки залишалися свіжими, ми використовували AngularJS (тому що клієнт був знайомий з ним, тому простіше в обслуговуванні для них), щоб робити запити до стеку rails безпосередньо для останніх заголовків.

Це вимагало робити речі в міждоменному режимі, оскільки домен сайту мав би вказувати на CloudFront. Я ще не писав API Rails, який повертав JSONP або сервіс AngularJS, тому мені довелося дослідити це, і це працювало для мене. (Ви також можете прочитати гіст.)

Примітка: Якщо ви не знайомі з JSONP, це не матиме багато сенсу. Прочитайте це.

Сервіс AngularJS

У AngularJS є концепція сервісів, які є просто синглтонами, які можна використовувати де завгодно. У нашому випадку ви можете уявити наш сервіс заголовків як API-клієнт заголовків, який обгортає наші виклики JSONP у простіший і централізований синтаксис. AngularJS надає деякі сервіси за замовчуванням, але ви можете писати власні. Насправді, ми могли б використовувати вбудований $resource сервіс замість написання власного, однак у мене були деякі проблеми з роботою JSONP, і я хотів краще зрозуміти компоненти нижчого рівня AngularJS, тому я написав свій:

angular.module('app')
  .factory('HeadlineService', ['$http',
    function($http) {
      'use strict';

      var BASE_URL = "<%= [App.settings.api_base_url, '/services/headlines'].join %>",
          CALLBACK_STRING = "?callback=JSON_CALLBACK";

      return {
        getHeadlines: function(){
          return $http.jsonp(BASE_URL + CALLBACK_STRING)
        },
        getHeadlineForId: function(id){
          return $http.jsonp(BASE_URL + "/" + id + ".json" + CALLBACK_STRING)
        }
      }

    }
  ]);

Сервіс вище використовує вбудований $http сервіс, який просто надає обгортку AJAX, щоб робити запити до нашого бекенду rails для отримання заголовків. Декілька речей, на які варто звернути увагу:

Контролер AngularJS

З побудованим HeadlinesService ми тепер можемо використовувати 2 функції, які він повертає, у нашому додатку AngularJS:

angular.module('app')
  .controller('HeadlinesCtrl', ['$scope', 'HeadlineService',
    function($scope, HeadlineService) {
      'use strict';
 
      HeadlineService.getHeadlines().success(function(data, status, headers, config){
        $scope.headlines = data;
        
        HeadlineService.getHeadlineForId(data[0].id).success(function(data){
          $scope.single_headline = data;
        }).error(function(error, status, headers, config){
          alert('щось пішло не так при отриманні заголовку');
        });
      }).error(function(error, status, headers, config){
        alert('щось пішло не так при отриманні всіх заголовків');
      });
    }
  ]);

Для прикладу я просто отримую повний об’єкт заголовків для першого заголовку, повернутого в успішному колбеку getHeadlines.

Об’єкт $scope доступний для переглядів у AngularJS, тому ми можемо відобразити заголовки на сторінці, маючи перегляд, наприклад:

<section class="headline_section" ng-controller="HeadlinesCtrl">

  <h3>Ось деякі заголовки...</h3>

  <div class="headlines">

    <ul>
      <li ng-repeat="headline in headlines">
        <a href="{{ headline.url }}" target="_none">{{ headline.title }}</a>
      </li>
    <ul>

  </div>

  <div class="headline">
    <a href="{{ single_headline.url }}" target="_none">{{ single_headline.title }}</a>
  </div>

</section>

Бекенд Rails

Тепер, коли ми заставили AngularJS звертатися до нашого бекенду Rails, нам потрібно написати бекенд Rails! Наступний контролер є досить простим рубі на рейлзах, однак для повернення JSONP замість старого доброго JSON ми використовуємо опцію callback у методі render. Це говорить Rails, що ми використовуємо JSONP, і щоб обгорнути дані JSON, які ми йому надали, в цей колбек і правильно встановити заголовок Content-Type, щоб все відповідало стандарту JSONP, і наш клієнт AngularJS міг обробляти дані.

class Services::HeadlinesController < ApplicationController
 
  def index
    page   = (params[:page] || 1).to_i
    per    = (params[:per]  || 5).to_i
    offset = (page - 1) * per
 
    @headlines = Headline.where(:disabled => false).offset(offset).limit(per).to_a
    render :json => @headlines.to_json, :callback => params[:callback]