19: AJAX: Ajax on Rails - LPC-Ltda/Ruby-on-Rails GitHub Wiki

Ajax no es una tecnología, son realmente muchas tecnologías, cada una resplandeciendo pore derecho propio, reuniéndose en nuevas formas poderosas -Jesse J. Garrett, who coined the name Ajax

Ajax es un acrónimo de asynchronous JavaScript and XML. Esto engloba técnicas que nos permiten avivar páginas web con comportamientos que ocurren fuera del ciclo de vida normal de un requerimiento HTTP (sin refrescar la página).

Algunos casos de uso ejemplos de Ajax son los siguientes:

  • Sugerencia de input "type ahead", en una búsqueda Google
  • Envío de datos en forma asíncrona
  • Navegación sin costura sobre mápas presentados en la web, como en Google Maps
  • Listas y tablas actualizadas dinámicamente, como en Gmail y otros servicios de email basados en la web
  • Hojas de calculo basadas en la web
  • Formularios que permiten edición en el lugar
  • Previsualización viva de escritura formateada a lo largo de un input de text

Ajax es posible gracias a XMLHttpRequestObject (o XHR abreviado), una API que está disponible en todos los browsers modernos. Esta permite código JavaScript en el browser para intercambiar data con el servidor y usarla para para cambiar la inteface de usuario de su aplicación al vuelo sin necesidad de refrescar la página. Trabajar directamente con XHR en una forma transversal compatible con el browser es dificil, por decir al menos sin embargo, somos afortunados ya que el eco-sistema open-source esta repleto de librerías Ajax JavaScript.

Incidentalente, Ajax, especialmente en Rails, tiene muy poco que ver con XML, a pesar de que está ahí al final del acronismo. De hecho, por defecto Rails 4 no incluye parsing de XML (sin embargo puede ser habilitado). La carga útil de estos requerimientos asíncronos que van y vienen del servidor puede ser cualqueir cosa. Frecuentemente sólo se ocupa de parámetros de formuario posteado al servidor y recibe fragmentos de HTML de vuelta para la inserción dinámica en el DOM de la página. Muchas veces hace más sentido para el servidor retornar data codificada en un tipo simple de JavaScript llamado JavaScript obejct notation (JSON).

Está fuera del alcance de este libro enseñarle los fundamentos de JavaScript y/o Ajax. Está también fuera de nuestro alcance bucear dentro de las consideraciones de diseño para agregar Ajax a su aplicación, elementos que son extensos y ocasionalmente controversiales. Una covertura propia de estos temas requeriría un libro completo y hay muchos de esos libros para elegir en el mercado. Por lo tanto, el resto del capítulo asumo que usted comprende que es Ajax y porque usted lo usa en sus aplicaciones. También asumo que usted tiene un entendimiento básico de la programación de JavaScript.

###19.0.1 Firebug

