Programació de la comunicació asíncrona amb jQuery

A banda de la gestió d’events i la manipulació del DOM, una de les funcionalitats més importants de jQuery és la gestió de peticions AJAX (Javascript asíncron i XML).

Una de les diferències és que no cal fer cap comprovació prèvia per discriminar entre les versions antigues del navegador Internet Explorer i els navegadors actuals, la biblioteca s’encarrega de fer-ho, i la gestió de la resposta se simplifica mitjançant mètodes propis en lloc d’haver d’afegir-hi la detecció d’events (que els navegadors antics potser no admeten).

Cal destacar que per poder utilitzar aquestes funcionalitats s’ha de carregar la biblioteca jQuery. Per exemple, a partir del codi local (suposem que es troba dins del directori /js/biblioteques i que el nom del fitxer és jquery-3.5.1.js):

  1. <script src="js/biblioteques/jquery-3.5.1.js"></script>

En cas d’utilitzar una xarxa de lliurament de continguts (en anglès, content delivery network, CDN) el codi seria el següent:

  1. <script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>

S’ha de tenir en compte que tant si s’utilitza la biblioteca com si es fa servir una implementació pròpia, els navegadors apliquen la política del mateix origen i, per consegüent, només es poden realitzar peticions al mateix domini en què es troba l’aplicació.

Aquesta limitació no s’aplica si el servei web al qual s’envia la petició admet CORS (peticions amb diferent origen) i, en cas de requerir-les, les capçaleres i les dades d’autenticació són correctes, o si admet JSONP (JSON amb padding) i es realitza la petició utilitzant aquesta tècnica.

A diferència de les implementacions pròpies, amb jQuery s’utilitza el mètode ajax() de l’objecte jQuery tant per a les peticions ordinàries com per a les peticions mitjançant JSONP.

Creació i enviament de peticions amb jQuery

Com que la biblioteca jQuery s’encarrega de gestionar les diferències entre versions, no cal comprovar si el navegador admet o no la utilització de l’objecte XMLHttpRequest o si es tracta d’una versió antiga d’Internet Explorer.

Només cal afegir la càrrega de la biblioteca (en el següent exemple mitjançant un CDN):

  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5. <meta charset="utf-8">
  6. <script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
  7. </head>
  8.  
  9. <body>
  10. </body>
  11.  
  12. </html>

Una vegada tingueu l’entorn preparat, només heu d’afegir les següents línies al final de l’element body per enviar la petició i mostrar el resultat per la consola:

  1. <script>
  2. $.ajax('https://api.idescat.cat/emex/v1/geo.xml?tipus=com', {
  3. success: processarResposta,
  4. error: processarError
  5. });
  6.  
  7. function processarResposta(dades, statusText, jqXHR) {
  8. console.log(dades, statusText);
  9. }
  10.  
  11. function processarError(jqXHR, statusText, error) {
  12. console.log(error, statusText);
  13. }
  14. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/zKVzwG?editors=0011.

Si en comptes de fer un projecte propi executeu l’exemple en codepen, per veure la resposta haureu d’anar a la consola del navegador, ja que la resposta és massa llarga per mostrar-la a la pròpia consola del codepen.

Com es pot apreciar, el codi és molt simple. S’invoca el mètode ajax() de l’objecte jQuery passant com a paràmetres l’URL on s’enviarà la petició i un objecte de JavaScript amb una propietat anomenada success, a la qual s’ha assignat la funció processarResposta(), i una altra propietat anomenada error, a la qual s’ha assignat la funció processarError().

Configuració de la funció 'ajax' de jQuery

Podeu consultar una llista completa d’opcions de configuració per la funció ajax() a l’enllaç següent: www.goo.gl/fDsgT5.

Aquest objecte conté les opcions que s’utilitzen per configurar la petició. En aquest cas se n’han vist dues:

  • success: s’executa si la petició s’ha realitzat correctament i rep la resposta, el text d’estat i l’objecte jqXHR creat per la petició.
  • error: s’executa quan s’ha produït un error. En aquest cas els paràmetres són l’objecte jqXHR, el text d’estat i l’error produït.

L’objecte jqXHR (una versió ampliada de l’objecte XMLHttpRequest dels navegadors) és retornat per la invocació del mètode ajax() i permet realitzar o encadenar altres accions a la invocació mitjançant una interfície anomenada objecte diferit (deferred object). Per aquesta raó, es passa també com a argument les funcions success i error, de manera que es pot obtenir més informació o realitzar altres operacions sobre la petició.

Objectes diferits

Podeu trobar més informació sobre els objectes diferits a l’enllaç següent: www.goo.gl/5oxmwa.

Tot i que la petició es realitza automàticament en invocar el mètode ajax(), cal recordar que (per defecte) és asíncrona i, per tant, la resposta no s’obté immediatament; l’aplicació continuarà executant-se sense bloquejar-se fins que es rebi la resposta, llavors s’executarà el mètode pertinent (assignat a success o error).

Alternativament, es pot invocar el mètode ajax() utilitzant, només, l’objecte amb les opcions com a paràmetre i assignant l’URL a la propietat url:

  1. $.ajax({
  2. url: 'https://api.idescat.cat/emex/v1/geo.xml?tipus=com',
  3. success: processarResposta,
  4. error: processarError
  5. });

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/Bprxvj?editors=1011.

