Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
Convert theme_item_list()
theme function to .html.twig
template and template_preprocess_item_list()
function for demonstration of Twig theming and ideal separation of concerns between logic and markup of item lists.
Remaining tasks
- Remove first/last/even/odd classes after fixing #1649780: Remove first/last/odd/even classes in favor of CSS3 pseudo selectors.
- #1785310: Support custom child list types in theme_item_list()
- Decide what to do with
<div class="item-list"></div>
wrapper. See comment #32
User interface changes
None.
API changes
None at the moment; Remaining tasks may dictate that client code output wrapper element.
See theme_item_list()
.
Comments
Comment #1
Fabianx CreditAttribution: Fabianx commentedHowto render a tree:
http://stackoverflow.com/questions/8326482/twig-how-to-render-a-tree
Comment #2
steveoliver CreditAttribution: steveoliver commentedSeems to be working...
Comment #3
Fabianx CreditAttribution: Fabianx commentedShould check if title is set
This loop is not necessary.
General:
We need to move the attribute processing part to preprocess function.
Comment #4
Fabianx CreditAttribution: Fabianx commentedHere is some code form initial code sprint that should help with the first, last, even and odd attributes so the code int the preprocess can be simpler.
Comment #5
steveoliver CreditAttribution: steveoliver commentedWith this PHP code in a node:
Bartik produces this output:
...with this markup:
Stark, with the attached patch, produces this output:
...with this markup:
Issues:
template_preprocess_item_list()
isn't being called for either Bartik or Stark.Comment #6
steveoliver CreditAttribution: steveoliver commentedAlso, in Stark, I'm getting the following errors:
User error: "class" is an invalid render array key in element_children() (line 6251 of core/includes/common.inc).
User error: "id" is an invalid render array key in element_children() (line 6251 of core/includes/common.inc).
User error: "data" is an invalid render array key in element_children() (line 6251 of core/includes/common.inc).
User error: "0" is an invalid render array key in element_children() (line 6251 of core/includes/common.inc).
User error: "1" is an invalid render array key in element_children() (line 6251 of core/includes/common.inc).
User error: "data" is an invalid render array key in element_children() (line 6251 of core/includes/common.inc).
User error: "0" is an invalid render array key in element_children() (line 6251 of core/includes/common.inc).
User error: "1" is an invalid render array key in element_children() (line 6251 of core/includes/common.inc).
User error: "data" is an invalid render array key in element_children() (line 6251 of core/includes/common.inc).
User error: "0" is an invalid render array key in element_children() (line 6251 of core/includes/common.inc).
User error: "1" is an invalid render array key in element_children() (line 6251 of core/includes/common.inc).
Comment #7
Fabianx CreditAttribution: Fabianx commentedJFYI: I fixed #6 in front-end branch.
Comment #8
steveoliver CreditAttribution: steveoliver commentedOK, I'm stuck here with children attributes. I expect things to work, don't get errors, but also don't get children attributes. Patch attached. Use this PHP code to test:
Comment #9
steveoliver CreditAttribution: steveoliver commented'class'
needed to be an array. Test this code with the attached patch. Works for me. Notice comment at http://api.drupal.org/api/drupal/core%21includes%21theme.inc/function/th... for question about supporting children lists with different types than their parents.Comment #10
steveoliver CreditAttribution: steveoliver commentedAnswer to child type question in #9: NO for now.
Use this test code, with attached patch (has cleaned up preprocess functions):
Comment #11
steveoliver CreditAttribution: steveoliver commentedChild item attributes still need work...
Comment #12
steveoliver CreditAttribution: steveoliver commentedOK, child attributes work. Please review attached patch with this code:
Expected output, should be the same in Garland:
Two issues/questions:
1. Preceding whitespace in class attribute. WTF?
2. Should we be doing striping in preprocess or in .twig?
Comment #13
Fabianx CreditAttribution: Fabianx commentedThat can't work " even", " odd" will always have bad whitespace ... ;-)
But I think with HTML5 initiative we will strip out all even/odd anyway and replace with n-th child CSS3 selectors.
Comment #14
steveoliver CreditAttribution: steveoliver commentedI knew those spaces were there, ;) I thought they'd need to be somewhere, but as this patch shows, it seems Twig will take care of the whitespace.
Comment #15
steveoliver CreditAttribution: steveoliver commentedChanges in this patch:
1. Removed status-messages preprocess function accidentally included in previous patch.
2. Clean up preprocess function(s).
3. Attributes are always set, even if empty. (As per Fabianx's comment in the select.twig issue.).
Notes on preprocess:
1. There is one top level preprocess function that runs items through _item() and _attributes() callbacks to deal with nested lists. I don't see a way to do this cleanly within the .twig file, without preprocess (to address comments by chx and stevector in #1696786: Integrate Twig into core: Implementation issue.
Notes on .twig template:
1. .twig file contains lots of comments -- it's a little ugly. @todo: remove them?
2. it supports child items to be of different type (ol, ul) than parent. them_item_list() didn't support this; child always inherited parent type. @todo: remove this?
Comment #16
steveoliver CreditAttribution: steveoliver commentedDon't use the patch in #15. Use this patch. All notes from #15 still apply.
Comment #17
sunThere should be no output at all if there are no items.
Comment #18
steveoliver CreditAttribution: steveoliver commentedBut of course!
Comment #19
steveoliver CreditAttribution: steveoliver commented"Any other key-value pairs" are not available in .twig; They are unset after being converted into attributes in _preprocess.
Patch attached includes comment reflecting this.
Comment #20
tstoecklerThe core issue about removing the (with CSS3) extraneous classes is here: #1649780: Remove first/last/odd/even classes in favor of CSS3 pseudo selectors
Comment #21
steveoliver CreditAttribution: steveoliver commentedThanks. What do you think, add a @todo until that lands?
Comment #22
tstoecklerThat would probably make sense. I just looked it up because I somehow had thought that was already in and was confused... :-)
Comment #23
crizIMHO not only the even/odd/last/first-classes should be eliminated, also the hardcoded wrapping div should be stripped out in the basic implementation of item-list.twig.
A similar comment is here: http://api.drupal.org/api/drupal/includes!theme.inc/function/theme_item_...
I think this makes also sense when item-list.twig will be used in other templates like for pagers or menus. See #1777332: Replace theme_menu_link() and menu-tree.html.twig with a single Twig template
Themes/modules can introduce this wrappers if they really need it easily.
Comment #24
jenlamptonI agree with @criz that we should remvoe the wrapper div from item list and only the list should be printed. the wrapper can also be added in templates calling item_list if needed.
Comment #25
steveoliver CreditAttribution: steveoliver commentedDefinitely. Here's the latest, also without all the verbose comments in .twig.
Comment #26
steveoliver CreditAttribution: steveoliver commentedHere, keeping classes for now (with @todo remove), but without wrapper div.
Comment #27
tstoecklerNot sure about removing the outer class. If we wouldn't support the list title, I would totally agree, because then we would have a straight mapping: theme_item_list() <-> ul/ol. Now the title seems somewhat misplaced without any outer nesting.
Either way, if we decide to remove the outer div, I think the outer if statement is superfluous, no?
Comment #28
steveoliver CreditAttribution: steveoliver commentedRe: wrapper:
It seems like we have a decision to make between trade-offs.
1. With an item-list wrapper, each level of the list is wrapped (eww), but title is "grouped" with list (ok for top level, not really necessary for lower levels).
2. Without an item-list wrapper, title can seem a little misplaced, and it's the client code's responsibility to provide a wrapper (not a big deal, maybe preferred), but you don't have unnecessary wrappers around child lists.
Re: outer if... I wondered the same thing. But can't an empty item list conceivably be sent to the theme layer?
Comment #29
tstoecklerIt can (although, conceivably, that could be considered a bug), but then the for loop will simply loop over an empty set and nothing will happen (unless you set a title, in which case that will get set).
Disclaimer: I don't know a thing about Twig, so it might be that Twig actually throws notices or something, but I think that is how it *should* work.
Comment #30
tstoecklerI guess it would be nice if we could feasibly remove the title "feature" from theme_item_list() and let people print that on their own. That would definitely be a follow-up/own issue, though. Then I'd be more comfortable with removing the wrapper. I will definitely not stand in the way of it here, though. As you point out, nested lists with a bunch of divs in the middle, really are sucky...
Comment #31
jenlamptonI have another template that needs to call item-list so I committed it to front end.
I renamed it to item-list.html.twig
I moved it into the theme.inc directory
and I also removed the even/odd first/last classes - but did this in a separate commit incase we need to revert :)
Comment #32
sunRemoving the wrapper presents a serious regression. You cannot apply styles to the list heading anymore.
Comment #33
Crell CreditAttribution: Crell commentedsun: Whether it's a regression or a "dear god finally!" is up to the themers to decide, not backend devs like you and me.
IMO sane handling of an empty list is a requirement, because that simplifies code elsewhere. For a wrapping div (or some other block element), you and I don't have an opinion.
Comment #34
jenlamptonJust a note that I added the wrapper div back in with a @todo to remove later.
Comment #35
steveoliver CreditAttribution: steveoliver commentedWhy not do
<h3 class="item-list-title">
?Comment #36
steveoliver CreditAttribution: steveoliver commenteddidn't mean to change status.. suggestion/idea for later.
Comment #36.0
steveoliver CreditAttribution: steveoliver commentedFixing code block.
Comment #37
steveoliver CreditAttribution: steveoliver commentedAdded issue summary.
Note that current code allows children lists to be of different type than parent (i.e
ol > li > ul
), while oldtheme_item_list()
would always pass the parent list type down to each child. Is this a feature we want to keep? I think so.Gonna mark postponed until #1649780: Remove first/last/odd/even classes in favor of CSS3 pseudo selectors allows us to remove the special classes.
Comment #37.0
steveoliver CreditAttribution: steveoliver commentedSummarize issue.
Comment #37.1
steveoliver CreditAttribution: steveoliver commentedAdd a link to comment #32.
Comment #38
steveoliver CreditAttribution: steveoliver commentedAdded #1785310: Support custom child list types in theme_item_list() to issue summary.
Comment #39
steveoliver CreditAttribution: steveoliver commented<li class="">
attribute (in ba7dde6b).{{ attributes.class }}
before the rest of{{ attributes }}
in d78b9c98.Comment #40
steveoliver CreditAttribution: steveoliver commented1. d2af331: Added support for child list type in line with theme_item_list() supports custom list types for child elements.
2. 547d374: Cleaned up output code whitespace:
Before:
After:
...it's not perfect, but better.
Comment #41
steveoliver CreditAttribution: steveoliver commented356f631: Added explicit class="{{ attributes.class }}" back to item-list.html.twig, for easy-understandability-of-how-to-inject-our-own-classes, even though it'll ofter result in the class attribute being empty. :(
My thinking was that when we needed to add a class at the template level, we'd create a new template (i.e. in one of the template suggestions), and do this explicit class="" + attributes stuff there, not in stock item-list.html.twig.
Singling out an attribute, like class for example, for this kind of override/injection, should be one of the first thing any D8 Twig themer should learn and understand, instead of it being in the core item-list template (where we'll be **ensuring** at least some frequency of empty class attributes).
Comment #42
steveoliver CreditAttribution: steveoliver commentedQueue cleanup. Don't see any point in keeping this open or tagged. See core issues #1939062: Convert theme_item_list() to Twig.
Comment #43.0
(not verified) CreditAttribution: commentedLink to existing issue for adding 'type' support to list item children.