jQuery Mobile, Django and Collapsible

November 12, 2010

This article follows on from two previous ones, available at http://drumcoder.co.uk/blog/2010/nov/12/jquery-mobile-basics/ and http://drumcoder.co.uk/blog/2010/nov/12/jquery-mobile-and-django/. Please read both those first.

This article covers the results page. This is a Django generated list of contest results for a given band, with collapsible details underneath, working like an Accordion.

Band Links

The links in the band list page go to:

<li><a href="/bands/{{band.slug}}/">{{band.name}}</a></li>

So we now need to add an entry into the band application's url.py for this:

(r'^([\w-]*)/$', 'mobile.bands.views.band'),

This links to the band view:

def band(request, pBandSlug):
    """
    Show a specific band
    """
    try:
        lBand = Band.objects.filter(slug=pBandSlug).select_related()[0]
    except IndexError:
        raise Http404()
    return render_auth(request, 'bands/results.htm', {"Band" : lBand,
                                                      "ContestResults" : lBand.contestresult_set.all()
                                                     })

The results.htm template contains:

{% load humanize %}
<div data-role="page">

<div data-role="header">
    <h1>Results</h1>
    <a data-icon="arrow-u" class="ui-btn-right" href="#home">Home</a>
</div>

<div data-role="content">
    <h2>{{Band.name}}</h2>
    <h3>{{Band.region.name}}</h3>

    <div data-role="collapsible-set">
    {% for result in ContestResults %}
        <div data-role="collapsible">
          <h3>{{result.results_position|ordinal}} - {{result.contest_event.contest.name}} {{result.contest_event.event_date}}</h3>
          <p>Competed as: {{result.band_name}}</p>
          <p>Conductor: <a href="/conductors/{{result.conductor.slug}}/">{{result.conductor.name}}</a></p>
          <p>Draw: {{result.draw}}</p>
          {% if result.points %}<p>Points: {{result.points}}</p>{% endif %}
          {% if result.test_piece %}
              <p>Test Piece: <a href="/pieces/{{result.test_piece.slug}}/">{{result.test_piece.name}}</a></p>
          {% endif %}
        </div>
    {% endfor %}
    </div>
</div>

<div data-role="footer">
    <h4>Footer</h4>
</div>

Collapsible

The collapsible behaviour is the key thing to note about this HTML.

Here's how it renders normally:

Collapsed

And here's what happens if you click on one of the plus symbols:

CollapsedOpen

If you click a different plus, then the one that is open is closed first.

The generated HTML for the first two rows looks something like this:

<div data-role="collapsible-set">
    <div data-role="collapsible">
        <h3>8th - Fife Charities Band Assocation Contest 3rd Oct 2009</h3>
        <p>Collapsed Content Is Here</p>
    </div>

    <div data-role="collapsible">
        <h3>7th - Northern Counties Open Contest 24th May 2009</h3>
        <p>Collapsed Content Is Here</p>
    </div>
</div>

The outer div defines data-role="collapsible-set" which indicates that the collapsible sub div's should be treated as one group, with only one open at a time.

Each inner div has data-role="collapsible" which adds the plus/minus button (this can also be used outside a set)

By default, jQuery mobile hides anything after the header, and by default the last one is open. To specify they should all be closed, add data-collapsed="true" to the collapsible div.

<div data-role="collapsible" data-collapsed="true">