Paràmetres

Habitualment en fer una petició AJAX s’han de passar un o més paràmetres al servidor. Aquests paràmetres són visibles a l’URL en el cas d’utilitzar el mètode GET per a l’enviament (el mètode per defecte).

Per exemple, la següent petició s’efectuaria sobre l’URL http://example.com i els paràmetres enviats són nom i cognom. Com que no s’especifica el mètode, s’utilitzarà GET. Fixeu-vos que en aquest cas s’afegiran a l’URL: http://example.com/?nom=Maria&Cognom=Campmany.

  1. $.ajax({
  2. url: 'http://example.com',
  3. data: { nom:"Maria", cognom:"Campmany"}
  4. });

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/jMgOpJ?editors=0010.

Obriu les eines de desenvolupadors i a la pestanya xarxa (network) trobareu una línia corresponent a aquesta petició. Quan hi feu clic, tindreu accés a tota la informació sobre la petició. Pareu especial atenció a la capçalera (header).

Podeu trobar més informació sobre les eines de desenvolupador a l’apartat “Comunicació asíncrona amb JavaScript” d’aquesta mateixa unitat.

El símbol ? indica que el que hi ha a continuació són els paràmetres de la consulta, que estarà formada per parells de clau i valor separats pel signe igual. Aquests parells, al seu torn, són separats d’altres paràmetres pel símbol &.

Així doncs, analitzant l’URL, es pot dividir en diferents parts:

  • http: correspon al protocol HTTP.
  • example.com: correspon al domini al qual s’envia la petició.
  • /: correspon a la ruta, que en aquest cas és l’arrel del domini.
  • nom=Maria: primer paràmetre, corresponent a la clau nom amb valor Maria.
  • cognom=Campmany: segon paràmetre, corresponent a la clau cognom amb valor Campmany.

Una alternativa a fer servir un objecte per passar els paràmetres és fer servir una cadena de text amb els parells clau-valor separats pel signe igual i aquests separats d’altres parells pel símbol &:

  1. $.ajax({
  2. url: 'http://example.com',
  3. data: 'nom=Maria&cognom=Campmany'
  4. });

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/MJVGMr?editors=0010.

Quan es fa servir un altre mètode, com POST o PUT, aquesta informació només és visible a la capçalera. Cal destacar que això no és cap garantia de privadesa ni seguretat, ja que aquesta capçalera pot ser inspeccionada per qualsevol node de la xarxa per on passi la informació.

  1. $.ajax({
  2. url: 'http://example.com',
  3. method: 'post',
  4. data: { nom:"Maria", cognom:"Campmany"}
  5. });

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/PGMovd?editors=0010.

En aquest cas, en analitzar la petició amb les eines de desenvolupador, podeu comprovar que l’URL no s’ha modificat i aquests paràmetres s’han afegit a la capçalera com a dades de formulari.

Cal destacar que la biblioteca jQuery permet assignar la propietat data de les opcions com a cadena de text per a tots els mètodes i no només per al mètode GET, ja que jQuery fa les conversions necessàries. Així doncs, és possible fer servir com a data la cadena de text nom=Maria&cognom=Campmany i en enviar la petició aquests paràmetres s’afegeixen com a dades a la capçalera:

  1. $.ajax({
  2. url: 'http://example.com',
  3. method: 'post',
  4. data: 'nom=Maria&cognom=Campmany'
  5. });

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/ORKPJa?editors=0010.

Tipus de dades: dataType

Si no s’especifica una altra cosa, s’espera que el format de la resposta sigui XML vàlid. És a dir, si proveu de fer una petició demanant un fitxer de text pla amb el contingut que trobareu a continuació, es produirà un error:

  1. Barcelona
  2. Girona
  3. Lleida
  4. Tarragona

Com que no s’ha especificat el tipus de dades, es produeix un error: el format del fitxer no és vàlid. Així doncs, cal especificar el tipus de dades que s’espera rebre del servidor assignant la propietat dataType a les opcions:

  1. $.ajax({
  2. url: 'https://api.idescat.cat/pob/v1/sug.txt?p=q/ab',
  3. dataType: 'text',
  4. success: processarResposta,
  5. error: processarError
  6. });

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/MjMvoJ?editors=1011.

Els tipus de dades admesos per les peticions AJAX de jQuery són:

  • xml (tipus per defecte): retorna un document XML que pot ser processat per jQuery.
  • html: retorna el codi HTML com a text pla.
  • script: avalua la resposta com a JavaScript.
  • json: avalua la resposta com a JSON i retorna un objecte de JavaScript.
  • jsonp: utilitzat per obtenir les dades d’un altre domini fent servir la tècnica JSONP, retorna un objecte de JavaScript amb les dades.
  • text: resposta com a text pla.

En cas de carregar fitxers, com en aquests exemples, o si el servidor no especifica el tipus multimèdia de la resposta, s’ha de sobreescriure mitjançant el mètode overrideMimeType() de l’objecte jqXHR.

