{# Field-level error message. The id is what `aria-describedby` on the control points at, so the message is read out when focus reaches the field rather than only being visible. Errors also carry an icon + "Error:" prefix so the failure is not signalled by the red colour alone (WCAG 1.4.1), and role="alert" so a message that arrives in an Unpoly fragment swap is announced. #} {% macro field_error(construct, error_id, error) %} {% if error %} {{ icon("cancel", label=construct.t("crud-error-prefix")) }} {{ construct.t(error.key, error.args) }} {% endif %} {% endmacro %} {# Attributes tying a control to its error message. Emitted on the control itself so the association survives however the field is laid out. #} {% macro error_attrs(error_id, error) %} {%- if error %} aria-invalid="true" aria-describedby="{{ error_id }}"{% endif -%} {% endmacro %} {# A displayed value, with an empty one left blank rather than printed. `format_value` hands back the stored attribute itself, which is None for a null column and for a `Related` pointing at nothing — and Jinja renders that as the word "None", so an unfilled field reads as though somebody typed it. The test is against none rather than falsiness, because 0, an empty string and False are all values somebody meant. #} {% macro shown(value) %}{%- if value is not none %}{{ value }}{% endif -%}{% endmacro %} {% macro render_field(construct, field, type, view, data=None, ds=None, errors=None) %} {% if type.secret and data and type.readonly %} {# Masked, with no way to change it — and never the stored value, which is exactly what `secret` exists to keep out of the response. #}
{{ construct.t("crud-no-items-found") }}
{% endif %} {% endif %} {% if not type.readonly and type.can_add %} {% if type.layout == 'form' %}{{ construct.t("crud-validation-errors") }}