Velocity - Looping Over Tables

Post any questions you have about using the Verj.io Studio, including client and server-side programming with Javascript or FPL, and integration with databases, web services etc.

Moderators: Dave, Jon, Steve, Ian

Jon Moore
Ebase User
Posts: 9
Joined: Thu Dec 29, 2011 9:43 pm

Velocity - Looping Over Tables

#1

Postby Jon Moore » Thu Sep 10, 2020 8:26 am

I have been following the instructions at https://resource-hub.verj.io/ebase/doc/Velocity.htm, creating a custom page layout that I want to access a table in the form. I've had problems, so I've tried to implement exactly the scenario in the instructions:

Code: Select all

<table>
#foreach ( $row in $CUSTOMER )
  <tr>
    <td>${CUSTOMER-NAME}</td>
    <td>${CUSTOMER-NUMBER}</td><td>${CUSTOMER-CREDIT}</td>
    <td>${CUSTOMER-PHONE}</td>
  </tr>
#end
</table>
There is a CUSTOMER table in the form, which has data in it (through a before form event) and displays correctly in the form body.

$CUSTOMER does not seem to exist as a variable accessible to Velocity. $tables and $tables.CUSTOMER do seem to exist, but $tables.CUSTOMER.rows does not seem to be a collection that Velocity can iterate.

Is the documented example still valid? Should it be working? If not, what's the up-to-date equivalent?

As a supplementary, related question: I like that the designer runs the template so that there's a decent wysiwyg view of the form. Obviously it can't render things that depend on runtime context, though. Is there a way to reliably detect in a Velocity template when it is running in the designer, rather than in the forms runtime, so that I can substitute decent-looking boilerplate?

Thanks,
Jon.
0 x

Jon
Moderator
Moderator
Posts: 1337
Joined: Wed Sep 12, 2007 12:49 pm

Re: Velocity - Looping Over Tables

#2

Postby Jon » Thu Sep 10, 2020 1:30 pm

Yes, this example works fine for me.
Which Verj.io version are you using?
How do you invoke Velocity?

Here is my example script (Javascript) which works on my system (V5.9). To run this you will need a form containing table CUSTOMER with columns NAME, NUMBER, PHONE and CREDIT and a visible field named RESULT set to display only with field "Contains HTML" checked.

Code: Select all

var t = tables.CUSTOMER;
t.insertRow();
t.NAME.value = "Jon";
t.NUMBER.value = "1";
t.CREDIT.value = 10000;
t.PHONE.value = "123 4567";
t.insertRow();
t.NAME.value = "Fred";
t.NUMBER.value = "2";
t.PHONE.value = "999 4567";

var template = "<table> " +
" #foreach ( $row in $CUSTOMER ) " +
"  <tr>" +
"    <td>${CUSTOMER-NAME}</td> " +
"    <td>${CUSTOMER-NUMBER}</td><td>${CUSTOMER-CREDIT}</td> " +
"    <td>${CUSTOMER-PHONE}</td> " +
"  </tr> " +
" #end " +
" </table>"
services.velocity.invokeTemplateFromInlineString(template);
fields.RESULT.value = VelocityServices.invokeTemplateFromInlineString(template);
I'm not sure I understand your second question about designer context. A Velocity template is executed as a result of a script statement and scripts are not executed when creating the designer's WYSIWYG view. Perhaps you could supply a bit more context.
0 x

Jon Moore
Ebase User
Posts: 9
Joined: Thu Dec 29, 2011 9:43 pm

Re: Velocity - Looping Over Tables

#3

Postby Jon Moore » Thu Sep 10, 2020 9:06 pm

Hi Jon,

I'm doing this in the Velocity template for a custom layout for a Page property set within a presentation template, not invoking it from a script. I'm trying to make the layout template do things diferently based on data in fields or tables (I may also try accessing session data - I'm tinkering to see what approaches are possible).

Hence my question about the designer - the Velocity template is obviously run to generate the designer view of the layout, which I wasn't expecting, but is a nice feature. But variables which are bound when the template runs in the form engine (e.g. $tables) just appear unchanged - "$tables" in the designer view. So I'd like to be able to substitute some boilerplate for the dynamic content when the template runs inside the designer. I've just tried it with a simple conditional on a variable which is only present at runtime:

Code: Select all

#if ($pages)
<h1>Live Form</h1>
#else
<h1>Designer</h1>
#end
This works as I expected - I see "Live Form" when I run the form, but "Designer" in the designer view. But I'm wondering if there is a 'proper' way for the template to test if it's running in the designer.

This is all in 5.9.0.
0 x

Jon
Moderator
Moderator
Posts: 1337
Joined: Wed Sep 12, 2007 12:49 pm

Re: Velocity - Looping Over Tables

#4

Postby Jon » Fri Sep 11, 2020 9:52 am

Ah, I see, sorry about the misunderstanding. The interface to Velocity works differently when it's invoked from a script - it has a specific API that is custom built for just this situation. When invoked for a custom layout the standard Javascript API is provided - the same as you have in a Javascript script i.e. form, fields, tables etc, but as you've noticed this is only available in the runtime environment, and is not available in the designer.

I'm afraid there isn't a formal isDesigner() check provided. We could probably hack something together to do this, but your current check for $pages (or maybe $form?) seems fine to me. If we were to change this in the future we would probably make $system available to both the runtime and the designer and add an isDesigner() method to this.
0 x

Jon Moore
Ebase User
Posts: 9
Joined: Thu Dec 29, 2011 9:43 pm

Re: Velocity - Looping Over Tables

#5

Postby Jon Moore » Fri Sep 11, 2020 12:49 pm

This works. I get the proper data displayed in the form, and the boilerplate in the designer.

I think I probably need to support 3 cases, though:
  • 'Live' running, and there is a table with data.
  • 'Live' running, and the table does not exist, or has no data.
  • Running in the designer.
So it probably needs another conditional based on a top-level variable which is always present in a form, to be properly robust. But it'll do for now.

Code: Select all

#if ($tables.CUSTOMERS)
<table>
#foreach ($i in [1 .. $tables.CUSTOMERS.rowCount])
#set ($tables.CUSTOMERS.currentRow = $i - 1)
  <tr>
    <td>${tables.CUSTOMERS.NAME.value}</td>
    <td>${tables.CUSTOMERS.NUMBER.value}</td>
    <td>${tables.CUSTOMERS.PHONE.value}</td>
  </tr>
#end
</table>
#else
<table>
#foreach ($i in [1 .. 2])
  <tr>
    <td>Customer $i name</td>
    <td>Customer $i number</td>
    <td>Customer $i phone</td>
  </tr>
#end
</table>
#end
	
## Loop through all child controls
#foreach( $ctrl in $child_controls )
   ## Write out any pending messages
   $MESSAGES
   ## Only write a control if it's not hidden
   #if ( $ctrl.showing )
      $CONTROL
   #end
#end
0 x

Jon
Moderator
Moderator
Posts: 1337
Joined: Wed Sep 12, 2007 12:49 pm

Re: Velocity - Looping Over Tables

#6

Postby Jon » Fri Sep 11, 2020 2:38 pm

Congratulations! I did wonder how you would get around the missing while statement in Velocity. The table toJSON() method might be useful as well.
0 x

Jon Moore
Ebase User
Posts: 9
Joined: Thu Dec 29, 2011 9:43 pm

Re: Velocity - Looping Over Tables

#7

Postby Jon Moore » Mon Sep 14, 2020 1:44 pm

The lack of 'while' does seem a very strange omission in the language.
0 x


Who is online

Users browsing this forum: No registered users and 3 guests