Loop through a list of items

If you have an advanced integration setup that is sending data in an array or JSON object, you can setup loops in your document to print that information. Examples include line items on an invoice or a list of items for a report.

From a technical standpoint, your data will look like this for HTTP POST (name/value pairs)

items[0][Name]=Shirt&items[0][Quantity]=2&items[0][Price]=19.95&items[1][Name]=Jeans&items[1][Quantity]=3&items[1][Price]=89.99

Or JSON:

{"items": [{"Name":"Shirt", "Quantity":"2", "Price":"19.95", "Total":"39.90"},{"Name":"Jeans", "Quantity":"3", "Price":"89.99", "Total":"269.97"}]}

Create a Simple List

If you have a list of items that you would just like to print in your document, you can use the following {foreach} code

{foreach from=$items item=_row}
{$_row.Quantity} x {$_row.Name} = ${$_row.Price|number_format:2}
{/foreach}

Create a Table

If you're generating an invoice, you're probably looking to create a table that loops through each of the line items and prints them on a new row in the table.  No problem!  We've added a new "tablerow" tag that you can use and it tells our system to treat the table row as a loop.  The tag works exactly like a "foreach" loop, so all you need to do is use "tablerow" instead of "foreach" and we'll handle the rest.

Here's an example:

Name Quantity Price Total
{tablerow from=$products item=_product}{$_product.Name} {$_product.Quantity} {$_product.Price} {$_product.Total}{/tablerow}

The data that you send through to a table row loop needs to be an array (ie and array of products) that can be easily looped through.  The part after the dot in the variable name is the individual property for that element in the array.

If you'd like to skip items in the list, you can use an if statement in your table like this:

Name Quantity Price Total
{tablerow from=$products item=_product}{tableif $_product.Type == 'shirt'}{$_product.Name} {$_product.Quantity} {$_product.Price} {$_product.Total}{/tableif}{/tablerow}

 

Create a Bulleted/Numbered List

You can also use special "listrow" code to loop through items in a list to create a bulleted list.  Like this:

  • {listrow from=$products item=_product}{$_product}{/listrow}

 

Sort Your List

If you would like to sort your items based on a certain "key" you can do so using the "sort" modifier in this format "|sort:[KEY]:[asc or desc]" (asc is default). For example:

{foreach from=$items|sort:"Name" item=_row}
{$_row.Quantity} x {$_row.Name} = ${$_row.Price|number_format:2}
{/foreach}

To sort by price in descending order:

{foreach from=$items|sort:"Price":"desc" item=_row}
{$_row.Quantity} x {$_row.Name} = ${$_row.Price|number_format:2}
{/foreach}

 

Sort By Multiple Fields

If you would like to sort your items based on multiple keys, you can do so using the "multisort" modifier in this format "|multisort:[KEY1]:[asc or desc]:[KEY2]:[asc or desc]" (asc is default). For example:

{foreach from=$items|multisort:"Name":"asc":"Price":"desc" item=_row}
{$_row.Quantity} x {$_row.Name} = ${$_row.Price|number_format:2}
{/foreach}
Have more questions? Submit a request