Per assegurar que el tipus de contingut multimèdia és correcte es pot utilitzar l’opció beforeSend, que permet especificar una funció que serà invocada abans de realitzar la petició. Aquesta funció rep com a paràmetre l’objecte jqXHR i sobre aquest es pot invocar el mètode overrideMimeType() per forçar el tipus de la resposta:

  1. $.ajax({
  2. url: 'https://api.idescat.cat/pob/v1/sug.txt?p=q/ab',
  3. dataType: 'text',
  4. beforeSend: function (jqXHR) {
  5. jqXHR.overrideMimeType('text/plain');
  6. },
  7. success: processarResposta,
  8. error: processarError
  9. });
  10.  

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/ozremJ?editors=1011.

Cal destacar que el més habitual és que el servidor web retorni el tipus multimèdia correcte i no s’hagi de sobreescriure.

Com es pot apreciar, un altre dels tipus admesos és HTML.

Suposem que s’ha d’afegir dinàmicament a una pàgina el codi HTML següent (podria ser la resposta enviada pel servidor, per exemple):

  1. <html>
  2. <h1>Provincies</h1>
  3. <ul>
  4. <li>Barcelona</li>
  5. <li>Girona</li>
  6. <li>Lleida</li>
  7. <li>Tarragona</li>
  8. </ul>
  9. </html>

Per fer-ho, només caldria especificar el tipus com html i afegir-lo utilitzant el mètode append() de l’objecte jQuery:

  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5. <meta charset="utf-8">
  6. <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
  7. </head>
  8.  
  9. <body>
  10. <script>
  11. $.ajax({
  12. url: 'provincies.html',
  13. dataType: 'html',
  14. success: processarResposta,
  15. error: processarError
  16. });
  17.  
  18. function processarResposta(dades, statusText, jqXHR) {
  19. $dades = $(dades);
  20. $('body').append($dades);
  21.  
  22. }
  23.  
  24. function processarError(jqXHR, statusText, error) {
  25. console.log(error, statusText);
  26. }
  27. </script>
  28. </body>
  29.  
  30. </html>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/XjLaVe?editors=1011.

Errors habituals quan no s'especifica el tipus de dades

Habitualment es produeixen errors si no s’especifica el tipus de dades mitjançant la propietat dataType, ja que a l’hora de processar la resposta la biblioteca jQuery intentarà interpretar-lo com XML i si no ho aconsegueix llença una excepció.

En aquest cas, com que el codi HTML utilitzat té el mateix format que XML (que és el valor per defecte de dataType) es considera correcte i no cal sobreescriure el tipus multimèdia.

En canvi, si la resposta fos en format JSON, caldria canviar el dataType per JSON.

El codi JavaScript seria el següent:

  1. $.ajax({
  2. url: 'https://api.idescat.cat/pob/v1/geo.json?q=cabell',
  3. dataType: 'json',
  4. success: processarResposta,
  5. error: processarError
  6. });
  7.  
  8. function processarResposta(dades, statusText, jqXHR) {
  9. console.log(dades, statusText);
  10. }
  11.  
  12. function processarError(jqXHR, statusText, error) {
  13. console.log(error, statusText);
  14. }

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/wzLqOM?editors=1011.

Com es pot apreciar, en aquest cas la resposta és un objecte JavaScript que es pot processar de la forma habitual.

Mètodes d'enviament

De la mateixa manera que a les implementacions pròpies d’AJAX, la biblioteca jQuery permet especificar el mètode amb el qual s’envien les peticions. Només cal afegir, a l’objecte amb les opcions, la propietat method i assignar-li el mètode pertinent: GET (per defecte), POST, PUT o DELETE. Per exemple, es pot especificar el mètode GET per llegir un fitxer JSON:

  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5. <meta charset="utf-8">
  6. <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
  7. </head>
  8.  
  9. <body>
  10. <script>
  11. $.ajax({
  12. method: 'get',
  13. url: 'https://api.idescat.cat/pob/v1/geo.json?q=cabell',
  14. dataType: 'json',
  15. success: processarResposta,
  16. error: processarError
  17. });
  18.  
  19. function processarResposta(dades, statusText, jqXHR) {
  20. console.log(dades, statusText);
  21. }
  22.  
  23. function processarError(jqXHR, statusText, error) {
  24. console.log(error, statusText);
  25. }
  26. </script>
  27. </body>
  28.  
  29. </html>

Podeu veure aquest exemple en el següent enllaç: codepen.io/ioc-daw-m06/pen/wzLQOR?editors=1011.

Cal recordar que aquests mètodes es corresponen amb els del protocol HTTP 1.1, i als serveis web REST són utilitzats sobre un mateix URL per realitzar diferents accions:

  • GET: consultar les dades.
  • POST: afegir noves dades.
  • PATCH: actualitzar dades ja existents.
  • PUT: reemplaçar dades ja existents.
  • DELETE: eliminar dades.

A més a més, jQuery ofereix dues dreceres per simplificar l’enviament de peticions a través dels mètodes GET i POST. Tots dos mètodes s’invoquen a partir de l’objecte jQuery:

'get' i 'post'

Podeu trobar totes les opcions dels mètodes $.get() i $.post() als següents enllaços: www.goo.gl/DVzxg8 i www.goo.gl/zRbp5A; respectivament.

  • $.get(): envia una petició AJAX fent servir el mètode GET.
  • $.post(): envia una petició AJAX fent servir el mètode POST.

