Nested markup — categories
Items contain their children; depth is derived from nesting. Click a
row to fire materialTreeActivate; arrows / Home / End /
Enter work from the keyboard.
(no events)
Flat markup with level — the django-mptt shape
The same tree as an ordered list of rows, exactly what mptt iteration
yields: <material-tree-item level="{{ node.level }}"
{% if not node.is_leaf_node %}has-children{% endif %}>.
No nesting in the markup at all.
Tri-state selection + form posting
selectable adds cascading checkboxes: a parent drives its
subtree and shows indeterminate for partial selections. Checked values
post as categories=… per node —
request.POST.getlist('categories') on the Django side.
Lazy children — src endpoint
Nodes marked has-children fetch
/demo-api/tree?parent=<value> on first expand
(mocked, 500 ms). Selection cascades into lazily loaded children.
Without src the tree emits materialTreeLoad
instead, and you insert items yourself.
Trailing cells — aligned columns (BOM)
Fixed-width slot="trailing" cells line up one under
another on every depth — only the label side indents. dense
rows.
RTL (hard-coded)
Indent, chevrons and arrow keys follow the reading direction.
Sample usage (Django + mptt)
<form method="post">
<material-tree selectable name="categories" src="{% url 'category-children' %}">
{% for node in nodes %}
<material-tree-item level="{{ node.level }}" value="{{ node.pk }}"
label="{{ node.name }}"
{% if not node.is_leaf_node %}has-children{% endif %}
{% if node.level < 1 %}expanded{% endif %}
{% if node.pk in selected_ids %}checked{% endif %}>
</material-tree-item>
{% endfor %}
</material-tree>
</form>
# views.py — lazy children + selection
def category_children(request):
parent = Category.objects.get(pk=request.GET['parent'])
return JsonResponse({'results': [
{'value': c.pk, 'label': c.name, 'has_children': not c.is_leaf_node()}
for c in parent.get_children()
]})
def save(request):
ids = request.POST.getlist('categories')