PHPTAL Dynamic Table Generation - template-tal

I find myself creating various tables for tabular data quite a bit, and would like to create a macro that can dynamically create tables based on a data structure defined in the calling template (not in the PHP code). Here's a simplistic example:
<!-- Define the macro -->
<tal:block metal:define-macro="table">
<table>
<tr tal:repeat="row data">
<td tal:repeat="col row" tal:content="col" />
</tr>
</table>
</tal:block>
<!-- Use the macro -->
<tal:block tal:define="data ???" metal:use-macro="table" />
What I'm looking for is how to define data (an array structure) from within PHPTAL itself. The reason I can't define this as a template variable in PHP (ex. $tpl->data = array(...)) is because the order and layout of the data belongs in the template. So, for example, if I wanted to flip the X and Y axes of the table, I should only have to modify the template, not the PHP code.
Edit:
To give an example, say I have arbitrary template variables foo, bar, and baz. I can use these in the templates like so:
<span tal:content="foo" /><br />
<span tal:content="bar" /><br />
<span tal:content="baz" />
How can I construct these variables into a two-dimensional data structure of rows and columns which I can then feed into a table-generating macro? Something like this (note: this doesn't actually work):
<tal:block tal:define="data [foo, bar; baz]" metal:use-macro="table" />
Where the desired output from the macro would be:
<table>
<tr>
<td>foo</td>
<td>bar</td>
</tr>
<tr>
<td>baz</td>
</tr>
</table>
And later on, if I wanted to swap the positions of foo and bar, I'd only need to modify the template and change the definition of data to data [bar, foo; baz].

You should probably use helper methods, e.g. either php:transpose_table(input_data) or wrap it in a TALES function:
function phptal_tales_transposed($expr, $nothrow) {
return 'transpose_table(' . phptal_tales($expr, $nothrow) . ')';
}
<tal:block tal:define="data transposed:input_data" metal:use-macro="table" />
Transposition or sorting in PHPTAL itself would be unnecessarily complicated (PHPTAL is not XSLT :)
Answer to edit :)
If you want to combine multiple variables into array, then use:
<tal:block tal:define="data php:array(foo, bar, baz)" metal:use-macro="table" />
array_chunk() function may be useful if you want to have certain number of columns.
and if you like custom syntax, then write phptal_tales_… function that translates your […] syntax to PHP code.

For a Generic Table Generation: PHPTAL: Repeat Column headers and values
<table>
<thead>
<tr>
<th tal:repeat="r results/0">${repeat/r/key}</th>
</tr>
</thead>
<tbody>
<tal:block tal:repeat="r results">
<tr>
<td tal:repeat="t r">${t}</td>
</tr>
</tal:block>
</tbody>
</table>

Related

Find and Replace with Visual Studio Code to replace cell alignment with a class

Since the align attribute is considered obselete I am cleaning up code to remove it and replace with a CSS class. I'm trying to determine if there is a way to do this using find and replace (or something else) in VS Code.
As an example, I might have some html that looks like this:
<table>
<tr>
<td align="left" class="someclass" id="mainTitleCell" title="Title1">Title1</td>
<td align="center" title="Title2">Title2</td>
<td class="someclass" align="right" title="Title3">Title3</td> <!-- attributes are not always in the same order -->
</tr>
<tr>
<td align="left">Title</td>
<td align="center">Title</td>
<td align="right">Title</td>
</tr>
</table>
which I would like to change to
<table>
<tr>
<td class="left someclass" id="mainTitleCell" title="Title1">Title1</td>
<td class="center" title="Title2">Title2</td>
<td class="right someclass" title="Title3">Title3</td>
</tr>
<tr>
<td class="left">Content</td>
<td class="center">Content</td>
<td class="right">Content</td>
</tr>
</table>
Basically removing the align attribute and either adding a class attribute with a specific value OR adding a specific value to an existing class attribute. Is there a way to do this with the Edit...Replace option in VS Code? I know I can find based on a regex but not sure how I would go about the replace since this becomes
Find the align tag
Remove it
Find a class attribute in the <td> or <th> tag and add the appropriate class
If there is no class attribute, add one with the appropriate class.
Obviously step #1 & 2 are easy, it's #3 & 4 that I'm not sure of. I'd be totally happy with having to run 3 separate find and replace commands (one for left, center and right).
Do I have any options here (I am open to extensions)?
UPDATE:
#Mark had the right answer and I was able to chain together several find and replace commands using the Replace Rules extension. With that I can open a file, run a single keystroke to find and replace everything and clean up the extra spaces in the class attribute.
Try this:
Find: align="(.*?)"(.*?) class="(.*?)"|class="(.*?)"(.*?) align="(.*?)"|align="(.*?)"
Replace: class="$7$1$6 $3$4"$2$5
See regex101 demo.
I'm a little surprised this works as well as it does (I included a couple of other test cases you didn't). The only issue (thus far...) is that it can result in one stray space, as in:
<td class="left ">Title</td> // only happens when there is no class attribute
as you can see in the demo page. You could then search for " and replace with just ". It could be handled by a conditional replacement but vscode find/replace doesn't allow those.
To some degree attributes will be re-ordered so that the class attribute is first, but not always - you didn't mention that as a concern - any attribute that occurs before either the first class or align attribute will not be moved. Otherwise, attributes like id or title if they are between class<->attribute (in any order) will be put last.

Cannot define Row Class to Footable