Aquests mètodes ofereixen un nombre més limitat d’opcions, però suficient en la major part de les situacions:

  • url: una cadena amb l’URL al qual s’envia la petició.
  • data: un objecte o cadena de text que s’enviaran com a paràmetres de la petició.
  • sucess: funció de tipus callback que serà cridada si la petició es realitza amb èxit.
  • dataType: el tipus de dades esperat pel servidor (xml, json, script, text i html).

Aquestes opcions es poden passar com a paràmetres individuals:

  1. $.get(url, data, success, dataType);

O com un objecte en el qual totes les propietats, excepte url, són opcionals:

  1. $.get({
  2. url: 'provincies.html',
  3. data: {nom: 'Josep', cognoms: 'Torres Blanc'},
  4. success: processarResposta},
  5. dataType: 'html'
  6. });

Així doncs, per fer una petició amb el mètode GET i obtenir una resposta en HTML el codi complet seria el següent:

  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5. <meta charset="utf-8">
  6. <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
  7. </head>
  8.  
  9. <body>
  10. <script>
  11. $.get({
  12. url: 'https://api.geekdo.com/xmlapi2/search',
  13. data: {query: 'dixit', type: 'boardgame'},
  14. success: processarResposta,
  15. dataType: 'xml'
  16. });
  17.  
  18. function processarResposta(dades, statusText, jqXHR) {
  19. console.log(dades);
  20. }
  21. </script>
  22. </body>
  23.  
  24. </html>

Podeu veure aquest exemple en el següent enllaç: codepen.io/ioc-daw-m06/pen/RGzvgP?editors=1011.

Com es pot apreciar, aquest exemple fa una petició a una API de jocs, en què diem que necessitem les dades de jocs amb un nom i un tipus determinat. En tractar-se del mètode get() s’ha convertit l’URL de la petició en quelcom similar al següent: https://api.geekdo.com/xmlapi2/search?query=dixit&type=boardgame. Tingueu en compte que en aquest exemple també farà falta comprovar les dades retornades en la consola del navegador i no en la consola de codepen.

És a dir, jQuery ha convertit l’objecte passat com a argument en paràmetres vàlids per enviar com a petició, fent servir el nom de les propietats com a clau i el valor assignat com a valor del paràmetre.

S’ha de tenir en compte que, tal com passa amb el mètode ajax(), si l’URL de la petició no correspon a un servidor que envia una resposta amb un tipus especificat, l’aplicació esperarà que es tracti d’una resposta XML i donarà error. En el cas de les dreceres no existeix l’opció beforeSend i, per consegüent, no es pot sobreescriure el tipus multimèdia de la resposta.

Interpretar la resposta

Segons el tipus de data (dataType) de la resposta i si aquesta és correcta, la resposta serà retornada de diferents maneres:

  • XML: un objecte XMLDocument.
  • HTML, TEXT: una cadena de text amb el contingut.
  • JSON: un objecte de JavaScript.
  • Script: un bloc de codi JavaScript que serà avaluat (executat).

XMLDocument és un tipus d’objecte que és herència de la interfície del DOM document i no afegeixen cap altra propietat o mètode nous. Això permet utilitzar jQuery per manipular aquest document:

  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5. <meta charset="utf-8">
  6. <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
  7. </head>
  8.  
  9. <body>
  10. <script>
  11. $.get({
  12. url: 'https://api.geekdo.com/xmlapi2/search',
  13. data: {query: 'dixit', type: 'boardgame'},
  14. success: processarResposta
  15. });
  16.  
  17. function processarResposta(dadesXML, statusText, jqXHR) {
  18. let $jocs = $(dadesXML).find('name');
  19. let $llista = $('<ul>');
  20.  
  21. $jocs.each(function() {
  22. let nomJoc = $(this).attr('value');
  23. $joc = $('<li>');
  24. $joc.text(nomJoc);
  25. $llista.append($joc);
  26. })
  27.  
  28. $('body').append($llista);
  29. }
  30. </script>
  31. </body>
  32.  
  33. </html>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/MjMLRP?editors=1010.

Una vegada s’ha obtingut la resposta, es converteix en un objecte jQuery i se seleccionen tots els elements amb el nom name. A continuació, es crea una nova llista sense numeració (<ul>) i es procedeix a recórrer-los fent servir el mètode each(), que invoca la funció passada com a argument amb l’element corresponent com a context. És a dir, l’element és referenciat per this (en aquest cas, cadascun dels elements de jocs).

De cadascun d’aquests elements s’obté el nom del joc a partir del contingut intern de l’atribut value de l’element mitjançant el mètode attr(). Seguidament es crea un nou element de tipus element de llista (<li>) i s’assigna com a contingut d’aquest element el nom del joc que, finalment, és afegit a la llista que, al seu torn, és afegida al cos de la pàgina quan es finalitza el recorregut.

Com que XML es el tipus per defecte, no cal especificar-lo quan es treballa amb aquest format.

