jQuery - JSON

W tym dziale przećwiczymy realne zastosowanie Ajaxa za pomocą jQuery.

Mamy listę html:


<div class="users-list" id="usersList">
</div>

Nasze zadanie polega na:

  • Połączeniu się pod odpowiedni adres API i ściągnięciu danych użytkowników
  • Wygenerowanie dla każdego użytkownika odpowiedniego kontenera na stronie
  • Każdy taki kontener powinien zawierać dane o użytkowniku oraz przycisk "Pokaż posty"
  • Po kliknięciu tego przycisku powinniśmy dynamicznie ściągnąć posty danego użytkownika oraz pokazać je pod danymi użytkownika
  • Kolejne klikanie w ten przycisk nie powinno już ściągać postów, a tylko je zwijać i rozwijać

Wygląd pojedynczego kontenera z użytkownikiem i pustą listą postów będzie miał postać:


    <article class="user-cnt" data-id="1">
        <h2 class="user-name">Przykładowy user</h2>
        <div class="address">
            Phone: 500 600 600<br>
            email: <a href="mailto: email@wp.pl">email@wp.pl</a>
        </div>

        <button type="button" class="btn show-posts">Show posts</button>

        <div class="user-posts">
            <h2 class="user-posts-title">User posts:</h2>
            <div class="user-posts-list">

                <div class="post">
                    <h3 class="post-title">Lorem ipsum dolor sit amet, consectetur</h3>
                    <div class="post-body">
                        Lorem ipsum dolor sit amet...
                    </div>
                </div>

            </div>
        </div>
    </article>

Dane użytkowników jak i ich postów pobierzemy ze strony https://jsonplaceholder.typicode.com/, która udostępnia jego z wielu przykładowych api.

Przykład 1 - czysty html

Łączenie się z API

Pierwszą rzeczą jaką zrobimy to połączenie się ze stroną i pobranie użytkowników. Jak wejdziemy na powyższą stronę, zobaczymy w opisie, że link do danych o użytkownikach ma postać https://jsonplaceholder.typicode.com/users. Wykorzystajmy go:


const apiURL = 'https://jsonplaceholder.typicode.com/';

const $list = $('.users-list');

function readData() {
    $.ajax({
        url : apiURL + 'users',
        dataType : 'json'
    })
    .done(ret => {
        console.log(ret);
    });
}

readData();

Dostaniemy w konsoli tablicę obiektów z danymi każdego użytkownika.


[
    {
        "id": 1,
        "name": "Leanne Graham",
        "username": "Bret",
        "email": "Sincere@april.biz",
        "address": {
            "street": "Kulas Light",
            "suite": "Apt. 556",
            "city": "Gwenborough",
            "zipcode": "92998-3874",
            "geo": {
                "lat": "-37.3159",
                "lng": "81.1496"
            }
        },
        "phone": "1-770-736-8031 x56442",
        "website": "hildegard.org",
        "company": {
            "name": "Romaguera-Crona",
            "catchPhrase": "Multi-layered client-server neural-net",
            "bs": "harness real-time e-markets"
        }
    },
    {...},
    {...},
    {...},
    ...
]

Zróbmy po nich pętlę i wygenerujmy kod naszych kontenerów:


const apiURL = 'https://jsonplaceholder.typicode.com/';

const $list = $('.users-list');

function readData() {
    $.ajax({
        url : apiURL + 'users'
    })
    .done(ret => {
        ret.forEach(user => {
            const $element = $(`
                <article class="user-cnt">
                    <h2 class="user-name">${user.name}</h2>
                    <div class="address">
                        Phone: ${user.phone}<br>
                        email: <a href="mailto: ${user.email}">${user.email}</a>
                    </div>
                    <button type="button" class="btn show-posts">Show posts</button>
                    <div class="user-posts">
                        <h2 class="user-posts-title">User posts:</h2>
                        <div class="user-posts-list">
                        </div>
                    </div>
                </article>
            `);

            $list.append($element);
        });
    });
}

readData();

Przykład 2 - wczytywanie listy użytkowników

Kolejnym elementem jest dodanie obsługi dla przycisków .show-posts. Tak samo jak przy eventach dla dynamicznych elementów, tak i tutaj musimy podpiąć event nie dla samych przycisków, a przez rodzica, któremu powiemy, że chodzi o nasze przyciski.

