How to AJAX - maciejjankowski/2018 GitHub Wiki
- odpal konsolę Javascriptu w Firefox | Chrome
- przeklej kawałek kodu i sprawdź jak działa
- pobaw się, poeksperymentuj
AJAX jest zbiorczą nazwą mechanizmu komunikowania się z serwerem aplikacji za pomocą Javascriptu, bez konieczności przeładowania całej strony wikipedia.
- Strona ładuje tabelę z danymi
- Po kliknięciu przycisku "pokaż więcej" do serwera zostaje przesłane żądanie GET zwracające kolejne rekordy w formacie JSON
- Po otrzymaniu odpowiedzi z rekordami z serwera (zazwyczaj w formacie JSON lub XML) rekordy są dodawane do istniejącej tabeli
var request = new XMLHttpRequest();
request.open('GET', '/my/url', true);
request.onload = function _ajaxOnLoadCallback() {
if (request.status >= 200 && request.status < 400) {
// Success!
var resp = request.responseText;
console.log("received text", resp)
} else {
// We reached our target server, but it returned an error
}
};
request.onerror = function _ajaxOnErrorCallback() {
console.log('ALWAYS WRITE ERROR HANDLING!');
};
request.send();
var request = new XMLHttpRequest();
request.open('POST', '/my/url', true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.send(data);
W HTTP dane przesyłane i odbierane są zwykłym tekstem, więc jeśli mamy jakąś bardziej skomplikowaną strukturę danych (np. słownik) to musimy ją jakoś zamienić (zserializować) na zwykły tekst. Można to zrobić na kilka sposobów, np. urlencode, JSON.
JSON:
{ "dupa" : "ja sia", "gruba" : "kasia" }
URLEncode:
dupa=ja%20sia&gruba=kasia
Kodowanie / dekodowanie odbywa się za pomocą metod:
const obiekt_do_zakodowania = { "dupa" : "ja sia", "gruba" : "kasia" };
const tekst_zakodowany = JSON.stringify()
Jeśli popatrzysz na inspektora to zobaczysz jaka jest między nimi różnica.
Co istotne, to ustawić odpowiednie nagłówki dla określonego typu, bo na tej podstawie serwer wie jak obsłużyć te dane, które otrzymuje.
Content-Type:
Accept-content-type:
Jeśli łączysz się z hostem innym niż obecny lub używasz innego portu, np. próbujesz ze strony google.com dostać się na onet.pl to przeglądarka zablokuje takie połączenie ze względów bezpieczeństwa. Aby przeglądarka umożliwiła połączenie, serwer docelowy musi zwrócić odpowiednie nagłówki CORS, zazwyczaj jest to:
Access-Control-Allow-Origin: https://amazing.site
- Fetch
- Websockets
- Promises
Fetch API jest nowocześniejszym podejściem do AJAXa, które zwraca Promise (warto poczytać dlaczego używanie promise jest lepsze od callbacków).
fetch('http://example.com/movies.json')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(JSON.stringify(myJson));
});
W przypadku wysyłania zapytań pod inne domeny niż ta na której znajduje się skrypt należy liczyć się z tym, że mogą nam zginąć ciastka, więc jeśli używamy ich do trzymania sesji, to musimy dodać specjalne nagłówki do zapytania, szczegóły w MDN:
Jak widać fetch posiada całkiem pokaźną listę opcji. Warto je sprawdzić:
postData(`http://example.com/answer`, {answer: 42})
.then(data => console.log(JSON.stringify(data))) // JSON-string from `response.json()` call
.catch(error => console.error(error));
function postData(url = ``, data = {}) {
// Default options are marked with *
return fetch(url, {
method: "POST", // *GET, POST, PUT, DELETE, etc.
mode: "cors", // no-cors, cors, *same-origin
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
credentials: "same-origin", // include, same-origin, *omit
headers: {
"Content-Type": "application/json; charset=utf-8",
// "Content-Type": "application/x-www-form-urlencoded",
},
redirect: "follow", // manual, *follow, error
referrer: "no-referrer", // no-referrer, *client
body: JSON.stringify(data), // body data type must match "Content-Type" header
})
.then(response => response.json()); // parses response to JSON
}
Załóżmy, że na backendzie, z poziomu Pythona czy innego node.js generujesz sobie tabelę i chcesz podpiąć do każdego wiersza przycisk wykonujący jakąś akcję AJAXem... Najczęstszą zagwozdką jest jak odpalić funkcję w backenedzie. Np. tak:
<tr>
<td>{{wiersz.id}}</td>
<td>{{wiersz.nazwa}}</td>
<td>{{wiersz.opis}}</td>
<td><button onclick="ajaxDelete( {{wiersz.id}} )">x</button></td>
</tr>
A jeśli podpinasz handlery używając addEventListener
to możesz użyć dataset, by podpiąć id do klikanego przycisku, a potem odczytać to id w obsłudze zdarzenia.
<tr>
<td>{{wiersz.id}}</td>
<td>{{wiersz.nazwa}}</td>
<td>{{wiersz.opis}}</td>
<td><button data-id="{{wiersz.id}}" class="row-delete-button">x</button></td>
</tr>
const deleteButtons = document.querySelectorAll(".row-delete-button");
for (let button of deleteButtons){
button.addEventListener("click", handleDeleteClick);
}
function handleDeleteClick(e){
console.log("about to delete:", e.target.dataset.id);
ajax.delete(`http://url.to/${e.target.dataset.id}`)
}