En el cas de HTML es pot utilitzar la funció jQuery() per convertir aquest text en un objecte jQuery que, al seu torn, converteix la cadena de text en elements del DOM: $branca = $(dades). Aquesta nova branca es pot afegir a altres elements: $(‘body’).append($branca).

  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5. <meta charset="utf-8">
  6. <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
  7. </head>
  8.  
  9. <body>
  10. <script>
  11. $.ajax({
  12. url: 'provincies.html',
  13. beforeSend: function (jqXHR) {
  14. jqXHR.overrideMimeType('text/html');
  15. },
  16. success: processarResposta,
  17. dataType: 'html'
  18. });
  19.  
  20. function processarResposta(dadesHTML, statusText, jqXHR) {
  21. let $branca = $(dadesHTML);
  22. $('body').append($branca);
  23. }
  24. </script>
  25. </body>
  26.  
  27. </html>

Podeu veure aquest exemple en lenllaç següent: codepen.io/ioc-daw-m06/pen/EgBMXj?editors=1010.

Com es pot apreciar, s’ha fet servir el mètode ajax() per incloure l’opció beforeSend i canviar el tipus multimèdia a text/html. D’aquesta manera s’eviten possibles errors de format, ja que el sistema de fitxers no especifica el tipus de contingut.

Per analitzar un fitxer de tipus textual s’han de fer servir les funcions de tractament de cadenes, com per exemple split(), o expressions regulars. Per exemple, es podria processar la resposta dividint la cadena per la coma, mitjançant el mètode split():

  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5. <meta charset="utf-8">
  6. <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
  7. </head>
  8.  
  9. <body>
  10. <script>
  11. $.ajax({
  12. url: 'https://api.idescat.cat/pob/v1/geo.txt?sug=por',
  13. success: processarResposta,
  14. dataType: 'text'
  15. });
  16.  
  17. function processarResposta(dadesText, statusText, jqXHR) {
  18. let municipis = dadesText.split('\n');
  19. let $llista = $('<ul>');
  20.  
  21. for (let i = 0; i < municipis.length; i++) {
  22. let $item = $('<li>');
  23. $item.html(municipis[i]);
  24. $llista.append($item);
  25. }
  26.  
  27. $('body').append($llista);
  28. }
  29. </script>
  30. </body>
  31.  
  32. </html>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/ZpdPME?editors=1010.

En el cas que la resposta sigui un objecte JavaScript és molt fàcil treballar-hi, ja que es pot accedir directament a tota la informació fent servir la notació de claudàtors (provincia[‘nom]) o de punt (provincia.nom).

  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5. <meta charset="utf-8">
  6. <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
  7. </head>
  8.  
  9. <body>
  10. <script>
  11. $.ajax({
  12. url: 'https://api.idescat.cat/pob/v1/geo.json?q=por',
  13. success: processarResposta,
  14. dataType: 'json'
  15. });
  16.  
  17. function processarResposta(dadesJSON, statusText, jqXHR) {
  18. let municipis = dadesJSON.feed.entry;
  19. let $llista = $('<ul>');
  20.  
  21. for (let i = 0; i < municipis.length; i++) {
  22. let $item = $('<li>');
  23. $item.html(municipis[i].title);
  24. $llista.append($item);
  25. }
  26.  
  27. $('body').append($llista);
  28. }
  29.  
  30. </script>
  31. </body>
  32.  
  33. </html>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/PGrLVd?editors=1010.

Per acabar, veurem un exemple amb JSONP:

  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5. <meta charset="utf-8">
  6. <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
  7. </head>
  8.  
  9. <body>
  10. <script>
  11. $.ajax({
  12. url: 'https://get.geojs.io/v1/ip/geo/8.8.8.8.js',
  13. beforeSend: jqXHR => jqXHR.overrideMimeType('application/javascript'),
  14. success: processarResposta,
  15. dataType: 'jsonp'
  16. });
  17.  
  18. function processarResposta(dadesJS, statusText, jqXHR) {
  19. console.log("Dades rebudes:", dadesJS);
  20. }
  21. </script>
  22. </body>
  23.  
  24. </html>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/pRLKZO?editors=1011.

Events AJAX

Durant l’enviament d’una petició AJAX mitjançant la biblioteca jQuery es disparen tot un seguit d’events. Aquests events poden ser locals (propis d’una petició concreta) o globals (disparats al document).

L’ordre en què es disparen aquests events és el següent:

  • ajaxStart (global): es dispara quan comença la petició i no hi ha cap altra petició en execució.
  • beforeSend (local): es dispara abans de realitzar-se l’enviament i permet modificar la petició abans d’enviar-la (per exemple, per sobreescriure el tipus multimèdia).
  • ajaxSend (global): disparat globalment quan es realitza l’enviament.
  • success (local): es dispara només si la petició retorna amb una resposta exitosa.
  • ajaxSuccess (global): disparat en les mateixes condicions que l’anterior, però globalment.
  • error (local): disparat només quan es produeix un error.
  • ajaxError (global): igual que l’anterior però disparat globalment.
  • complete (local): es dispara en finalitzar la petició independentment de si ha estat exitosa o errònia.
  • ajaxComplete (global): igual que l’anterior però disparat globalment.
  • ajaxStop (global): es dispara quan no hi ha més peticions AJAX processant-se.

Fixeu-vos que els events locals es corresponen amb les opcions a les quals es pot afegir una funció de tipus callback: beforeSend, success, error i complete.

Per altra banda, a excepció de l’event ajaxStart i ajaxStop, la resta d’events globals són rèpliques dels events locals i es disparen a continuació d’aquests.

