@drawoharaik ❤️ dit! << klik mij 🐛 🫖 🧚
/angularjs-service-rails-api
gepubliceerd op: 2014-06-10

Recentelijk moest ik duiken in AngularJS op een Rails-site die we voor een klant aan het bouwen waren. Naast pagina-inhoud heeft de klant bepaalde pagina's die koppen weergeven die verwijzen naar externe bronnen, maar die in onze Rails-stack worden opgehaald via een rake-taak die periodiek wordt uitgevoerd. De site moet zwaar worden gecached via een CDN, zoals CloudFront, maar om de koppen vers te houden, hebben we AngularJS gebruikt (omdat de klant ermee bekend was, dus eenvoudiger onderhoud voor hen) om de rails-stack rechtstreeks te vragen naar de nieuwste koppen.

Dit vereiste dingen op een cross-domain-manier te doen aangezien het domein van de site naar CloudFront zou verwijzen. Ik had nog nooit een Rails-API geschreven die JSONP of een AngularJS-service retourneerde, dus moest ik een beetje verkennen en dit is wat voor mij werkte. (Je kunt ook de gist lezen.)

Opmerking: Als je niet bekend bent met JSONP, maakt dit niet veel zin. Lees dit.

AngularJS Service

AngularJS heeft het concept van services, die gewoon singletons zijn die overal kunnen worden gebruikt waar je ze nodig hebt. In ons geval kun je aan onze koppenservice denken als de koppen-API-client die onze JSONP-aanroepen inbouwt in een eenvoudigere en gecentraliseerde syntax. AngularJS biedt enkele services standaard, maar je kunt je eigen schrijven. In feite, we hadden de ingebouwde $resource service kunnen gebruiken in plaats van onze eigen te schrijven, maar ik had moeite om JSONP aan de praat te krijgen en ik wilde de lagere AngularJS-componenten beter begrijpen, dus schreef ik mijn eigen:

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)
        }

    }
  ]);

De bovenstaande service maakt gebruik van de ingebouwde $http service, die gewoon een AJAX-wrapper biedt, om aanvragen te doen aan onze rails-back-end om koppen te verkrijgen. Een paar dingen om op te merken:

AngularJS Controller

Met onze HeadlinesService gebouwd kunnen we nu de 2 functies die het retourneert, gebruiken in onze AngularJS-app:

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('er is iets misgegaan bij het ophalen van een kop');
        });
      }).error(function(error, status, headers, config){
        alert('er is iets misgegaan bij het ophalen van alle koppen');
      });
    }
  ]);

Voor voorbeelddoeleinden haal ik gewoon het volledige kopobject op voor de eerste kop die wordt geretourneerd in de getHeadlines succes-callback.

Het $scope-object wordt blootgesteld aan de views in AngularJS, dus we kunnen de koppen op de pagina weergeven door een view te hebben zoals:

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

  <h3>Hier zijn enkele koppen...</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 Backend

Nu we AngularJS hebben laten bellen naar onze Rails-back-end, moeten we de Rails-back-end schrijven! De volgende controller is vrij basis Ruby on Rails, maar om JSONP te retourneren in plaats van ouderwets JSON, gebruiken we de callback-optie in de render-methode. Dat vertelt Rails dat we JSONP gebruiken en dat we de JSON-data die we hebben gegeven, moeten inbouwen in die callback en de Content-Type-header correct moeten instellen zodat alles voldoet aan de JSONP-standaard en onze AngularJS-client de data kan verwerken.

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]
  end
 
  def show
    @headline = Headline.find(params[:id])
    if @headline.present?
      render :json => @headline.to_json, :callback => params[:callback]
    else
      render :json => {error: 'geen kop gevonden'}.to_json, :callback => params[:callback], :status => 404
    end
  end
 
end

En daar hebben we het! Een werkende JSONP-gebaseerde AngularJS-app met een Rails-back-end.

Hier is de volledige gist als je het op die manier wilt lezen. Als dit je geholpen heeft of als je vragen hebt, laat het ons weten op Twitter op @dojo4.