Mir Stephen
Mir Stephen

Reputation: 1927

Is it possible to pass Ajax response as template context variable in Django?

I sent an Ajax request to server to get some filtered data and here is a sample I receive from server:

(3) [{…}, {…}, {…}]
0: {id: 1, title: "12 Rue Longueil", slug: "12-rue-longueil", latitude: null, longitude: null, …}
1: {id: 2, title: "15 Rue Sherbrooke LM", slug: "15-rue-sherbrooke-lm", latitude: null, longitude: null, …}
2: {id: 3, title: "Cycle Neron", slug: "cycle-neron", latitude: "-73.5987000000000000", longitude: "45.4799000000000000", …}
length: 3
__proto__: Array(0)

above data is logged from console.

I want to display these data in HTMl tags within cards below.

enter image description here

but for that I need to use that received data and create children using JavaScript e.g. document.createElement('DIV'). and then place these data.

  $(document).on('submit', "#filterform", function (e) {

        e.preventDefault();

        $.ajax({
            type: 'GET',
            url: "{% url 'listing:search' %}",
            data: {
                listing_for: $('#listing_for').val(),
                // cutted
            },

            success: function (response) {
                const listings = eval(response);
    
                const content = document.getElementById('content');

                for (let i = 0; i < listings.length; i++) {
                    const div = document.createElement('div');
                    div.className = 'listing mgb-1';
                    div.innerHTML = data[i].title;
                    content.appendChild(div);

                    // have to create, add lots of divs and classes
                }

            }
        })
    })

I was wondering if there is a way to sent Ajax request data as template variable? Or do I have to hardcode all HTML tags using Javascript?

Edit: Edited content based on first answer creating a separate HTML.

def search(request):
    ...
    lst = list()

    for listing in queryset:
        ser = ListingSerializer(listing)
        lst.append(ser.data)
    
    return render(request, 'listing/newHtmlFile.html', {'listings': json.dumps(lst)})

separate HTML:

{% for list in listings %}
<div class="jumbotron">
    <h1>{{ list.title }}</h1>
</div>
{% endfor %}

and ajax request:

success: function (response) {
    document.querySelector('#content').insertAdjacentHTML('beforeend', response);
}

Upvotes: 1

Views: 1109

Answers (2)

revliscano
revliscano

Reputation: 2272

Yes, you can. Basically the idea is to make a separate HTML file that's going to be rendered by the view that handles the AJAX request. Then, you can use JavaScript and the insertAdjacentHTML() function to insert it in your original HTML file.

Take a look at this example:

view:

def ajax_handler(request):
    # ... logic
    return render(request, 'newHtmlFile.html', {'your_context': data})

Original HTML file

<div id='container'>
    
</div>

newHtmlFile.html

<p>{{ your_context }}</p>

JavaScript part (in this example I use Vanilla, not JQuery)

let ajax = new XMLHttpRequest();
ajax.onreadystatechange = function(){
    if (this.readyState === 4){
        if (this.status === 200){
            document.querySelector('#container').insertAdjacentHTML('beforeend', this.responseText);
        }
    }
}
ajax.open('GET', '/ajax-handler-url', true);
ajax.send();

If you are interested to know how this works, we can break it down as follows:

  1. You have some data (like a queryset, in your case) in your view
  2. You call the render() method and you pass that data as the context data to a template
  3. The render() method what actually does is to (let me be redundant here) render a HTML file (combining the HTML itself with the context data you passed) and return a HTTPResponse object containing a rendered text.
  4. That rendered text (which is a bytestring that represents the content of the rendered HTML) is given as a response to the client. In this case, it's given specifically to the $.ajax() function that made the request.
  5. We use the insertAdjacentHTML() function to append that rendered text to the desired element (in the example above, the #container div).

Upvotes: 1

thebjorn
thebjorn

Reputation: 27311

A quick, and possibly "dirty", way of doing it is to use the backtick strings in javascript:

success: function (r) {
    const listings = JSON.parse(r);  // with the correct headers from django, this should't be needed.
    listings.forEach(e => $('#content').append(`
        <div class="listing mgb-1">${e.title}</div>
    `));
}

You should return your data from django with the appropriate headers so you automatically get json and don't need to eval(response).

Upvotes: 1

Related Questions