Cal destacar que per detectar quan es dispara un event global s’ha d’especificar la detecció al document, tal com es mostra en l’exemple següent:

  1. $(document).ajaxSend(() => console.log("S'ha enviat una petició AJAX"));

Per contra, la detecció d’event local s’especifica a la petició concreta que hi està lligada, com es pot apreciar en l’exemple següent:

  1. $.ajax({
  2. url: 'provincies.xml',
  3. beforeSend: jqXHR => console.log("Disparat abans d'enviar una petició concreta");
  4. });

Fixeu-vos que en el primer cas el missatge es mostraria a la consola quan s’enviés qualsevol petició AJAX, mentre que en el segon cas només es mostraria quan s’enviés la petició concreta on s’ha especificat.

Altres opcions del mètode Ajax

El mètode ajax() ofereix múltiples opcions per crear una petició, a banda dels més utilitzats com method, url i data, podeu trobar els següents, que permeten crear peticions més avançades afegint dades d’autenticació o modificant les capçaleres per adequar-les als requeriments dels serveis web.

  • async: per defecte el seu valor és true; si s’estableix com a false, la petició serà síncrona.
  • contentType: permet canviar el tipus de contingut enviat en les peticions a altres dominis; si és diferent d‘application/x-www-form-urlencoded, multipart/form-data o text/plain s’activaran mesures de comprovació extres al navegador.
  • crossDomain: permet forçar l’enviament de la petició com si es tractés d’un altre domini, tot i que el destí sigui el mateix en què es troba l’aplicació.
  • headers: aquesta propietat permet afegir parelles de claus i valors addicionals a la capçalera de la petició. Per exemple, és possible que alguns serveis web requereixin que a la capçalera es trobi un token d’autenticació.
  • password: contrasenya que serà utilitzada en una petició HTTP d’autenticació.
  • username: nom d’usuari que serà utilitzat en una petició HTTP d’autenticació.

Addicionalment, tot i que no es recomana fer-lo servir, jQuery ofereix el mètode ajaxSetup(), que permet establir uns valors per defecte que seran utilitzats per totes les peticions realitzades pel mètode ajax() i els seus derivats, com els mètodes get() i post().

Així, per exemple, el següent codi canviaria el mètode d’enviament per defecte (GET) per POST, cosa que podria fer que altres peticions de l’aplicació deixessin de funcionar o tinguessin efectes no desitjats.

  1. $.ajaxSetup({
  2. method: 'post'
  3. });

JSONP i CORS amb jQuery

Sovint és necessari l’accés a dades en serveis webs amb origen diferent. Per poder accedir a aquestes dades depenem del servidor, que ha d’oferir les dades o bé en format JSONP (JSON amb padding) o implementar mecàniques per habilitar el CORS (Cross-Origin Resource Sharing).

En tots dos casos jQuery utilitza la mateixa interfície, mitjançant el mètode AJAX, de manera que les tècniques emprades són pràcticament transparents a l’usuari.

Aquesta estandardització es veu molt clarament en la implementació de les peticions amb JSONP, ja que en lloc d’afegir una etiqueta script amb l’origen de les dades i passant els paràmetres com una cadena de text, es pot crear una petició AJAX amb les opcions corresponents. Fixeu-vos en una aplicació que consulta l’agregador (feed) de l’aplicació flickr enviant com a paràmetres l’etiqueta kittens i el format json.

API de Flickr

Podeu trobar tota la informació per desenvolupadors de l’API de Flickr a l’enllaç següent: www.goo.gl/tAVTBD.

  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5. <meta charset="utf-8">
  6. </head>
  7.  
  8. <body>
  9. <script>
  10. $.ajax({
  11. url: 'http://api.flickr.com/services/feeds/photos_public.gne',
  12. success: processarResposta,
  13. dataType: "jsonp",
  14.  
  15. // Nom del paràmetre que indica al servidor el nom de la funció de callback, definit a la seva documentació
  16. jsonp: "jsoncallback",
  17.  
  18. // Paràmetres que es passen al servidor, definits a la seva documentació
  19. data: {
  20. tags: 'kitten',
  21. format: 'json'
  22. }
  23. });
  24.  
  25. function processarResposta(dades) {
  26. let imatges = dades.items;
  27. console.log(dades);
  28. for (let i = 0; i < imatges.length; i++) {
  29. let $img = $('<img>');
  30. $img.attr('src', imatges[i].media.m);
  31. $img.attr('title', imatges[i].title);
  32. $img.attr('alt', imatges[i].title);
  33. $('body').append($img);
  34. }
  35. };
  36. </script>
  37. </body>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/ExgRdBN?editors=0011.

Com es pot apreciar, només es diferencia d’una crida AJAX estàndard en el fet que s’ha especificat l’opció jsonp per indicar el nom del paràmetre corresponent a la funció callback, que es farà servir com a padding a la resposta, i l’opció dataType a la qual s’ha assignat el valor jsonp.

Quant a les crides a serveis web que admetin CORS, com també en les implementacions pròpies d’AJAX, no canvia res quan el servidor inclou a la capçalera el paràmetre Access-Control-Allow-Origin i el seu valor es correspon amb el del domini on s’executa l’aplicació o el seu valor és * i, per consegüent, accessible per a tothom.