Firebug (http://www.getfirebug.com) es una extensión extremadamente poderosa para Firefox y una heramienta que debe tener para hacer funcionar Ajax. Esta le permite inspeccionar requerimientos Ajax y prueba el DOM de la página en extenso, incluso le permite cambiar elementos y estilos CSS al paso y ver el resultado en la pantalla de su browser. Es también un depurador de JavaScript muy poderoso que puede ser usado para mirar expresiones y puntos de quiebre.

Firebug también tiene una consola interactiva que le permite experimentar con JavaScript en el browser tal como si usted usara irb en Ruby. En algunos casos, el código de ejemplo en este capítulo es copiado desde la consola Firebug, la cual tienen un prompt >>>.

Como yo le cuento en tono de broma a mis alumnos de Ruby on Rails cuando cubrimos Ajax on Rails, "Incluso si usted no ha escuchado nada más yo digo, use Firebug! La productividad incrementa su experiencia, lo cual hará mi cuota rápidamente".

Alternativamente, si usted usa Chrome o Safari, ambos browsers tienen herramienta similares empaquetadas. Mi preferencia personal es Chrome DevTools (http://developers.google.com/chrome-developer-tools/), la cual es continuamente mejorada en cada nueva versión de Chrome.

##19.1 JavaScript discreto (unobtrusive)

La característica unobtrusive JavaScript (UJS) en Rails provee una API independiente de librería para especificar acciones Ajax. El aquipo de Rails ha provisto implementaciones UJS tanto para jQuery como para Prototype, disponible en https://github.com/rails/jquery-ujs y https://github.com/rails/prototype-rails, respectivamente. Por defecto, las aplicaciones recién creadas de Rails usan jQuery como su librería JavaScript elegida.

Integrar jQuery dentro de su aplicación Rails, simplemente incluya la gema jquery-rails en su Gemfile y ejecute bundle install. A continuación, asegúrese que las instrucciones correctas estén presentes en su archivo manifiesto JavaScript (como se ve en el siguiente texto).

# Gemfile
gem 'jquery-rails'

// app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs

Incluyendo estas instrucciones require en su archivo manifiesto JavaScript, tanto la librería jQuery como jquery_ujs son automáticamente empaquetados junto al resto de sus activos y servidos al browser eficientemente. El uso de archivos manifiesto en cubierto en detalle en el Capítulo 20, "Assets Pipeline"

###19.1.1 Uso de UJS

Previo a la versión 3.0, Rails no era unostrusive, lo que resultaba en un markup general que era acoplado a la librería de su opción. Por ejemplo, uno de los más dramáticos cambios causados por moverse a UJS fue la forma en que los link delete son genrados.

= link_to 'Delete', user_path(1), method: :delete,
  data: { confirm: "Are you sure?" }

Previo al uso de las técnicas UJS, el HTML resultante luciría como el siguiente:

<a href="/users/1" onclick="if (confirm('Sure')) { var f =
  document,createElement('form'); f.style.display = 'none';
  this.parentNode.appendChild(f); f.method = 'POST'; f.action =
  this.href; var m = document.createElement('input'); m.setAttribute('type',
  'hidden'); m.setAttribute('name', '_method'); m.setAttribute('value',
  'delete'); f.appendChild(m); f.submit(); }; return false; ">Delete</a>

Ahora, tomando ventaja de UJS luce asi:

<a data-confirm="Are you sure?" data-method="delete" href="/usuers/1"
  rel="nofollow">Delete</a>

Note que Rails usa el atributo estándar data- de HTML5 como un medio para adjuntar eventos personalizados a los elementos DOM.

También requerido para el soporte UJS de Rails es el csrf_meta_tag, el cual debe puesto en el head del documento y agregar los meta tags csrf-params y csrf-token usados en la generación dinámica de formularios.

%head
  = csrf_meta_tag

CSRF es por "cross-site request fogerty" y csrf_meta_tag es un método para ayudarnos a prevenir un ataque que pueda ocurrir. CSRF es cubierto en detalle en el Capítulo 15, "Seguridad".

###19.1.2 Helpers

Como cubrimos en el Capítulo 11, "Todo acerca de los helpers" Rails viene con métodos helpers de vistas para generar markups para elementos comunes de HTML. La siguiente es una lista de helpers Action View que tienen hooks para habilitar comportamiento Ajax via el driver unobtrusive JavaScript.

19.1.2.1 button_to

El helper button_to genera un form que contiene un botón único que hace un submit a la URL creada por el conjunto de opciones. Seteando la opción :remote a true le permite al driver JavaScript unobtrusive hacer un requerimiento Ajax en el background a la URL.

Para ilustarar, el markup

= button_to("new User", new_user_path, remote: true)

genera

<form action="/users/new" class="button_to" data-remote="true" method="post">
  <div>
    <input type="submit" value="New User">
    <input name="authenticity_token" type="hidden" value="HDVQ/5AHK+f5ChqN8qaah8Pd0gZzkoa21vqbvbayHBY=">
  </div>
</form>

Para desplegar un prompt de confirmación JavaScript con una pregunta especificada, entregue el atributo :confirm con una pregunta. Si es aceptada, el botón será enviado normalmente; de otra forma, ninguna acción es realizada.

= button_to("Deactivate", user, data: { confirm: 'Are you sure?' })

El driver unobtrusive JavaScript también permite deshabilitar el botón cuando es clickeado vía el atributo data :disable_with. Esto previene los requerimientos duplicados hacia el servidor causados por clicks de botón subsecuentes realizados por un usuario. Si es usado en combinación con remote: true, una vez que el requerimiento esta completo, el driver JavaScript unobtrusive volverá a habilitar el botón y reseteará el texto al valor original.

= button_to("Deactivate", user, data: { disable_with: 'Deactivating...' } )

19.1.2.2 form_for

El helper form_for es usado para crear formularios con una instancia de Active Model. Para habilitar el envío (submit) del formulario vía Ajax, setee la opción remote: true. Por ejemplo, asumamos que tenemos un formulario para crear un nuevo usuario,

= form_for(user, remote: true) do |f|
  ...

Generaría

<form accept-charset="UTF-8" action="/users" class="new_user" data-remote="true" id="new_user" method="post">
</form>

19.1.2.3 form_tag

Como con form_for, form_tag acepta la opción :remote para permitir el envío (submit) de formulario. Para información detallada de form_tag ver el Capítulo 11, "Todo acerca de los helpers".

19.1.2.4 link_to

El helper link_to crea un tag link de un nombre dado usando una URL creada por el conjunto de opciones. Setear la opción remote: true le permite al driver unobtrusive JavaScript hacer un requerimiento Ajax a la URL en lugar de seguir el link.

= link_to "User", user, remote: true

Por defecto, todos los links siempre realizarán un requerimiento GET de HTTP. Para especificar un verbo HTTP alternativo, como DELETE, uno puede setear la opción :method con el verbo HTTP deseado (:post, :patch, o :delete).

= link_to "Delete User", user, method: :delete

Si el usuario tiene JavaScript deshabilitado, el requerimiento siempre se caerá al usar GET, sin importar que :method haya usted especificado.

El helper link_to también acepta los atributos data :confirm y disable_with, cubierto pronto en la sección button_to en este capítulo.

###19.1.3 Eventos personalizados UJS JQuery

Cuando un form, link o button es marcado con el atributo data-remote, el driver UJS jQuery dispara los eventos personalizados de la Tabla 19.1.

Tabla 19.1 Eventos personalizados del driver UJS jQuery

Nombre del evento Parámetros Ocurrencia
ajax:before event El evento Ajax a comenzado; aborta si es detenido
ajax:beforeSend event, xhr, settings Antes que el requerimiento sea enviado; aborta si es detenido
ajax:send event, xhr El requerimiento esta enviado
ajax:success event, data, status, xhr El requerimiento está completado y la respuesta HTTP es un éxito
ajax:error event, xhr, status, error El requerimiento está completado y la respuesta HTTP es un error
ajax:complete event, xhr, status Después que el requerimiento está completado independiente del resultado
ajax:aborted:required event, elements Cuando existe un campo requerido en blanco en un formulario, continúa con el envío (submit) si es detenido
ajax:aborted:file event, elements Cuando existe un campo file lleno en el formulario, aborta si es detenido

Esto le permite, por ejemplo, manejar el éxito/falla de un envío (submit) Ajax. Para ilustrar, unamos los eventos ajax:success y ajax:error en el siguiente CoffeeScript:

$(document).ready ->
  $("#new_user")
    .on "ajax:success", (event, data, status, xhr) ->
      $(@).append xhr.responseText
    .on "ajax:errror", (event, xhr, status, error) ->
      $(@).append "Something bad happened"

##19.2 Turbolinks

Rails 4 introduce una controversial nueva característica llamada Turbolinks. Turbolinks es una librería JavaScript que cuando está habilitada, adjunta un menjador de click a todos los links de una página HTML. Cuando un link es clickeado, Turbolinks ejecuta un requerimiento Ajax y reemplaza el contenido de la página actual con el tag <body> de la respuesta.

Al usar Turbolinks también cambia la dirección de la página actual, permitiéndole al usuario hacer un bookmark de una página específica y usar el botón back como lo haría normalmente. Turbolinks usa el API history de HTML5 para lograr esto.

La gran ventaja de Turbolinks es que le permite al browser del usuario a sólo traer los stylesheets, JavaScripts e incluso las ímagenes requeridos una vez para renderear la página. Turbolinks efectivamente hace su sitio más rápido y sensible.

Para integrar Turbolinks dentro de su aplicación Rails existente, simplemente incluya la gea turbolinks en su Gemfile y corra bundle install. A continuación agregue require turbolinks en su archivo manifiesto de JavaScript.

# Gemfile
gem 'turbolinks'

// app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require turbolinks

###19.2.1 El uso de Turbolinks

En Rails 4, Turbolinks esta habilitado por defecto pero puede ser desabilitado si usted prefiere no usarlo. Para desabilitar el uso de Turbolinks para un link específico de una página, simpleente use el tag data-no-turbolink como sigue:

= link_to 'User', user_path(1), 'data-no-turbolink' => true

Esto no depende de algún framework particular, como jQuery o ZeptoJS, e intentará ser tan unobtrusive como sea posible.

Una advertencia de Turbolinks es que sólo funcionará con requerimientos GET, Usted puede, sin embargo, enviar requerimientos POST a un link con Turbolin habilitado, siempre y cuando se envie una redirección en lugar de un render inmediato. Esto es porque el método debe retornar el browser del usuario a una ubicación que pueda ser rendereada por un requerimiento GET. (pushState no registra el método HTTP, sólo la ruta por requerimiento).

###19.2.2 Eventos de Turbolinks

Cuando usamos Turbolinks, el evento ready de DOM puede ser disparado sólo en el requerimiento de página inicial, y sobrescribe el proceso de carga de página normal. Esto significa que usted no puede descansar en DOMContentLoaded o jQuery.ready() para gatillar la evaluación del código. Para gatillar el código que es dependiente de la carga de una página en Turbolinks, uno debe adjuntarlo al evento page:chage personalizado de Turbolinks.

$(document).on "page:change", -> alert "loaded!"

Cuando Turbolinks requiere una versión fresca de una página desde el servidor, los siguientes eventos son gatillados sobre el document:

page:fetch Turbolinks a comenzado a tomar una nueva página objetivo. page:receive La nueva página objetivo ha sido tomada desde el servidor. page:change La página ha sido parseada y cambiada a la nueva versión. page:update si jQuey es incluido, gatilla un evento ajaxSuccess de jQuery. page:load Término del proceso de carga de la página.

Por defecto, Turbolinks pone en cache 10 cargas de páginas para reducir los requerimientos al servidor. En este caso, el evento page:restore es disparado al final del proceso 'restore'.

jquery.turbolinks Si usted tiene una aplicación Rails existente que se une frecuentemente al evento jQuery.ready, usted puede desear verlo usando la librería jquery.turbolinks (https://github.com/kossnocorp/jquery.turbolinks). Cuando Turbolinks gatilla el evento page:load sobre un documento jquery.turbolinks lo hará automáticamente el evento jQuery.ready también.

###19.2.3 Controversia

Turbolinks indudablemente hace más rápidos muchos sitios evitando el reproceso de los tags <head>. Esto fue suficiente para el el equipo del nucleo de Rails lo empaquetara como parte oficial de Rails. Aunque esto está lleno de críticas. Algunas de ellas apuntan al door de cabeza de asegurarse de que las funciones Ajax a lo largo de la aplicación funcionen realmente bien cuando Turbolinks está habilitado. Otras apuntan a como rompe las apliaciones para browsers antiguos como IE8. Y otras apuntan a la ineficiencia. Ya que la mayoría de las aplicaciones se obtendrían lejos refrescando secciones de la página más pequeñas que el elemento <body> completo.

Pensamos que vale la pena darle una oportunidad a Turbolinks en sus aplicaciones, especialmente si usted está partiendo desde cero y puede tomar sus desafíos en cuenta desde el comienzo de un proyecto. Sin embargo, también admitimos que lo hemos desabilitado en muchos de nuestros proyectos. Su cantidad de trabajo puede variar.

Hay algunos de los temas que usted puede necesitar conocer con su uso de Turbolinks (http://net.tutsplus.com/tutorials/ruby/digging-into-rails-4):

Pérdidas de memoria Turbolinks no limpia y vuelve a cargar su JavaScript cuando la página cambia. Usted puede potencialmente los efectos de la pérdida de memoria en su aplicación, especialmente si usted usa mucho JavaScript. ** Event binding** Usted debe tomar en consideración los antiguos browsers. Asegúrese de escuchar los eventos page:*, Así como DOMContentLoaded también. Frameworks del lado del cliente Turbolinks puede no jugar limpio con otros frameworks del lado del cliente como Backbone, Angular, Knockout, Ember, etc...

##19.3 Ajax y JSON

JavaScript object notation (JSON) es una forma simple de codificar objetos JavaScript. Es tmbién considerado un formato de datos endependiente del lenguaje. Es el método preferido para intercambiar data entre el código de una aplicación web corriendo en el servidor y cualquier código corriendo en el browser, particularmente para requerimientos Ajax.

Rails provee un metodo to_json sobre todo objeto, usando un mecanismo sensible para hacerlo con todos los tipos. Por ejemplo, los objetos BigDecimal, aunque son números, son serializados a JSON como strings, ya que está es la mejor forma de representar un BigDecimal en una forma independiente del lenguaje. Usted puede siempre personalizar el método to_json de cualquiera de sus clases si lo desea, pero no es necesario hacerlo.

###19.3.1 link_to de Ajax

Para ilustrar un requerimiento Ajax, habilitemos nuestro controlador Client para responder a JSON y entregemosle un método para entregar el número de planillas de tiempo pendiente para cada cliente:

respond_to :html, :xml, :json
...
# GET /clients/counts
# GET /clients/counts.json
def counts
  respond_with(Client.all_with_count) do |format|
    format.html { redirect_to clients_path }
  end
end

Esto usa el método all_with_counts, el cual retorna un arreglo de hashmaps:

def self.all_with_counts
  all.map do |client|
    { id: client.id, draft_timesheets_count: client.timesheets.draft.count }
  end
end

Cuando GET /clients/counts es requerido y el tipo contenido es JSON, la respuesta es la siguiente:

[{"draft_timesheets_count":0, "id":20},
 {"draft_timasheets_count":1, "id":21}] 

Usted notará que en el código ejemplo que HTML y XML son también tipos de contenidos soportados, así depende de usted decidir qué formato funciona mejor para ellos. Veremos formatos diferentes a JSON en las siguientes secciones.

En este caso, nuestra vista index de Client requiere una respuesta en formato JSON:

- content_for :head do
  = javascript_include_tag 'clients.js'
...
%table#clients_list
...
  - @clients.each do |client|
    %tr[client]
      %td= client.name
      %td= client.code
      %td.draft_timesheets_count= client.timesheets.draft.count
...
= link_to 'Update draft timesheets count', counts_clients_path, 
         remote: true, data: { type: :json }, id: 'update_draft_timesheets'

Para completar la parte asíncrona de esta característica con Ajax-habilitado, también necesitamos agregar un manejador de eventos al evento ajax:success de UJS, disparado cuando la llamada Ajax sobre el elemento update_draft_timesheets se complete exitosamente. Aquí. jQuey es usado para unir una función JavaScript al evento una vez que la página es cargada. Esto es definido en clients.js:

$(function()) {
  $("#update_draft_timesheets").on("ajax:success", function(event, data) {
    $(data).each(function() {
      var td = $('#client_' + this.id + ' .draft_timesheets_counts')
      td.html(this.draft_timesheets_count);
    });
  });
});

En cada fila del listado clients, el td respectivo con una clase draft_timesheets_count es actualizado en su lugar con los valores desde la respuesta en JSON. No hay necesidad de refrescar la página, y la experiencia del usuario ha sido mejorada.

Como restriccion arquitectónica, esto requiere que este pedazo de JavaScript tenga conocimeinto íntimo de la estructura HTML de la página objetivo y de cómo transformar el JSON en cambios sobre el DOM. Esta es la principal razón por la cual JSON es el mejor formato para separar el nivel de presentación de su aplicación o, más importante, cuando la página requiere JSON de otra aplicación también.

Algunas veces, sin embargo, puede ser deseable para el servidor responder con un pedazo de HTML, usado para remplazar una región de la página objetivo.

##19.4 Ajax y HTML

Las clases Ruby en su aplicación Rails normalmente contendrán el volúmen de la lógica de la aplicación y el estado. Las aplicaciones caragadas de Ajax pueden reforzar esa lógica y estado al transferir HTML -en lugar de JSON- para manipular el DOM.

Una aplicación web puede responder a un requerimiento Ajax con un fragmento HTML, usado para insertar o reemplazar una parte existente de la página. Esto es hecho usualmente cuando la transformación descansa en reglas de negocios complejas y quizá estados complejos que pueden ser ineficientes de duplicar en JavaScript.

Digamos que su aplicación necesita desplegar clientes en algún tipo de orden de prioridad, y ese orden es altamente variable y dependiente del contexto actual. Podría haber un montón de reglas distando en que orden se debe mostrar. Quizá esto es que cuando quiera que un cliente tenga más de un cierto número de planillas de tiempo del proyecto, necesitamos señalar esto en la página.

$td.draft_timesheets_count
  - if client.timesheets.draft.count > 3
    %span.draft-overlimit WARNING!
    %br
  = client.timesheets.draft.count

Junto con eso, digamos en viernes o sábado, necesitamos agrupar clientes por sus día de gasto más activo así que podemos construirnos una plan de acción para el comienzo de la próxima semana.

Hay sólo dos reglas de negocios que, combinadas, son un poco de un puñado a implementar tanto en Rails como en JavaScript. Las aplicaciones tienden a tener más de sólo dos reglas combinadas, y esto se hace rápidamente prohibitivo implementar estas reglas en JavaScript para transforar JSON en cambios DOM. Esto es particularmente cierto cuando la página hace una llamada Ajax externa y no una que hayamos escrito nosotros.

Podemos optar por transferir HTML en la llamada Ajax y, usando JavaScript, actualizar la sección con ese HTML, Bajo un contexto, el fragmento de HTML retornado puede lucir como lo siguiente:

<tr id="client_22" class="client"></tr>
<tr>
  <td></td><td>Aardworkers</td><td>AARD</td><td>$4321</td>
  <td class="draft_timesheets_count">0</td>
</tr>
<tr id="client_23" class="client"></tr>
<tr>
  <td></td><td>Zorganization</td><td>ZORG</td><td>$9999</td>
  <td class="draft_timesheets_count">1</td>
</tr>

Mientras que en otro contexto, puede lucir así:

<tr>
  <td>Friday</td>
</tr>
<tr>
  <td>Saturday</td>
</tr>
<tr id="client_24" class="client"></tr>
<tr>
  <td></td><td>Hashrocket</td><td>HR</td><td>$12000</td>
  <td class="draft_timesheets_count">
    <span class="drafts-overlimit">WARNING!</span>
    5
    </td>
</tr>
<tr id="client_22" class="client"></tr>
<tr>
  <td></td><td>Aardworwers</td><td>AARD</td><td>$4321</td>
  <td class="draft_timesheets_count">0</td>
</tr>

El manejador de eventos de JavaScript para la respuesta Ajax entonces sólo necesita actualizar el innerHTML de un elemento HTML particular para alterar la página sin tener que conocer nada acerca de las reglas de negocio usadas para determinar que HTML resultante será.

##19.5 Ajax y JavaScript

La primea razón por la cual usted deseará trabajar con una respuesta JavaScript a un requerimiento Ajax es cuando sea para JSONP (JSON con padding). Los pads de JSON, o wraps, data JSON en una llamada a una función JavaScript que existe en su página. Usted especifica el nombre dela función en un parámetro string de consulta callback. Note que algunas APIs públicas pueden usar alguna distinta a callback, pero esto se está transformando en una convención en Rails y en muchas aplicaciones JSONP.

Aunque la entrada de Wikipedia de Ajax no mensiona específicamente a JSONP, y el requerimiento no es XHR según la definición de Rails, nos gusta pensar en esto como Ajax de todas formas -esto es, después de todo, JavaScript asíncrono.

JSONP es una técnica para obtener data inter dominio, eliminando la política del mismo origen de los browsers. Esto introduce muchos temas de seguridad y protección que están fuera de alcance de este libro. Sin embargo, si usted necesita usar JSONP, el stack de Rails provee una forma simple de manejar requerimientos JSONP (con Rack::JSONP) o haciendo requerimientos JSONP (con UJS y jQuery).

Para responder a requerimientos JSONP, active el módulo Rack JSONP desde la RubyGem rack-contrib en su archivo environment.rb:

class Application < Rails::Application
  require 'rack/contrib'
  config.middleware.use 'Rack::JSONP'
  ...

Entonces sólo use UJS para contarle a jQuery que esto es una llamada JSONP cambiando el data-type a jsonp:

= link_to 'Update draft timesheets count', counts_clients_path, remote: true,
      data: { type: :jsonp }, id: 'update_draft_timesheets'

jQuery automáticamente agregará el ?callback= y un nombre de función random al string de consult del URI del requerimiento. Adicionalmente a esto, también agregará los tags script necesarios para esquivar la política del mismo origen. Nuestro manejador de eventos existente es ligado a ajax:success, así éste es llamado con la data justo como antes. Ahora, sin embargo, puede recibir data de otra aplicación web.

jQuery también hace un requerimiento como si fuera para JavaScript, así nuestro controlador necesita a respond_to :js. Desafortunadamente, Rails automáticamente redereará para respuestas JavaScript que no están ahí todavía, así agregamos un manejador especial para JavaScript en su controlador:

respond_to :html, :js
...

def counts
  respond_with(Client.all_with_counts) do |format|
    format.html { redirect_to clients_path }
    format.js { render json: Client.all_with_counts.to_json }
  end
end

Aún convertimos nuestra data a JSON. El módulo Rack::JSONP entonces pads (padea) la data JSON en una llamada a la función JavaScript especificada en el string consulta del requerimiento. La respuesta luce así:

jsonp1234556789([{"id":1, "draft_timesheets_count":0},
{"id":2, "draft_timesheets_count":1}])

Cuando la respuesta Ajax está completa, su manejador de eventos Ajax es llamado y la data JSON es pasada a él como parámetro.

⚠️ **GitHub.com Fallback** ⚠️