Lekcja 13: AJAX w jQuery - Wprowadzenie i $.ajax()

AJAX (Asynchronous JavaScript and XML) to technika pozwalająca na komunikację z serwerem w tle, bez konieczności przeładowywania całej strony. jQuery znacznie upraszcza wykonywanie zapytań AJAX, oferując zestaw wygodnych metod, z których najbardziej podstawową i elastyczną jest $.ajax().

Podstawy AJAX

Dzięki AJAX, strona internetowa może:

Metoda $.ajax()

$.ajax() to niskopoziomowa, ale bardzo konfigurowalna metoda do wykonywania asynchronicznych zapytań HTTP. Przyjmuje jeden argument – obiekt konfiguracyjny z wieloma opcjami.

Podstawowa składnia:

$.ajax({ 
  url: "adres_url_serwera", // Adres URL, do którego wysyłane jest zapytanie
  method: "GET", // Metoda HTTP (GET, POST, PUT, DELETE, etc.) - domyślnie GET
  dataType: "json", // Oczekiwany typ danych odpowiedzi (np. "json", "xml", "html", "text")
  data: { klucz1: "wartosc1", klucz2: "wartosc2" }, // Dane do wysłania (dla POST, PUT)
  
  // Funkcje zwrotne (callbacki)
  beforeSend: function(xhr) {
    // Wykonywane przed wysłaniem zapytania
    console.log("Wysyłanie zapytania...");
    // Można np. pokazać wskaźnik ładowania
  },
  success: function(data, textStatus, xhr) {
    // Wykonywane, gdy zapytanie zakończy się sukcesem
    console.log("Sukces! Otrzymano dane:", data);
    // `data` zawiera przetworzone dane odpowiedzi (np. obiekt JSON)
  },
  error: function(xhr, textStatus, errorThrown) {
    // Wykonywane, gdy wystąpi błąd
    console.error("Błąd AJAX:", textStatus, errorThrown);
    console.error("Status:", xhr.status, "Odpowiedź:", xhr.responseText);
  },
  complete: function(xhr, textStatus) {
    // Wykonywane zawsze po zakończeniu zapytania (po success lub error)
    console.log("Zapytanie zakończone.");
    // Można np. ukryć wskaźnik ładowania
  }
});

Najważniejsze Opcje Konfiguracyjne $.ajax():

Przykład: Pobieranie danych JSON

Załóżmy, że pod adresem /api/produkty serwer zwraca listę produktów w formacie JSON:

// Pobierz listę produktów po kliknięciu przycisku
$("#pobierz-produkty").on("click", function() {
  $.ajax({
    url: "/api/produkty", // Zastąp prawdziwym adresem URL
    method: "GET",
    dataType: "json",
    beforeSend: function() {
      $("#lista-produktow").html("Ładowanie...");
    },
    success: function(produkty) {
      let listaHtml = "
    "; produkty.forEach(function(produkt) { listaHtml += `
  • ${produkt.nazwa} - ${produkt.cena} zł
  • `; }); listaHtml += "
"; $("#lista-produktow").html(listaHtml); }, error: function() { $("#lista-produktow").html("

Nie udało się pobrać produktów.

"); }, complete: function() { console.log("Zakończono próbę pobrania produktów."); } }); });

Przykład: Wysyłanie danych POST

Wysyłanie danych z formularza na serwer:

$("#formularz-kontaktowy").on("submit", function(event) {
  event.preventDefault(); // Zatrzymaj domyślne wysyłanie formularza

  let daneFormularza = $(this).serialize(); // Zbierz dane formularza jako query string

  $.ajax({
    url: "/api/kontakt", // Zastąp prawdziwym adresem URL
    method: "POST",
    data: daneFormularza,
    dataType: "json", // Oczekujemy odpowiedzi JSON od serwera
    beforeSend: function() {
      $("#status-wysylania").text("Wysyłanie...").show();
    },
    success: function(odpowiedz) {
      if (odpowiedz.sukces) {
        $("#status-wysylania").text("Wiadomość wysłana!").css("color", "green");
        $("#formularz-kontaktowy")[0].reset(); // Wyczyść formularz
      } else {
        $("#status-wysylania").text("Błąd: " + odpowiedz.wiadomosc).css("color", "red");
      }
    },
    error: function() {
      $("#status-wysylania").text("Błąd serwera. Spróbuj ponownie.").css("color", "red");
    }
  });
});

Zadanie praktyczne

Użyj publicznego API, np. JSONPlaceholder (https://jsonplaceholder.typicode.com/users/1), aby pobrać dane pojedynczego użytkownika.

Stwórz plik HTML z przyciskiem "Pobierz Użytkownika" i pustym div-em z ID "dane-uzytkownika".

Używając $.ajax():

  1. Po kliknięciu przycisku, wykonaj zapytanie GET do https://jsonplaceholder.typicode.com/users/1.
  2. Ustaw oczekiwany dataType na "json".
  3. W funkcji success, wyświetl imię (name), email (email) i miasto (address.city) użytkownika w div-ie "dane-uzytkownika".
  4. W funkcji error, wyświetl komunikat błędu w tym samym div-ie.
  5. W funkcji beforeSend, wyświetl tekst "Ładowanie danych użytkownika..." w div-ie.
  6. W funkcji complete, wypisz w konsoli "Zakończono zapytanie o użytkownika.".
Pokaż rozwiązanie

HTML:

<!DOCTYPE html>
<html>
<head>
    <title>Test $.ajax()</title>
</head>
<body>
    <button id="pobierz-usera">Pobierz Użytkownika</button>
    <div id="dane-uzytkownika" style="margin-top: 10px; border: 1px solid #ccc; padding: 10px; min-height: 50px;">
        
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <script>
        $(function() {
            $("#pobierz-usera").on("click", function() {
                $.ajax({
                    url: "https://jsonplaceholder.typicode.com/users/1",
                    method: "GET",
                    dataType: "json",
                    beforeSend: function() {
                        $("#dane-uzytkownika").html("Ładowanie danych użytkownika...");
                    },
                    success: function(user) {
                        let userInfo = `<h3>${user.name}</h3>`;
                        userInfo += `<p>Email: ${user.email}</p>`;
                        userInfo += `<p>Miasto: ${user.address.city}</p>`;
                        $("#dane-uzytkownika").html(userInfo);
                    },
                    error: function(xhr, status, error) {
                        $("#dane-uzytkownika").html("<p style='color: red;'>Błąd pobierania danych: " + status + " - " + error + "</p>");
                    },
                    complete: function() {
                        console.log("Zakończono zapytanie o użytkownika.");
                    }
                });
            });
        });
    </script>
</body>
</html>

Zadanie do samodzielnego wykonania

Użyj API JSONPlaceholder dla postów (https://jsonplaceholder.typicode.com/posts).

Stwórz formularz z jednym polem tekstowym (textarea) dla treści posta i przyciskiem "Dodaj Post". Dodaj też div z ID "status-posta".

Używając $.ajax():

  1. Po wysłaniu formularza (przechwyć zdarzenie submit i użyj event.preventDefault()):
  2. Wykonaj zapytanie POST do https://jsonplaceholder.typicode.com/posts.
  3. Wyślij dane w obiekcie data, np. { title: 'Mój nowy post', body: trescZPolaTextarea, userId: 1 }.
  4. Ustaw dataType na "json".
  5. W funkcji success, wyświetl w div-ie "status-posta" komunikat "Post dodany pomyślnie! ID: [ID zwrócone przez API]". API zwróci obiekt z dodanym postem, w tym jego nowe ID.
  6. W funkcji error, wyświetl komunikat błędu.

Uwaga: JSONPlaceholder symuluje dodawanie danych, faktycznie ich nie zapisuje, ale zwraca obiekt tak, jakby post został dodany (zazwyczaj z ID 101).

FAQ - AJAX w jQuery ($.ajax())

Czym różni się $.ajax() od $.get() i $.post()?

$.get() i $.post() to uproszczone metody-skróty dla typowych zapytań GET i POST. Są one zbudowane na bazie $.ajax() i oferują mniej opcji konfiguracyjnych, ale są szybsze w użyciu dla prostych przypadków. $.ajax() daje pełną kontrolę nad wszystkimi aspektami zapytania.

Co to jest CORS (Cross-Origin Resource Sharing)?

CORS to mechanizm bezpieczeństwa przeglądarek, który ogranicza możliwość wykonywania zapytań AJAX do innych domen (innych niż ta, z której pochodzi strona). Aby zapytanie do innej domeny się powiodło, serwer docelowy musi wysłać odpowiednie nagłówki HTTP (np. Access-Control-Allow-Origin), zezwalające na takie zapytanie. JSONPlaceholder wysyła te nagłówki, dlatego możemy go używać w przykładach.

Jak obsługiwać różne kody statusu HTTP w error?

Obiekt xhr (XMLHttpRequest) przekazywany do funkcji error zawiera właściwość status z kodem odpowiedzi HTTP (np. 404, 500). Możesz użyć instrukcji if lub switch, aby zareagować inaczej w zależności od kodu błędu: if (xhr.status === 404) { ... } else if (xhr.status === 500) { ... }.

Czy muszę używać wszystkich callbacków (beforeSend, success, error, complete)?

Nie, wszystkie funkcje zwrotne są opcjonalne. Najczęściej używa się success do obsługi pomyślnej odpowiedzi i error do obsługi błędów. beforeSend i complete są przydatne np. do zarządzania wskaźnikami ładowania.

Co jeśli serwer nie odpowiada (timeout)?

Jeśli ustawisz opcję timeout i serwer nie odpowie w określonym czasie, zostanie wywołana funkcja error. Wartość textStatus będzie wtedy zazwyczaj równa "timeout".