Vegeu en l’exemple següent com es consulta una font de dades obertes de l’Ajuntament de Barcelona per recuperar la llista de festivals de l’any 2019 en format JSON:

  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5. <meta charset="utf-8">
  6. <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
  7. </head>
  8.  
  9. <body>
  10. <h1>Festivals 2019</h1>
  11.  
  12. <script>
  13.  
  14. $.ajax({
  15. url: 'https://dades.eicub.net/api/1/festivals-assistents',
  16. success: processarResposta,
  17. dataType: 'json',
  18. data: {
  19. format : 'json',
  20. Any : 2019,
  21. }
  22. });
  23.  
  24. function processarResposta(dades, statusText, jqXHR) {
  25. let $llista = $('<ul>');
  26.  
  27. for (let i=0; i<dades.length; i++) {
  28. let $dada = processarDada(dades[i]);
  29. $llista.append($dada)
  30. }
  31.  
  32. $('body').append($llista);
  33. }
  34.  
  35. function processarDada(dada) {
  36. let $item = $('<li>');
  37. let $enllac = $('<a>');
  38. $enllac.html(dada.Nom_activitat);
  39. $enllac.attr('href', dada.Web);
  40. $enllac.attr('title', dada.Organitzador);
  41. $item.append($enllac);
  42.  
  43. return $item;
  44. }
  45.  
  46. </script>
  47. </body>
  48. </html>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/RGXbBr?editors=1010.

En cas que el servei web requereixi autenticació o altres paràmetres, a les opcions que es passen al mètode ajax() es pot especificar la propietat xhrFields per incloure-les en la petició (per exemple, per afegir l’ús de credencials si el servidor les demana), i es faria de la manera següent:

  1. $.ajax({
  2. url: url_en_un_domini_diferent,
  3. xhrFields: {
  4. withCredentials: true
  5. }
  6. });

Com que les dades addicionals que pot requerir un servei web que implementi les mecàniques per habilitar CORS poden ser molt diferents, cal consultar-ne la documentació per poder configurar correctament les peticions.

Accés a dades obertes amb jQuery

L’accés a dades obertes com les que ofereix l’Observatori de dades culturals de Barcelona (www.barcelonadadescultura.bcn.cat/dades-obertes) es realitza de la mateixa manera que altres peticions AJAX mitjançant jQuery, amb l’avantatge que en cas que sigui necessari enviar paràmetres, la tasca se simplifica, ja que no cal modificar la informació de la capçalera i es tracta igual si el mètode d’enviament és GET o qualsevol altre.

Vegeu a continuació un exemple d’una consulta que carrega les dades de les “activitats de difusió de les fàbriques de creació” i permet seleccionar, segons el seu identificador, de quina activitat es volen visualitzar les dades.

  1. <!DOCTYPE html>
  2. <html>
  3.  
  4. <head>
  5. <meta charset="utf-8">
  6. <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
  7.  
  8. <style>
  9. h1 {
  10. text-align:center;
  11. }
  12.  
  13. fieldset {
  14. width: 300px;
  15. margin: 0 auto;
  16. }
  17. label {
  18. display: inline-block;
  19. width: 150px;
  20. font-weight: bold;
  21. }
  22. span {
  23. display: inline-block;
  24. width: 150px;
  25. }
  26. ul {
  27. list-style-type: none;
  28. padding: 0;
  29. }
  30. </style>
  31.  
  32. </head>
  33.  
  34. <body>
  35. <h1>Activitats de difusió de les fàbriques de creació</h1>
  36. <fieldset>
  37. <select id="identificador">
  38. <option>Selecciona un identificador</option></select>
  39. <ul>
  40. <li><label>Any:</label><span id="any"></span></li>
  41. <li><label>Equipament:</label><span id="equipament"></span></li>
  42. <li><label>Districte:</label><span id="districte"></span></li>
  43. <li><label>Ambit:</label><span id="ambit"></span></li>
  44. <li><label>Assistents:</label><span id="asistents"></span></li>
  45. <li><label>Notes:</label><span id="notes"></span></li>
  46. <li><label>Tipus d'equipament:</label><span id="tipusEquipament"></span></li>
  47.  
  48. <li><label>Titularitat:</label><span id="titularitat"></span></li>
  49. <li><label>Activitats de difusió dins dels centres:</label><span id="activitats"></span></li>
  50. </ul>
  51. </fieldset>
  52.  
  53. <script>
  54. let dades = {};
  55.  
  56. $.ajax({
  57. url: 'https://dades.eicub.net/api/1/fabriquescreacio-difusio',
  58. success: processarResposta,
  59. dataType: 'json'
  60. });
  61.  
  62. function processarResposta(resposta, statusText, jqXHR) {
  63. let $llistaDesplegable = $('#identificador');
  64.  
  65. for (let i = 0; i < resposta.length; i++) {
  66. let $item = processarDada(i);
  67. dades[i] = resposta[i];
  68. console.log(resposta[i]);
  69. $llistaDesplegable.append($item);
  70. }
  71.  
  72. $llistaDesplegable.on('change', actualitzarDadesMostrades);
  73. }
  74.  
  75. function processarDada(num) {
  76. let $item = $('<option>');
  77. $item.attr('value', num);
  78. $item.text(num);
  79. return $item;
  80. }
  81.  
  82. function actualitzarDadesMostrades(event) {
  83. let $llistaDesplegable = $('#identificador');
  84. let dada = dades[$llistaDesplegable.val()]
  85.  
  86. actualitzarDadaMostrada('any', dada.Any);
  87. actualitzarDadaMostrada('equipament', dada.Equipament);
  88. actualitzarDadaMostrada('districte', dada.Nom_Districte);
  89. actualitzarDadaMostrada('ambit', dada.Ambit);
  90. actualitzarDadaMostrada('asistents', dada.Assistents);
  91. actualitzarDadaMostrada('notes', dada.Notes_Dades || 'No hi ha cap nota');
  92. actualitzarDadaMostrada('tipusEquipament', dada.TipusEquipament);
  93. actualitzarDadaMostrada('titularitat', dada.Titularitat);
  94. actualitzarDadaMostrada('activitats', dada.Activitats_de_difusio_dins_dels_centres);
  95.  
  96. }
  97.  
  98. function actualitzarDadaMostrada(nom, valor) {
  99. $('#' + nom).html(valor);
  100. }
  101.  
  102. </script>
  103. </body>
  104.  
  105. </html>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/bwXGLx?editors=1111.

