How to set multiple values for the same attribute in tal:attributes using TALES
-
23-02-2021 - |
题
I'm trying to set multiple css classes on one element.
Unfortunately this doesn't work, as it returns: LanguageError: Duplicate attribute name in attributes.
<ul>
<li tal:repeat="item mainnav"
tal:attributes="class 'first' if repeat.item.start else nothing;
class 'last' if repeat.item.end else nothing;
class 'active' if item.active else nothing">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</ul>
Combining those 3 cases into one expression makes it quite complicated, because there are 6 different css states:
- first + active
- first
- last + active
- last
- active
- (none)
There are 2 possible solutions that I can think of:
-> check each combination inline:
<ul>
<li tal:repeat="item mainnav"
tal:attributes="
class 'first active' if (repeat.item.start and item.active) else
'first' if repeat.item.start else
'last active' if (repeat.item.end and item.active) else
'last' if repeat.item.end else
'active' if item.active else nothing">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</ul>
-> create a method that returns the combined css classes
Now, is there a better approach and if not, which of those 2 is better (probably the latter one, as if it gets more complicating the inline script will become unreadable/unmanageable).
BTW, are there any good resources and examples about Chameleon
, TALES
(other than http://chameleon.repoze.org/docs/latest)
解决方案
You're not using tal:condition, it has a purpose. I don't like overly nested conditionals, gets you no where. Haven't tested this but you may get the idea.
<ul>
<tal:myloop tal:repeat="item mainnav">
<li tal:condition="item.active" tal:attributes="class
'active first' if repeat.item.start
else 'active last' if repeat.item.end
else 'active'">
<a tal:attribute="href item.href" tal:content="item.title"></a>
</li>
<li tal:condition="not item.active" tal:attributes="class
'first' if repeat.item.start
else 'last' if repeat.item.end else None">
<a tal:attribute="href item.href" tal:content="item.title"></a>
</li>
</tal:myloop>
</ul>
其他提示
You can use tal:define
multiple times to define the various parts of your class string, then construct the actual attribute from those parts:
<tal:loop repeat="item mainnav">
<li tal:define="class_first 'first' if repeat.item.start else '';
class_last 'last' if repeat.item.end else '';
class_active 'active' if item.active else '';"
tal:attributes="class string:$class_first $class_last $class_active">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</tal>
This could result in an empty class attribute, which is harmless.
As for additional documentation; Chameleon is an implementation of TAL, originally developed for Zope Page Templates. As such, you'll find a lot of documentation for the latter also applies to Chameleon, as long as you take into account that Chameleon's default TALES modus is python:
, while ZPT defaults to path:
instead. The Advanced Page Templates chapter of the Zope Book applies to Chameleon as well, for example.
In Chameleon you can do:
<ul>
<li tal:repeat="item mainnav"
class="${'first' if repeat.item.start else ''}
${'last' if repeat.item.end else ''}
${'active' if item.active else ''">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</ul>
[Edit] Or better like this:
<ul>
<li tal:repeat="item mainnav"
class="${('first ' if repeat.item.start else '') +
('last ' if repeat.item.end else '') +
('active' if item.active else '')}">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</ul>