I'm using jQuery Footable V3 and trying to using the class="" attribute to but when the table is initialized the class attr desappear
Here is my example
<table class="table footable " data-page-size="20" data-paging="true" data-filtering="true" data-sorting="true" ">
<thead>
<tr>
<th data-type="number" data-breakpoints="all" >ID</th>
<th data-sortable="false" data-filterable="false" data-formatter="formatter">X</th>
<th data-type="text" data-sortable="true"><?=_("Descripción")?></th>
</tr>
</thead>
<tbody>
<tr class="danger">
<td>1111111111111</td>
<td>1111111111111</td>
<td>1111111111111</td> </tbody> </table>
Not only class attributes are removed from static tables (where HTML was already created before footable() call) but also HTML inside TD (links, images etc.) with current version 3.0.1.
The current FT 3 documentation is misleading concerning "static" tables, partially wrong.
Have a look at Github issues. You'll find some related to your question.
The best way to avoid this behavior is to load table rows and columns via JSON strings e.g. created with PHP.
Or to use workarounds. Use attributes like data-myclass="danger" and convert it via JQuery to class attribute after FT has been initialized. Sometimes slow!
Or use FT 2 until developers post a statement or maybe new version 3.

MailChimp custom template problems with drag and drop blocks

I am creating a custom MailChimp template but having issues when using the mc:repeatable element. I have it on a wrapped around a block of code and in the editor when creating a campaign, it works fine, I can spawn a new version of the parent block and move it around in the template, but when I preview, or send the email, the child block that was spawned from the parent sits below it's parent and not where I have placed it following spawning it from the parent block... Something seems to be wrong? (Heavily simplified) Code example below... Anyone any ideas on the fix???
<!-- BLOCK A -->
<div style="width:100%" mc:repeatable="CONTENTBLOCK_A">
<p>This is block A</p>
</div>
<!-- end of BLOCK A -->
<!-- BLOCK B -->
<div style="width:100%" mc:repeatable="CONTENTBLOCK_B">
<p>This is block B</p>
</div>
<!-- end of BLOCK B -->
So, when creating a new campaign, if I duplicate BLOCK A and position the duplicated BLOCK A below BLOCK B - in the preview within the campaign editor it looks fine, but when I click to view it into PREVIEW MODE, or send a PREVIEW EMAIL - the duplicated BLOCK A sits above BLOCK B and below its original spawned parent BLOCK A element...
Any ideas? Are the HTML COMMENTS (e.g. < !-- --> ) The issue perhaps?
Very late, but I was able to find success using this:
<table mc:repeatable="content" mc:variant="variant_1">
<tr>
<td mc:edit="section_1">
Variant 1 Content
</td>
</tr>
</table>
<table mc:repeatable="content" mc:variant="variant_2">
<tr>
<td mc:edit="section_2">
Variant 2 Content
</td>
</tr>
</table>
<table mc:repeatable="content" mc:variant="variant_3">
<tr>
<td mc:edit="section_3">
Variant 3 Content
</td>
</tr>
</table>
More in depth explanation can be found here: Create Editable Content Areas with MailChimp’s Template Language

Parent Parent Next Child Child selector?

How can I simplify this jquery selector?
$(this).parent().parent().next().children().children().slideDown('normal');
It works but I want to learn on how to improve it.
Thanks!
:)
Update: Here is the code sample.
<tr>
<td><div class="LinkHeader accordionButton">TRAINING VIDEOS<span class="right"></span></div></td>
</tr>
<tr>
<!-- Content of Training Videos -->
<td align="left" valign="top">
<div class="accordionContent"> (I want to select this div)
<div id="yunero"></div>
</div>
</td>
</tr>
According to your markup, it would be shorter (and arguably clearer) to write:
$(this).closest("tr").next().find(".accordionContent").slideDown();
Here, closest() will walk the ancestor chain and match the first <tr> element it finds, then next() will match its immediate next sibling, and finally find() with a class selector will return the <div> element you're interested in.
In passing, note that strictly speaking there is no "normal" duration for animations. The only supported strings are "fast" (200 ms) and "slow" (600 ms), and any other string will be interpreted as 400 ms, as if you had omitted the argument altogether. For instance, slideDown("superFast") will behave exactly the same as slideDown("normal") or plain slideDown().

vb.net inline IF with OR... not evaluating

I'm working on a small problem where I'm trying to show/hide a panel based on two criteria
A specific data field must not be blank
The specific data filed must also not equal "Not Relocatable"
Unfortunately this doesn't seem to be working for me (note that setting either one or the other criteria works just fine.)
<asp:Panel runat="server" Visible='<%#If(Not String.IsNullOrEmpty(DataBinder.Eval(Container.DataItem, "_236")) Or Not DataBinder.Eval(Container.DataItem, "_236") = "Not Relocatable", True, False)%>'>
<tr>
<td>
</td>
<td class="align-right lightgreen">
Buyer would consider relocating a business, if it is:
</td>
<td>
</td>
<td colspan="3">
<%#DataBinder.Eval(Container.DataItem, "_236")%>
</td>
<td>
</td>
</tr>
</asp:Panel>
Can anyone lend a hand to rectify this problem for me?
The syntax <%# %> is a data binding syntax, not an inline expression syntax. You cannot use procedural code inside of it like you can in the inline code <% %> tags.
Data binding tags must contain a single Eval or Bind function. If you need to do conditional branching based on those functions, you will need to do it using inline code around the binding tags.