104 Comments

  • Avatar
    Jeremy Clarke

    Hi John,

    Yes, but you'll need to send over all the data in the same JSON request (you'll have multiple arrays inside your JSON)

  • Avatar
    John Williams

    That is what I had in mind. Looks like I have some prototyping to do if the customer will approve the budget.
    Thanks Jeremy.

  • Avatar
    Douglas Jerum

    In Podio I have an array (a single field) that is a preset list of choices. You can select multiple choices. Each choice has a set value that comes over in the array (i.e. {$fitness_competition_within_5_minute_drive.0} would be the first one; .1 is the second and so on). I want the merge to list all of the selected ones in my document. Is there a way to do this without just inserting a merge field for each one so that if I add other selections in the future I dont have to update the merge document? Code I tried is below:
    {foreach from=$fitness_competition_within_5_minute_drive item=$fitness_competition_within_5_minute_drive}
    {$fitness_competition_within_5_minute_drive.}
    {/foreach}

  • Avatar
    Jeremy Clarke

    Hi Douglas,

    You're very close, but you need to make sure that you name the "item" inside your loop a different variable name than the array merge fields. Try this code:

    {foreach from=$fitness_competition_within_5_minute_drive item=_competition}
    {$_competition}
    {/foreach}

  • Avatar
    Douglas Jerum

    Its not working. Comes up blank. Here is the code:
    {foreach from=$fitness_competition_within_5_minute_drive item=$_competition}
    {$_competition}
    {/foreach}

    Here is an example of an item withing the array:

    {$fitness_competition_within_5_minute_drive.0}

  • Avatar
    Jeremy Clarke

    Hi Douglas,

    You didn't use my example :) There should not be a "$" sign in the "item" part of your foreach loop. It should be:

    {foreach from=$fitness_competition_within_5_minute_drive item=_competition}

  • Avatar
    Peter O' Brien

    Hi Jeremy,

    I have my data coming from a JotForm InfiniteList via Zapier. It looks like this in Zapier:

    prevEmploy: [{"Employer":"Employer 1","Title":"Title 1","Location":"Location 1"},{"Employer":"Employer 2","Title":"Title 2","Location":"Location 2"}]

    The loop {listrow from=$prevEmploy item=row} {$row.Employer} {$row.Title} {$row.Location} {/listrow} should print out the Employer, Title and Location but just prints "[ ["

    I'm unsure why this is not working, any help will be greatly appreciated.

  • Avatar
    Jeremy Clarke

    Hi Peter,

    If the value of a merge field is being sent over as JSON, you'll need to use the Field Map (https://support.webmerge.me/hc/en-us/articles/206526086-Field-Map) so that we can detect the JSON and parse it.

    Simply enter your merge field inside the Field Map box under your merge field name and that should do it! Like this:

    {$prevEmploy}

  • Avatar
    Markwood Fields

    Hey Jeremy,
    Been using this a while with a lot of success. Hitting an error right now where it seems to error out when I have carriage returns/line breaks in the data coming in.

    Have tried the same record w/o carriage returns and works fine, but as soon as one is added it errors out. The result is a table with just "[" in every field.

    Any ideas what to do here?

  • Avatar
    Jeremy Clarke

    Hi Markwood,

    Return carriages are not allowed in JSON, so you need to use a \r instead. Can you try that for me?

  • Avatar
    Markwood Fields

    Hey Jeremy, this seemed to do the trick. Thanks for your help!

  • Avatar
    William Greenberg

    Hi Jeremy, im new here, but this is a very cool product. im using webmerge through zapier which is pulling from airtable. would this configuration allow me to create a table (like for a multi-sale invoice)? thank you very much for your help.

  • Avatar
    Jeremy Clarke

    Hi William,

    I know Airtable/Zapier have been trying to improve their integration to send data from related tables in Airtable, but I'm not 100% sure if they've been able to get that done yet. Please reach out to their team for clarification.

    Thanks!

  • Avatar
    Dan Bailey

    Hi Jeremy,

    I've been using Airtable + Zapier + WebMerge successfully for a few months now. It works pretty well. To answer William's question, I have a similar challenge. To get around it, I download the tab from AirTable as a .csv and then host it in Box. After generating a shareable link, I use a "sync" base in AirTable (where all my parameter values are captured and updated) to input the link and then generate a new document using the .csv URL for table generation.

  • Avatar
    William Greenberg

    Dan, im not sure i understand the steps; would you mind emailing me privately in more detail? wgreenberg1@me.com. thank you.

  • Avatar
    William Greenberg

    Jeremy, i think I've made some progress. I created a JSON - style array from within Airtable. it looks like this:

    {"Products" : [{"Description": "PENDANT", "Price": "9695"},{"Description": "RING", "Price": "9870"}]}

    Then in web merge i create a 2-column table that looks like this:

    In column 1:
    {tablerow from=$Products item=_product}
    {$_product.Description}

    In column 2:
    {$_product.Price}
    {/tablerow}

    Furthermore, i turn on the Field Map, and under _product, i give the names {$Description} {$Price}.

    Then in zapier, i assign $Products that JSON-style text string i wrote initially.

    the resulting table that i get is blank, except for an "open curly bracket" in each cell.

    any insight is appreciated. thank you.

  • Avatar
    Jeremy Clarke

    Hi William,

    For the JSON, you just need to send over the list items, not the name of the field. So it should be:

    [{"Description": "PENDANT", "Price": "9695"},{"Description": "RING", "Price": "9870"}]

    Then, when Zapier sends over JSON, they encode it as text, so you'll need to pass the field through the Field Map so we can convert it to JSON. Simply put {$Products} in Field Map under Products: and that should do it!

  • Avatar
    Gerod Carfantan

    Hey Jeremy - any chance I can do a loop that spans multiple pages in PowerPoint like you describe above in Word?

    I tried to import a PPTX template where the {/foreach} tag was in the second page but I got an import error ( "unclosed {foreach} tag")

  • Avatar
    Jeremy Clarke

    Hi Gerod,

    Unfortunately, we do not have a way to span the foreach loops over multiple slides. Sorry!

  • Avatar
    Brian Hage

    Hi Jeremy,

    I want to use this feature to build custom reports for my clients. I am going to using Globiflow to build the JSON, but is it possible to hide certain columns of the template depending on what columns have data? I can put if(empty) in the column or how would that work in my excel template I upload?

  • Avatar
    Jeremy Clarke

    Hi Brian,

    Unfortunately, we don't have a way to dynamically hide columns. If you were using a Word document, you could setup different tables (with diff columns) then use logic to determine which one to show.

  • Avatar
    Forrest Chamberlain

    Is there a way to limit the results of an array... meaning I only want to list 4 items, then add ", ET AL" after the limited result, even if there is 20 in the array.
    Also, is there a way to count the array and display the row # of the item in the array being listed? Thanks for your assistance!

  • Avatar
    Jeremy Clarke

    Hi Forrest,

    You can limit the number of items in your loop using array_slice() like this:

    {foreach from=array_slice($products, 0, 4) item=_product key=_key}

    Also, you'll notice I used "key" in the loop too, and you can print the item number like: {$_key + 1} (it starts at "0" so you need to add 1)

  • Avatar
    Forrest Chamberlain

    Okay perfect!

    Now I have a worse problem. (I just posted this, but the post gone :( )

    My arrays come from FormAssembly, and WebMerge treats the attributes as separate arrays. For example:

    product 1:
    shirt-size[0]=
    shirt-color[0]=
    shirt-logos[0]0]=
    shirt-logos=[0][1]=

    product 2:
    shirt-size[1]=
    shirt-color[1]=
    shirt-logos[1]0]=
    shirt-logos=[1][1]=

    So, I end up with separate arrays like:
    shirt-size
    shirt-color

    Make sense? Any idea for a work around?

    Edited by Forrest Chamberlain
  • Avatar
    Jeremy Clarke

    Hi Forrest,

    You can use that "key" to reference the other fields like this:

    {foreach from=$shirt_sizes item=_size key=_k}
    Size: {$_size}
    Color: {$shirt_colors[$_k]}
    Logos: {$shirt_logos[$_k]}
    {/foreach}

  • Avatar
    Forrest Chamberlain

    ahhh, of course. Your a damn genius. I suppose WebMerge pretty much adapts php language? So I do $counts on arrays, etc, as well? Limit implodes, etc?

  • Avatar
    Forrest Chamberlain

    Okay, I guess I have just a few more questions and I should be set...
    1. Can I create arbitrary variables for use in the form? Like, I want to limit an array's result and store than in a new array, but then implode it.
    2. I want to count arrays prior to listing and store it in a var.
    Sorry to haggle you. I've dug around trying to find these answers.

  • Avatar
    Jeremy Clarke

    Hi Forrest,

    Yes, it's like PHP :)

    1. Yes, you can create any field/variable you want in your document. I suggest using a field with an underscore at the beginning like {$_new_array} so know it's an internal variable

    2. Yes, you can do this: {$_count = count($products)}

  • Avatar
    Belinda Whitfield

    Hello Jeremy,

    Has the feature to use arrays in excel been added?

  • Avatar
    Jeremy Clarke

    Hi Belinda,

    Yes, you can now use {tablerow} inside of Excel :)

Please sign in to leave a comment.
Powered by Zendesk