Table/(Grid with read only data) with coloring in email notification

Can some suggest what is the best way to display a tabular data with coloring on rows in email body via a notification ?

Currently using ncolumntable() for table formation and used default html template in send email smart service for email body.

As the current requirement is to show the data in rows different color based on one of the column data (have some validations ) .I have tried using Ncoulmnstyledtable) , its not working .

Appian version is 16.3.

  Discussion posts and replies are publicly visible

Parents
  • +1
    Certified Lead Developer
    Hi @Soujanya as per my understanding, you can create an expression rule(The Parent Rule) which returns the <table> <tr> & <th> and create an another Expression Rule (The Child Rule) which accepts a rule input of type array, loop <td> tag along with item here using a!forEach(), where you can setup the color for table, tr and td tags.

    This Child rule must be called into Parent rule to form td (the dynamic content for the Table).

    Now try calling the Parent rule as body for the Send Email Node, this should accomplish your requirement.

    Hope this will help you.
  • Getting the following error while using apply() function.

    Problem: An invalid expression has been encountered in a task.
    Details: ERROR:An error occurred while evaluating expression: =rule!PRTRR_getEmailBodyByNotificationType( notificationType_txt: pv!notificationType_txt, PRTRR_PipelineList: pv!currentPipelineList, PRTRR_PipelinePrecandidateList: pv!currentPreCandidateList, AMU_txt: pv!filteredAMUList[pv!index_int].Description ) (Expression evaluation error in rule 'prtrr_createemailsectionrequireattention' (called by rules 'prtrr_createemailbodyrequireattention' > 'prtrr_getemailbodybynotificationtype') at function fn!apply [line 35]: A rule or function reference is expected as the 1st parameter.) (Data Inputs)

    =================================
    Parent rule to form a table shown below
    ===================================

    "<table style='width:100%' border='1'><tr bgcolor='blue'<th>License Number</th><th>Line Number </th><th>Last Producing UWI</th><th>Activity Type</th><th>LastProduction Date</th><th>Date of Non-Compliance</th><th>Days to Non-Compliance</th></tr>" & fn!apply(rule!PRTRR_CreateEmailBodySectionTable(PRTRR_PipelineList:ri!PRTRR_PipelineList),ri!PRTRR_PipelineList,0) & "</table><br></br><br></br> <font color='yellow'>YELLOW </font><legend>-> Inactive Lines Requiring a Decision</legend><br></br><font color='orange'>ORANGE </font><legend>-> Non-Compliance Imminent </legend><br></br><font color='red'>ORANGE</font><legend>-> Non-Compliant Pipelines </legend>"

    ======
    Childrule
    =======
    if(
    (if(ri!PRTRR_PipelineList.DateofNonCompliance > todate(now()),tointeger(ri!PRTRR_PipelineList.DateofNonCompliance-todate(now())),0
    )>97),
    "<tr bgcolor='Yellow'><td>ri!PRTRR_PipelineList.LicenseNumber</th><td>ri!PRTRR_PipelineList.LineNumber</td><td>ri!PRTRR_PipelineList.LastProducingUWI</td><td>ri!PRTRR_PipelineList.activity_type</td><td>rule!displayDateTimeListSimple(ri!PRTRR_PipelineList.LastProdDate)</th><td>rule!displayDateTimeListSimple(ri!PRTRR_PipelineList.DateofNonCompliance)</td><td>if(rule!lengthNullSafe(ri!PRTRR_PipelineList.DateofNonCompliance) = 0,null(),if(ri!PRTRR_PipelineList.DateofNonCompliance > todate(now()),
    tointeger(ri!PRTRR_PipelineList.DateofNonCompliance-todate(now())),0))</td></tr>",
    if((if(ri!PRTRR_PipelineList.DateofNonCompliance > todate(now()),tointeger(ri!PRTRR_PipelineList.DateofNonCompliance-todate(now())),0
    )<=97),"<tr bgcolor='Orange'><td>ri!PRTRR_PipelineList.LicenseNumber</th><td>ri!PRTRR_PipelineList.LineNumber</td><td>ri!PRTRR_PipelineList.LastProducingUWI</td><td>ri!PRTRR_PipelineList.activity_type</td><td>rule!displayDateTimeListSimple(ri!PRTRR_PipelineList.LastProdDate)</th><td>rule!displayDateTimeListSimple(ri!PRTRR_PipelineList.DateofNonCompliance)</td><td>if(rule!lengthNullSafe(ri!PRTRR_PipelineList.DateofNonCompliance) = 0,null(),if(ri!PRTRR_PipelineList.DateofNonCompliance > todate(now()),
    tointeger(ri!PRTRR_PipelineList.DateofNonCompliance-todate(now())),0))</td></tr>",
    if((if(ri!PRTRR_PipelineList.DateofNonCompliance > todate(now()),tointeger(ri!PRTRR_PipelineList.DateofNonCompliance-todate(now())),0
    )<=0),"<tr bgcolor='Yellow'><td>ri!PRTRR_PipelineList.LicenseNumber</th><td>ri!PRTRR_PipelineList.LineNumber</td><td>ri!PRTRR_PipelineList.LastProducingUWI</td><td>ri!PRTRR_PipelineList.activity_type</td><td>rule!displayDateTimeListSimple(ri!PRTRR_PipelineList.LastProdDate)</th><td>rule!displayDateTimeListSimple(ri!PRTRR_PipelineList.DateofNonCompliance)</td><td>if(rule!lengthNullSafe(ri!PRTRR_PipelineList.DateofNonCompliance) = 0,null(),if(ri!PRTRR_PipelineList.DateofNonCompliance > todate(now()),
    tointeger(ri!PRTRR_PipelineList.DateofNonCompliance-todate(now())),0))</td></tr>","")))


    Please suggest the way to loop and form the table row data .Also , need to sort the data based on column DateofNonCompliance
  • +2
    Certified Lead Developer
    in reply to Soujanya B

    Hi Soujanya B (soujanyab0001) i have tried to build a similar type of rule as yours to generate the template having table structure with dynamic data into their cells by considering apply() instead of a!forEach() as you are working with Appian 16.3, please find the source code below:

     

    Parent rule, which is needed to be invoke inside Email body:

    "<table style='width:100%' border='1'>
    <tr bgcolor='blue'>
    <th>License Number</th>
    <th>Line Number </th>
    <th>Last Producing UWI</th>
    <th>Activity Type</th>
    <th>LastProduction Date</th>
    <th>Date of Non-Compliance</th>
    <th>Days to Non-Compliance</th>
    </tr>
    "&apply(
      rule!PRTRR_CreateEmailBodySectionTable(
        PRTRR_PipelineList: ri!PRTRR_PipelineList, 
        index: _
      ),
      if(
          rule!APN_isEmpty(ri!PRTRR_PipelineList),
          {},
          enumerate(1 + length(ri!PRTRR_PipelineList))
        )
    )&"
    </table><br></br><br></br> 
    <font color='yellow'>YELLOW </font><legend>-> Inactive Lines Requiring a Decision</legend><br></br><font color='orange'>ORANGE </font><legend>-> Non-Compliance Imminent </legend><br></br><font color='red'>ORANGE</font><legend>-> Non-Compliant Pipelines </legend>"

    let's consider the rule input: PRTRR_PipelineList is of type CDT (Array) or Any Type here.

     

    The Child rule which is responsible to form <td> having dynamic content for it's cell let's say: PRTRR_CreateEmailBodySectionTable

    concat(
      "<tr>",
      "<td>"&index(index(ri!PRTRR_PipelineList, "lineNumber", null()), ri!index, null())&"</td>",
      "<td>"&index(index(ri!PRTRR_PipelineList, "uwi", null()), ri!index, null())&"</td>",
      "<td>"&index(index(ri!PRTRR_PipelineList, "activity", null()), ri!index, null())&"</td>",
      "<td>"&index(index(ri!PRTRR_PipelineList, "lastprod", null()), ri!index, null())&"</td>",
      "<td>"&index(index(ri!PRTRR_PipelineList, "nonComp", null()), ri!index, null())&"</td>",
      "<td>"&index(index(ri!PRTRR_PipelineList, "daysTo", null()), ri!index, null())&"</td>",
      "<td>"&index(index(ri!PRTRR_PipelineList, "daysToNonCompliance", null()), ri!index, null())&"</td>",
      "</tr>"
    )

    here we have 2 rule inputs: 

    ri!index                       -> Type ->  Number(Integer)

    ri!PRTRR_PipelineList    -> Type ->  CDT (Array) or Any Type

     

    You can modify the property names inside this Child rule as per your cdt properties such as: ActivityType, LastProductionDate etc.. and also you can have any conditional pre-check if required as per your business requirement.

     

    in my case: following is the input for the Parent rule, for demo: 

    {
    	{
    		lineNumber: 123,
    		uwi: "abc",
    		activity: "test",
    		lastprod: today(),
    		nonComp: today()+1,
    		daysTo: 2,
    		daysToNonCompliance: 6
    	},
    	{
    		lineNumber: 456,
    		uwi: "def",
    		activity: "test1",
    		lastprod: today()+5,
    		nonComp: today()+6,
    		daysTo: 6,
    		daysToNonCompliance: 17
    	}
    }

    which you need to change with your data, at later point of time.

     

    So at the end, when the parent rule executes, this generates your required response as shown below:

     

    Hope this will help you in building Dynamic Email Body for your scenario.

  • But , First row in table is always displaying as empty row and after calculating DaysOfNon-compliant - it was displaying the value appending with semicolon.

    Child rule

    concat(

    if(

    if(

    rule!lengthNullSafe(

      todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))

      ) = 0,

    0,

    if(

       todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),

    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

     

    )>97,"<tr bgcolor='yellow'>",if(

    and(if(

    rule!lengthNullSafe(

      todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))

      ) = 0,

    0,

    if(

       todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),

    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

     

    )<=97 ,if(

    rule!lengthNullSafe(

      todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))

      ) = 0,

    0,

    if(

       todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),

    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

     

    )>0),"<tr bgcolor='orange'>",if(

    if(

    rule!lengthNullSafe(

      todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))

      ) = 0,

    0,

    if(

       todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),

    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

     

    )<=0,"<tr bgcolor='red'>",""

    )

    )

    ),

     

      "<td>"&index(index(ri!PRTRR_PipelineList, "LicenseNumber", null()), ri!index, null())&"</td>",

       "<td>"&index(index(ri!PRTRR_PipelineList, "LineNumber", null()), ri!index, null())&"</td>",

      "<td>"&index(index(ri!PRTRR_PipelineList, "LastProducingUWI", null()), ri!index, null())&"</td>",

      "<td>"&index(index(ri!PRTRR_PipelineList, "activity_type", null()), ri!index, null())&"</td>",

      "<td>"&rule!displayDateTimeListSimple(index(index(ri!PRTRR_PipelineList, "LastProdDate", null()), ri!index, null()))&"</td>",

      "<td>"&rule!displayDateTimeListSimple(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))&"</td>",

      "<td>"&if(

    rule!lengthNullSafe(

      todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))

      ) = 0,

    0,

    if(

       todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),

    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

     

    )&"</td>",

      "</tr>"

    )

     

    passing the index as (_) from parent rule.

  • Hmm.But the first row in table is always displaying as empty row and daysofnon-compliant is displaying the value appending with ;.

     

    child rule as shown below. index  value passed as _ from parent rule.

    concat(

    if(

    if(

    rule!lengthNullSafe(

      todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))

      ) = 0,

    0,

    if(

       todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),

    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

     

    )>97,"<tr bgcolor='yellow'>",if(

    and(if(

    rule!lengthNullSafe(

      todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))

      ) = 0,

    0,

    if(

       todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),

    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

     

    )<=97 ,if(

    rule!lengthNullSafe(

      todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))

      ) = 0,

    0,

    if(

       todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),

    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

     

    )>0),"<tr bgcolor='orange'>",if(

    if(

    rule!lengthNullSafe(

      todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))

      ) = 0,

    0,

    if(

       todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),

    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

     

    )<=0,"<tr bgcolor='red'>",""

    )

    )

    ),

     

      "<td>"&index(index(ri!PRTRR_PipelineList, "LicenseNumber", null()), ri!index, null())&"</td>",

       "<td>"&index(index(ri!PRTRR_PipelineList, "LineNumber", null()), ri!index, null())&"</td>",

      "<td>"&index(index(ri!PRTRR_PipelineList, "LastProducingUWI", null()), ri!index, null())&"</td>",

      "<td>"&index(index(ri!PRTRR_PipelineList, "activity_type", null()), ri!index, null())&"</td>",

      "<td>"&rule!displayDateTimeListSimple(index(index(ri!PRTRR_PipelineList, "LastProdDate", null()), ri!index, null()))&"</td>",

      "<td>"&rule!displayDateTimeListSimple(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))&"</td>",

      "<td>"&if(

    rule!lengthNullSafe(

      todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))

      ) = 0,

    0,

    if(

       todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),

    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

     

    )&"</td>",

      "</tr>"

    )

     

     

  • But the first ron in table is always coming as empty row and value of dayofnon-compliant is appending with semicolon in all rows
    concat(
    if(
    if(
    rule!lengthNullSafe(
    todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))
    ) = 0,
    0,
    if(
    todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),
    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

    )>97,"<tr bgcolor='yellow'>",if(
    and(if(
    rule!lengthNullSafe(
    todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))
    ) = 0,
    0,
    if(
    todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),
    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

    )<=97 ,if(
    rule!lengthNullSafe(
    todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))
    ) = 0,
    0,
    if(
    todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),
    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

    )>0),"<tr bgcolor='orange'>",if(
    if(
    rule!lengthNullSafe(
    todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))
    ) = 0,
    0,
    if(
    todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),
    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

    )<=0,"<tr bgcolor='red'>",""
    )
    )
    ),

    "<td>"&index(index(ri!PRTRR_PipelineList, "LicenseNumber", null()), ri!index, null())&"</td>",
    "<td>"&index(index(ri!PRTRR_PipelineList, "LineNumber", null()), ri!index, null())&"</td>",
    "<td>"&index(index(ri!PRTRR_PipelineList, "LastProducingUWI", null()), ri!index, null())&"</td>",
    "<td>"&index(index(ri!PRTRR_PipelineList, "activity_type", null()), ri!index, null())&"</td>",
    "<td>"&rule!displayDateTimeListSimple(index(index(ri!PRTRR_PipelineList, "LastProdDate", null()), ri!index, null()))&"</td>",
    "<td>"&rule!displayDateTimeListSimple(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))&"</td>",
    "<td>"&if(
    rule!lengthNullSafe(
    todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))
    ) = 0,
    0,
    if(
    todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null())) > todate(now()),
    tointeger(todate(index(index(ri!PRTRR_PipelineList, "DateofNonCompliance", null()), ri!index, null()))-todate(now())),0)

    )&"</td>",
    "</tr>"
    )
  • But the first row in table is always displaying as empty row and value of dayOfnon-compliant is appending with semicolon at the end for all rows (i.e. daysofnon-compliant is calculate based on dateofnon-compliant).
  • 0
    Certified Lead Developer
    in reply to Soujanya B

    You can use String concatenation instead of using concat() function in Child rule like &" "& , to avoid semi-colon issue, if this do not work, try removing semicolon from the resultant String using text Function

  • Thanks! Finally completed my requirement with your help.
    Thanks for the quick replies.
Reply Children