Com es pot apreciar, en aquest cas no s’han especificat paràmetres perquè la font de dades no els requeria. En els casos que és necessari només cal especificar a l’objecte d’opcions la propietat data i assignar els paràmetres necessaris, o en forma de cadena de text o d’objecte, i la biblioteca s’encarregarà d’afegir-los a la capçalera de la forma correcta segons el mètode emprat.

Mètodes d'ajuda: serialize, serializeArray i param

La biblioteca jQuery ofereix un seguit de mètodes per facilitar l’obtenció i manipulació de dades a l’hora de realitzar peticions, entre els quals cal destacar serialize(), serializeArray() i params().

El mètode serialize() converteix la informació dels controls d’un objecte jQuery que encapsula un formulari en una cadena de text codificada per fer servir com a paràmetre. Aquesta cadena estarà formada pels controls que disposin de la propietat name i el seu valor.

  1. <form>
  2. <input name="nom" />
  3. <input name="cognom" />
  4. </form>
  5.  
  6. <div></div>
  7.  
  8. <script>
  9. $('input').on('input change', () => {
  10. let serialitzat = $('form').serialize();
  11. $('div').html(serialitzat);
  12. });
  13. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/vXoExz?editors=1010.

Com es pot apreciar, això permet establir l’opció data d’una petició AJAX directament a partir d’aquesta cadena, ja que inclou tota la informació del formulari sense haver de recórrer els elements i extreure’n les dades una per una.

De forma similar, el mètode serializeArray() també obté la informació del formulari, però en aquest cas genera un array d’objectes JavaScript amb tota la informació del formulari.

  1. <form>
  2. <input name="nom" value="Maria"/>
  3. <input name="cognom" value="Campmany"/>
  4. </form>
  5. <button>Comprovar</button>
  6.  
  7. <script>
  8. $('button').on('click', () => {
  9. let serialitzat = $('form').serializeArray();
  10. console.log(serialitzat);
  11. });
  12. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/wgbobe?editors=1011.

L’array d’objectes generats per la invocació del mètode serializeArray() seria el següent:

  1. [
  2. Object {
  3. name: "nom",
  4. value: "Maria"
  5. },
  6. Object {
  7. name: "cognom",
  8. value: "Campmany"
  9. }
  10. ]

Fixeu-vos que aquest objecte es pot utilitzar directament com a valor assignat a l’opció data: aquesta opció accepta tant objectes de JavaScript (sense mètodes) com cadenes de text.

Mentre que serialize() i serializeArray() faciliten la tasca de convertir les dades introduïdes als controls d’un formulari per fer-les servir com a dades en una petició AJAX, el mètode param() permet convertir objectes de JavaScript, objectes de jQuery i arrays en una cadena de consulta codificada:

Cadena de consulta és una mena de cadena de text amb un format especial utilitzat per enviar peticions i que, inclús, pot portar paràmetres.

  1. let dades = [{
  2. name: 'nom',
  3. value: 'Maria'
  4. }, {
  5. name: 'cognom',
  6. value: 'Campmany'
  7. }, ]
  8.  
  9. console.log($.param(dades));

Podeu veure aquest exemple en el següent enllaç: codepen.io/ioc-daw-m06/pen/xEvbpp?editors=0011.

La cadena de consulta obtinguda en invocar el mètode param() serà: nom=Maria&cognom=Campmany.

Fixeu-vos que mentre que serialize() i serializeArray() són mètodes que es criden sobre una instància d’objecte jQuery que conté la referència al formulari, el mètode param() s’invoca directament a partir de la funció jQuery() passant les dades que s’han de convertir com a argument.

S’ha de tenir en compte que en el cas dels objectes JavaScript, han de tenir forçosament el format següent:

  1. [
  2. {
  3. name: nom_del_parametre
  4. value: valor_corresponent
  5. }
  6. ]

Els objectes jQuery han de contenir referències a objectes de tipus input en què els controls tinguin definit l’atribut name (com en el cas de serialize() i serializeArray()), en cas contrari no funcionarà correctament.

Anar a la pàgina anterior:
Exercicis d'autoavaluació
Anar a la pàgina següent:
Activitats