Back to Blog Tutorial

Building a Data List: Using Only HTML Structure and SQL

This post kicks off a short hands-on arc building a small Contacts feature, piece by piece. Let's start with the List component, generated from a SQL query and a small HTML-style structure expressed in XML. You define the structure, write the query — the platform renders the UI automatically, without you writing any front- or back-end code

Mar 2005·6 min read·Post 3 of 8

A list is the right starting point because it's the smallest complete loop the engine runs: SQL goes in, a row template gets repeated once per result, HTML comes out. Nothing else on the platform is simpler than this, and almost everything else is built from the same idea.

The component for this is dynamiclist — a repeating HTML-style structure driven by data. In XML, it appears as an element where you provide an outer container tag, a per-row template, and a SQL query. The engine runs the query and, for every row returned, renders your template once with that row's values substituted in.

1 The XML

Here's a complete, working dynamiclist block for a Contacts list — nothing trimmed:

emxml_blkContactsList.xml
<Block tag='div'>

    <Element type='dynamiclist' tag='div'>
    <Attributes class='contact-grid' />

    <ListRow tag='div'>
        <Attributes class='contact-card' />
        <Element type='htmlitem' tag='div'>
            <Attributes class='contact-avatar' />
            <Text>%initials%</Text>
        </Element>
        <Element type='inlinecontainer' tag='div'>
            <Element type='htmlitem' tag='div'>
                <Attributes class='contact-name' />
                <Text>%contact_name%</Text>
            </Element>
            <Element type='htmlitem' tag='div'>
                <Attributes class='contact-role' />
                <Text>%job_title%</Text>
            </Element>
            <Element type='htmlitem' tag='div'>
                <Attributes class='contact-meta' />
                <Text>%email%</Text>
            </Element>
        </Element>
    </ListRow>

    <!-- SQL always comes last, after ListRow -->
    <SQL>
        <query name='contacts_list'>
            <![CDATA[
                SELECT contact_id,
                       contact_name,
                       job_title,
                       email,
                       UPPER(LEFT(contact_name,1)) AS initials
                FROM   contacts
                WHERE  dept_id = '%deptid%'
                ORDER BY contact_name ASC
            ]]>
        </query>
    </SQL>
    </Element>

</Block>

That's the whole thing. No loop to write, no result-set iteration, no string concatenation to build up a row of markup — just a template and a query.

What each piece is doing

<Block>
Every block file's root element is <Block>, not the dynamiclist itself — the actual list content sits one level in, as the Block's child.
tag='div'
The outer wrapper the whole list renders inside — here a grid container, styled by the contact-grid class.
ListRow
The free-form template for one row. It's processed as a full element tree — anything you can build elsewhere on the platform (nested containers, conditional blocks, other elements) is available inside it.
%tokens%
Column values from the query, substituted per row. These row tokens are scoped to this ListRow only — they don't leak into the rest of the page the way a single-row block query would.
SQL last
Inside a dynamiclist, the query always comes after ListRow — the same file-ordering convention used everywhere else on the platform.
2 What the engine generates

The engine runs contacts_list, gets back however many rows match, and renders ListRow once per row with that row's tokens substituted. For four contacts, the output looks like this:

Generated HTML
<div class="contact-grid">

    <div class="contact-card">
        <div class="contact-avatar">S</div>
        <div>
            <div class="contact-name">Sarah Whitfield</div>
            <div class="contact-role">Operations Manager</div>
            <div class="contact-meta">s.whitfield@example.com</div>
        </div>
    </div>

    <!-- … repeated once per row, no two the same — the template is fixed, the data isn't … -->

</div>

Every card is the identical markup structure — only the token values change. That's the entire value of a dynamiclist: write the shape once, let row count vary freely.

3 How it actually renders

And here's that generated HTML, live, in a real browser — not a mockup:

app.example.com/manager/contacts
S
Sarah Whitfield
Operations Manager
s.whitfield@example.com
D
David Okonkwo
Procurement Lead
d.okonkwo@example.com
M
Maria Santos
Finance Controller
m.santos@example.com
J
James Cooper
Warehouse Supervisor
j.cooper@example.com
Four rows or four hundred, the XML above doesn't change. The row count is the database's problem, not the developer's.

Why not just write a PHP loop?

Why should you? Because doing this manually means backend code, escaping values, wiring templates, and keeping everything in sync — work you don't need to do. dynamiclist lets you define the structure and the SQL together, and the engine handles the repetitive part.

In the next post, we'll look at dynamiclisttable — the component built specifically for tabular, column-based data — and how it extends the same idea for spreadsheet-like layouts.