Podpięcie eventu bezpośrednio pod przyciski .show-posts, by nie zadziałało, ponieważ w momencie podpinania przyciski te jeszcze nie istnieją (utworzone zostaną dopiero po odpowiedzi z serwera)


const apiURL = 'https://jsonplaceholder.typicode.com/';

const $list = $('.users-list');

function readData() {
    $.ajax({
        url : apiURL + 'users'
    })
    .done(ret => {
        ret.forEach(user => {
            const $element = $(`
                ...
            `);

            $list.append($element);
        });
    });
}

readData();

togglePosts($btn) {
    ...
}

$list.on('click', '.show-post', function() {
    togglePosts($(this));
});

Żeby pobrać dane postów, musimy połączyć się na adres https://jsonplaceholder.typicode.com/posts?userId=1, czyli do naszego bazowego apiUrl musimy dodać userId=1.
Skąd wziąć id usera? Możemy do tego wykorzystać wcześniej generowany html, dodając do .user-cnt atrybut data-id="userID":


function readData() {
    $.ajax({
        url : apiURL + 'users'
    })
    .done(result => {
        result.forEach(user => {
            const $element = $(`
                <article class="user-cnt" data-id="${user.id}">
                    ...
                </article
            `);
        });
    });
}

Pozostaje dokończyć obsługę przycisku .show-posts:


function togglePosts($btn) {
    const $cnt = $btn.closest('.user-cnt');
    const $postList = $cnt.find('.user-posts-list');
    const id = $cnt.attr('data-id');

    $.ajax({
        url : `https://jsonplaceholder.typicode.com/posts?userId=${id}`,
        method : 'get',
        dataType : 'json'
    })
    .done(response => {
        $postList.empty();
        response.forEach(post => {;
            const $post = $(`
                <div class="post">
                    <h3 class="post-title">${post.title}</h3>
                    <div class="post-body">
                        ${post.body}
                    </div>
                </div>
            `);
            $postList.append($post);
        });
        $cnt.find('.user-posts').slideDown();
    });
}

Przykład 3 - wczytywanie postów

Dodatkowo chcielibyśmy, by użytkownik miał możliwość zwinięcia listy postów, co powodowało by zmianę tekstu na przycisku z Show posts na Hide posts i vice wersa. Możemy to zrobić np. poprzez sprawdzenie, czy ta lista jest widoczna:


function togglePosts($btn) {
    const $cnt = $btn.closest('.user-cnt');
    const $postList = $cnt.find('.user-posts-list')
    const id = $cnt.attr('data-id');

    if (!$cnt.find('.user-posts').is(':visible')) {
        $btn.text('Hide posts');
        $.ajax({
            ...
        });
    } else {
        $cnt.find('.user-posts').slideUp();
        $btn.text('Show posts');
    }
}

Przykład 4 - zwijanie i rozwijanie postów

Ostatnia rzecz, to typowa optymalizacja. Gdy pierwszy raz pobierzemy dane, to przy następnym zwijaniu i rozwijaniu nie ma sensu non stop ich pobierać. Przyda się tutaj jakaś zmienna przełącznik, która powie nam czy dane się już wcześniej wczytały. Idealnie do tego nadaje się obiekt data udostępniany przez jQuery:


function togglePosts($btn) {
    const $cnt = $btn.closest('.user-cnt');
    const $postList = $cnt.find('.user-posts-list');
    const id = $cnt.attr('data-id');

    if (!$cnt.find('.user-posts').is(':visible')) {
        $btn.text('Hide posts');
        if (!$btn.data('loaded')) {
            $.ajax({
                url : `https://jsonplaceholder.typicode.com/posts?userId=${id}`,
                method : 'get',
                dataType : 'json'
            })
            .done(function(response) {
                $postList.empty();
                response.forEach(post => {;
                    const $post = $(`
                        <div class="post">
                            <h3 class="post-title">${post.title}</h3>
                            <div class="post-body">
                                ${post.body}
                            </div>
                        </div>
                    `);
                    $postList.append($post);
                });

                $btn.data('loaded', true);

                $cnt.find('.user-posts').slideDown();
            });
        } else {
            $cnt.find('.user-posts').slideDown();
        }
    } else {
        $cnt.find('.user-posts').slideUp();
        $btn.text('Show posts');
    }
}

Przykład 5 - zwijanie i rozwijanie postów bez zbędnego pobierania