A question was recently raised on the APEX Community forum about how to create a custom card report template in order to render a repeating group of Cards. Several of my recent projects have involved navigation via Cards, so this one got me thinking. And actually, I could totally see this being useful in a number of applications, so I thought I’d give it a try!
First of all, the results. Please keep in mind that this is a single report region with multiple groups.
You can also see it in action here.
Full disclaimer, most of the inspiration for this came from Jorge Rimblas, who has written some fantastic posts about the power of Classic Reports. Be sure to visit his blog from some great inspiration and how-tos!
In the meantime, here is the full break-down of how I built my ‘House of Cards’ Card Report Template.
The query
I am not here to teach you Oracle Analytic functions, and in fact, I had to dig through my notes from KScope18 to remember what exactly Jorge had done in one of his custom templates. And yay me, I found them.
select dname header -- this is the repeating header ,ename card_title ,job card_subtitle ,'card text' card_text --anything you want for the card text ,'card sub text' card_subtext ,'fa-cloud' card_icon --any icon, as appropriaite ,null card_link --card link for drill through ,'t-Cards--animColorFill t-Cards--animRaiseCard t-Cards u-colors t-Cards--displaySubtitle t-Cards--featured t-Cards--block force-fa-lg t-Cards--displayIcons t-Cards--cols t-Cards--5cols t-Cards--animColorFill' card_list_modifiers -- this is to modify look and feel of cards, see below.. ,row_number() over ( partition by e.deptno order by ename) rn ,count(*) over ( partition by e.deptno ) total_rows -- this is basically the total number of employees in the department from eba_demo_chart_emp e, eba_demo_chart_dept d where e.deptno=d.deptno order by dname, row_number() over (partition by e.deptno order by ename)
This query yields these results:
Basically, the RN and TOTAL_ROWS columns tell me when a new repeating group starts (RN=1), and when it ends (RN = TOTAL_ROWS). I will use these conditions in my custom template.
New Custom Card Report Template
I started with a copy of the Report Template – Cards (Shared Components –> Templates –> Cards Report Template), which I then modified this way:
Modified Before and After Rows
I turned my template from a single Cards <UL>, to a <TABLE> containing ROWS of Cards. So my before and after rows open a <TABLE> tag rather than a <UL> tag
Before Rows:
<div class="t-Report" id="report_#REGION_STATIC_ID#"> <div class="t-Report-wrap"> <table class="t-Report-pagination" role="presentation"></table> <div class="t-Report-tableWrap"> <table class="t-Report-report">
After Rows:
</tbody> </table> </div> </div> </div>
Row Template 1
<tr><td> <h1> #HEADER# </h1> </td></tr> <tr><td><ul class="t-Cards #CARD_LIST_MODIFIERS#" #REPORT_ATTRIBUTES# id="#REGION_STATIC_ID#_cards" data-region-id="#REGION_STATIC_ID#"> <li class="t-Cards-item #CARD_MODIFIERS#"> <div class="t-Card"> <a href="#CARD_LINK#" class="t-Card-wrap"> <div class="t-Card-icon u-color #CARD_COLOR#"><span class="t-Icon fa #CARD_ICON#"><span class="t-Card-initials" role="presentation">#CARD_INITIALS#</span></span></div> <div class="t-Card-titleWrap"><h3 class="t-Card-title">#CARD_TITLE#</h3><h4 class="t-Card-subtitle">#CARD_SUBTITLE#</h4></div> <div class="t-Card-body"> <div class="t-Card-desc">#CARD_TEXT#</div> <div class="t-Card-info">#CARD_SUBTEXT#</div> </div> <span class="t-Card-colorFill u-color #CARD_COLOR#"></span> </a> </div> </li>
Basically, we are saying that this is the template to use every time we have a new repeating group (:RN=1), and it includes a row for #HEADER# (Our departments, in this example).
Row Template 2
<li class="t-Cards-item #CARD_MODIFIERS#"> <div class="t-Card"> <a href="#CARD_LINK#" class="t-Card-wrap"> <div class="t-Card-icon u-color #CARD_COLOR#"><span class="t-Icon fa #CARD_ICON#"><span class="t-Card-initials" role="presentation">#CARD_INITIALS#</span></span></div> <div class="t-Card-titleWrap"><h3 class="t-Card-title">#CARD_TITLE#</h3><h4 class="t-Card-subtitle">#CARD_SUBTITLE#</h4></div> <div class="t-Card-body"> <div class="t-Card-desc">#CARD_TEXT#</div> <div class="t-Card-info">#CARD_SUBTEXT#</div> </div> <span class="t-Card-colorFill u-color #CARD_COLOR#"></span> </a> </div> </li>
For these rows, we simply need to render the Card. It is neither the first record in the group, nor the last.
Row Template 3
<li class="t-Cards-item #CARD_MODIFIERS#"> <div class="t-Card"> <a href="#CARD_LINK#" class="t-Card-wrap"> <div class="t-Card-icon u-color #CARD_COLOR#"><span class="t-Icon fa #CARD_ICON#"><span class="t-Card-initials" role="presentation">#CARD_INITIALS#</span></span></div> <div class="t-Card-titleWrap"><h3 class="t-Card-title">#CARD_TITLE#</h3><h4 class="t-Card-subtitle">#CARD_SUBTITLE#</h4></div> <div class="t-Card-body"> <div class="t-Card-desc">#CARD_TEXT#</div> <div class="t-Card-info">#CARD_SUBTEXT#</div> </div> <span class="t-Card-colorFill u-color #CARD_COLOR#"></span> </a> </div> </li> </ul></td></tr>
This is the template for the last record (Card) of a group. It is almost identical to Row Template 2, except it includes closing tags for the <UL> (list of cards), and closes the row.
I had hoped that by starting with a copy of the CARDS template, it would pick up the ‘Template Options’ I set using the #COMPONENT_CSS_CLASSES# substitution string, but that didn’t seem to work. So I’ve handled the customization with my ‘CARD_LIST_MODIFIERS’ column in my query.
In the example above, in order to render a 5 columns, raised fill, color animation list of cards, I used: ‘t-Cards–animColorFill t-Cards–animRaiseCard t-Cards u-colors t-Cards–displaySubtitle t-Cards–featured t-Cards–block force-fa-lg t-Cards–displayIcons t-Cards–cols t-Cards–5cols t-Cards–animColorFill’
You can refer to other classes using the table below, though.
Create Report Region on page, using new Custom Card Report Template
In the Attributes section of your query, select your new template. As mentioned, for some reason the Template Options did not seem to work for me, which is why I added my new custom column (CUSTOM_LIST_MODIFIERS) to both the template and query. If you have a better solution, please do let me know!
I think NEXT LEVEL AWESOMENESS for this guy would be to wrap a list of cards within… a parent list of Cards :-) So using this example, Card 1 would be ACCOUNTING, with the employees rendered as cards in the body. Card 2 would be RESEARCH, etc. But hey, we just got hit with 40cm of snow and I’d much rather go play. But if any of you peeps manage it, hope you’ll share!! Sharing is caring, amiright? And from what I know about the APEX Community, I have no doubt you will go above and beyond.
Speaking of which, my colleague Jaydip Bosamiya just published an awesome post about making your cards FLIP. I mean, I don’t even care what those babies really say, I just want the flippin’ cards ;-)
Happy APEXing, friends!