Profile Picture of the author

Jinja in SnapApp

on 09-19-2024 12:00 AM by SnapApp by BlueVector AI

531

Jinja is a versatile template engine for Python, enabling the creation of dynamic web content by combining static data with object-driven elements. Within SnapApp, Jinja facilitates the seamless integration of structured data into templates, ensuring robust and adaptable presentations. For more comprehensive syntax details, please visit Jinja Documentation.

Table of Contents

Delimiters in Jinja

In Jinja, delimiters are used to differentiate template code from standard content, ensuring proper parsing and rendering of dynamic data. These delimiters help instruct Jinja on how to process specific sections of the code.

Delimiter Purpose Example
{{…}} Outputs the enclosed content in the template. {{ customer[‘full_name’] }}
{%…%} Handles Jinja control statements without producing output. {% set x = 42 %}
{#…#} Adds comments to the code for clarity, not rendered in output. {# Documenting SnapApp features #}

Variables in Jinja

Variables in Jinja are used to store and access dynamic data. They are automatically typed, allowing for flexible use without requiring manual type declarations. Meaningful variable names contribute to clearer, more maintainable templates, reducing potential for errors.

{% set totalOrders = 150 %}
{{ totalOrders }}

Output: 150

Variables can also be conditionally defined, enabling more tailored content:

{% set displayName = user.first_name | title if user.first_name else "SnapApp Invalid User" %}

Variable data types

Jinja supports a variety of data types commonly used in web applications. Understanding these types allows for effective manipulation of variables within SnapApp.

Data Type What does it represent Example Values
None Indicates the absence of value. none or None
Integer Represents whole numbers. 100, 54, 3021
Float Decimal-based numbers. 12.34, 3.75, 42.5, 3.1415, 15.0
String Sequence of characters. “status”, “SnapappUser”, “SnapApp is Cool”
Boolean True or False values. true or True, false or False
List Mutable array, enclosed in [ ]. Can contain any data type [1, ‘string’, [ [ ], [ ] ], { 1: ‘a’ }, none ]
Tuple Immutable sequence, enclosed in ( ). Can contain any data type (1, ‘string’, [ [ ], [ ] ], { 1: ‘a’ }, none )
Dictionary Key-value pairs, enclosed in { }. Value can be any data type. { ‘name’: ‘John’, ‘role’: ‘Admin’ }

Jinja allows conditional logic to be embedded in templates, enhancing the ability to dynamically adjust output based on variable states.

{% if project.status == 'pending' %}
  {{ "Project is pending." }}
{% elif project.status == 'deal qualified' %}
  {{ "Order has qualified the deal." }}
{% else %}
  {{ "Order is complete." }}
{% endif %}

Expressions

Expressions in Jinja provide the means to perform calculations, make comparisons, and manipulate data. Built on familiar Python syntax, they are intuitive and powerful for creating responsive content in SnapApp templates.

  • Math Operators

  • + Addition

  • - Subtraction
  • * Multiplication
  • / Division
  • // Integer division, rounding down. e.g. 20 // 7 returns 2.
  • % Modulus (remainder of division). For instance, 11 % 7 returns 4.

  • Comparison Operators

  • == Checks for equality.

  • != Checks for inequality.
  • > Greater than comparison.
  • >= Greater than or equal to.
  • < Less than comparison.
  • <= Less than or equal to.

  • Logical Operators

  • and Boolean AND operator.

  • or Boolean OR operator.
  • not Boolean negation.

Other Operators

  • in : This operator checks whether a value exists within a sequence (like a list or dictionary). It evaluates based on keys in dictionaries.

Jinja {{ 'status' in order }} {# Checks if 'status' key exists in the order dictionary #}

  • is : The is operator applies various tests to validate data, such as determining if a variable is defined or of a certain type.

Jinja {{ order.total is number }} {# Checks if the total is a number #}

  • ~ : The ~ (read as “tilde”) operator concatenates operands, converting them into strings before joining them.

Jinja {{ 'Total: ' ~ order.total }} {# Produces "Total: 500" if order.total is 500 #}

Jinja Data Structures

In SnapApp, Jinja offers several core data structures that allow for flexible and dynamic template creation. Lists, tuples, and dictionaries are key elements, enabling the manipulation and organization of data for rendering dynamic content efficiently.

  • Lists : A list is a mutable, ordered collection of items that can hold diverse data types without requiring prior declaration. Lists in Jinja are ideal for managing sequences of objects or values, allowing for flexible additions, removals, and retrieval of data.

How to initialize and access items in a list?

To define a list in Jinja, use the following syntax:

{% set sampleList = [10, 20, 30, 40, 50] %}

This creates a list containing the numbers 10 through 50. An empty list can be initialized as:

{% set emptyList = [] %}

Lists are indexed, meaning each item in the list can be accessed by its position. The first element in any list is always at index 0, the second at index 1, and so on. For instance, in the list below:

{% set alphaList = ['X', 'Y', 'Z', 'A'] %}
  • X is at index 0,
  • A is at index 3.

To retrieve an item, use the index:

{{ alphaList[2] }}  {# Returns 'Z' #}

Adding and removing items from a list

Lists in Jinja allow for dynamic changes, such as appending or removing elements.

Appending Elements: To add an item to an existing list, use the append method.

{% set myList = [2, 4, 6] %}
{% append 8 to myList %}

This adds 8 to the end of myList, resulting in [2, 4, 6, 8].

Removing Elements: Use the pop() method to remove elements from a list. The method can be used with or without an index.

{% set temp = myList.pop(1) %}  {# Removes the second item in the list #}

Example:

{% set myList = [100, 200, 300, 400] %}
{% set temp = myList.pop(2) %}  {# Removes 300 from the list #}
{{ myList }}  {# Outputs [100, 200, 400] #}

To find an element’s index, use the index() function:

{% set myIndex = myList.index(400) %}
  • Tuples : Tuples, similar to lists, are ordered collections but are immutable—once created, they cannot be changed. Tuples are often used for fixed data that should remain constant throughout template rendering.

How to create and access Tuples

To define a tuple:

{% set sampleTuple = ('SnapApp', 'is', 'Cool') %}

Elements within a tuple can be accessed via their index, just like lists:

{{ sampleTuple[1] }}  {# Returns 'is' #}

A tuple with a single element requires a trailing comma:

{% set singleItemTuple = ('SnapApp',) %}

Example:

{% set pageData = [] %}
{% append ('home.html', 'Home Page') to pageData %}
{% append ('about.html', 'About SnapApp') to pageData %}
{{ pageData[1][1] }}  {# Outputs 'About SnapApp' #}
  • Dictionaries : Dictionaries are collections of key-value pairs, where each key is unique and mapped to a corresponding value. In SnapApp, dictionaries are particularly useful for storing structured data such as customer information, settings, or configuration parameters.

How to define and access Dictionaries?

To create a dictionary:

{% set customerInfo = {"Name": "Alex", "Role": "Administrator"} %}

Access values by referencing their keys:

{{ customerInfo["Name"] }}  {# Outputs 'Alex' #}

Keys in dictionaries can be strings, numbers, or None. Values can be any data type, including other dictionaries, lists, or tuples.

{% set productDetails = {"Product": "Laptop", "Price": 1500, "Stock": 30} %}
{{ productDetails["Product"] }}  {# Returns 'Laptop' #}

Jinja Filters

In SnapApp, filters are a powerful tool that can be used to modify data output dynamically within templates. Filters are applied to variables, allowing content transformations without modifying the original data source.

  • Filter Syntax: Filters are applied by using the pipe (|) symbol, followed by the name of the filter. The filter modifies the variable it is applied to. For instance, to convert a customer’s name to uppercase:
{{ customer["name"] | upper }}

This will convert the customer’s name to uppercase. The pipe symbol is an intuitive way to express “apply this filter to the variable.”

Filters that require additional parameters take arguments within parentheses, as demonstrated below:

{{ "product" | replace('p', 'b') }}

This replaces all occurrences of ‘p’ with ‘b’, resulting in the output broduct.

  • Chaining Filters : Multiple filters can be applied to a variable in sequence. Filters are evaluated left to right, provided that the output of one filter is a valid input for the next.

Example

{{ "apple" | upper | replace('P', 'b') }}
Output: AbbLE
{{ "apple" | replace('p', 'b') | upper }}
Output: ABBLE

Global Variables in Jinja

Campaigns and Projects have variables. Variables such as ID and name can be used for personalization in campaigns.

  • Project Level:
  • {{ project[‘id’] }} - Returns the project ID.
  • {{ project[‘name’] }} - Returns the project name.
  • Scenario, Email Campaign :
  • {{ scenario[‘id’] }} - Fetches the unique ID of the scenario or email campaign.
  • {{ scenario[‘name’] }} - Retrieves the name of the scenario or email campaign.
  • Scenario Node
  • {{ action[‘id’] }} - Gets the ID of the action node within the scenario.
  • {{ action[‘name’] }} - Returns the name of the action node or email campaign.
  • Banner
  • {{ banner[‘id’] }} - Returns the banner ID.
  • {{ banner[‘name’] }} - Provides the banner name.
  • {{ banner[‘variant’][‘id’] }} - Retrieves the ID of the banner variant.
  • {{ banner[‘variant’][‘name’] }} - Returns the variant name.
  • Experiments
  • {{ experiment[‘id’] }} - Fetches the ID of the experiment.
  • {{ experiment[‘name’] }} - Returns the name of the experiment.
  • {{ experiment[‘variant’][‘id’] }} - Retrieves the variant ID of the experiment.
  • {{ experiment[‘variant’][‘name’] }} - Provides the variant name of the experiment.
  • Tag manager
  • {{ tag[‘id’] }} - Returns the tag ID.
  • {{ tag[‘name’] }} - Retrieves the name of the tag.

Technical Expressions

The following expressions are more technical and may be useful in specific scenarios:

  • sent_timestamp | from_timestamp: Converts a Unix timestamp into a human-readable format.
  • api_base_url: Returns a URL to an API path.
  • public_base_url: Returns a URL to a public app-based resource.
  • random_bytes(length) | hexencode: Generates a random byte string of the specified length and encodes it in hexadecimal.
  • random_bytes(length) | b64encode: Generates a random byte string of the specified length and encodes it in Base64.

Functions on Data Types

Data Type Functions Description
Integer int.bit_length() Returns the number of bits necessary to represent an integer in binary, excluding the sign and leading zeros.
Integer int.conjugate() Returns the conjugate of the complex number. Complex numbers are not supported, so int.conjugate() returns the number itself.
Float float.as_integer_ratio() Returns a tuple containing a pair of integers whose ratio is exactly equal to the original float and with a positive denominator.
Float float.is_integer() Returns True if the float instance is finite with integral value, and False otherwise.
Float float.hex() Returns a representation of a floating-point number as a hexadecimal string. For finite floating-point numbers, this representation will always include a leading 0x and a trailing p and exponent.
Float float.fromhex(string) Returns float represented by a hexadecimal string. The string may have leading and trailing whitespace.
Float float.conjugate() Returns the conjugate of the complex number. Complex numbers are not supported, so int.conjugate() returns the number itself.
String str.capitalize() Returns a copy of the string with its first character capitalized and the rest lowercase.
String str.center(width[, fillchar]) Returns a centered string of length width. Padding is done using the specified fillchar (default is an ASCII space). The original string is returned if the width is less than or equal to len(s).
String str.count(sub[, start[, end]]) Returns the number of non-overlapping occurrences of substring sub in the range [start, end].
String str.encode(encoding=”utf-8”, errors=”strict”) Returns an encoded version of the string as a bytes object. Default encoding is ‘utf-8’. errors may be given to set a different error handling scheme.
String str.endswith(suffix[, start[, end]]) Returns True if the string ends with the specified suffix, otherwise return False. the suffix can also be a tuple of suffixes to look for. With optional start, test beginning at that position. With optional end, stop comparing at that position.
String str.expandtabs(tabsize=8) Returns a copy of the string where all tab characters are replaced by one or more spaces, depending on the current column and the given tab size. sub[, start[, end]]
String str.find(sub[, start[, end]]) Returns the lowest index in the string where substring sub is found within the slice s[start:end]. Optional arguments start and end are interpreted as in slice notation. Return -1 if a sub is not found.
String str.index(sub[, start[, end]]) Like str.find(), but throws an error when the substring is not found. Return the lowest index in the string where substring sub is found within the slice s[start:end].
String str.isalnum() Returns true if all characters in the string are alphanumeric and there is at least one character, false otherwise.
String str.isalpha() Returns true if all characters in the string are alphabetic and there is at least one character, false otherwise.
String str.isdigit() Returns true if all characters in the string are digits and there is at least one character, false otherwise.
String str.islower() Returns true if all cased characters in the string are lowercase and there is at least one cased character, false otherwise.
String str.isspace() Returns true if there are only whitespace characters in the string and there is at least one character, false otherwise.
String str.istitle() Returns true if the string is a title cased string and there is at least one character. For example, uppercase characters may only follow uncased characters and lowercase characters only cased ones. Return false otherwise.
String str.isupper() Returns true if all cased characters in the string are uppercase and there is at least one cased character, false otherwise.
String str.join(iterable) Returns items of the iterable joined by the string. An error will be thrown if there are any non-string values in iterable. The separator between elements is the string providing this method.
String str.ljust(width[, fillchar]) Returns the string justified to the left by adding fillchar character to the right, so that the string + filling characters have a length of the specified width. Padding is done using the specified fillchar (default is an ASCII space). The original string is returned if the width is less than or equal to the string length. Similar to str.center() and str.rjust().
String str.lower() Returns a copy of the string with all the cased characters converted to lowercase.
String str.lstrip([chars]) Returns a copy of the string with leading characters removed. The chars argument is a string specifying the set of characters to be removed. If omitted or None, the chars argument defaults to removing whitespace. The chars argument is not a prefix; rather, all combinations of its values are stripped.
String str.partition(sep) Split the string at the first occurrence of sep, and return a 3-tuple containing the part before the separator, the separator itself, and the part after the separator. If the separator is not found, return a 3-tuple containing the string itself, followed by two empty strings.
String str.replace(old, new[, count]) Returns a copy of the string with all occurrences of substring old replaced by new. If the optional argument count is given, only the first count occurrences are replaced.
String str.rfind(sub[, start[, end]]) Returns the highest index in the string where substring sub is found, such that sub is contained within s[start:end]. Optional arguments start and end are interpreted as in slice notation. Return -1 on failure.
String str.rindex(sub[, start[, end]]) Like str.rfind() but throws an error when the substring sub is not found.
String str.rjust(width[, fillchar]) Returns the string right-justified in a string of length width. Padding is done using the specified fillchar (default is an ASCII space). The original string is returned if the width is less than or equal to len(s).
String str.rpartition(sep) Splits the string at the last occurrence of sep, and return a 3-tuple containing the part before the separator, the separator itself, and the part after the separator. If the separator is not found, return a 3-tuple containing two empty strings, followed by the string itself.
String str.rstrip([chars]) Returns a copy of the string with trailing characters removed. The chars argument is a string specifying the set of characters to be removed. If omitted or None, the chars argument defaults to removing whitespace. The chars argument is not a suffix; rather, all combinations of its values are stripped.
String str.split(sep=None, maxsplit=-1) Returns a list of the strings split by the separator sep. If maxsplit is given, at most maxsplit splits are done (thus, the list will have at most maxsplit+1 elements). If maxsplit is not specified or -1, then there is no limit on the number of splits. Splitting an empty string with a specified separator returns [‘’].
String str.splitlines() Returns a list of the lines in the string, breaking at line boundaries. Line breaks are not included in the resulting list unless keepends is given and true.
String str.startswith(prefix[, start[, end]]) Returns True if the string starts with the prefix, otherwise return False. the prefix can also be a tuple of prefixes to look for. With optional start, test string beginning at that position. With optional end, stop comparing string at that position.
String str.strip([chars]) Return a copy of the string with the leading and trailing characters removed. The chars argument is a string specifying the set of characters to be removed. If omitted or None, the chars argument defaults to removing whitespace. The chars argument is not a prefix or suffix; rather, all combinations of its values are stripped.
String str.swapcase() Returns a copy of the string with uppercase characters converted to lowercase and vice versa.
String str.title() Returns a title cased version of the string where words start with an uppercase character and the remaining characters are lowercase
String str.translate(table) Returns a copy of the string in which each character has been mapped through the given translation table.
String str.upper() Returns a copy of the string with all the cased characters converted to uppercase.
String str.zfill(width) Returns a copy of the string left filled with ASCII ‘0’ digits to make a string of length width. A leading sign prefix (‘+’/’-‘) is handled by inserting the padding after the sign character rather than before. The original string is returned if the width is less than or equal to len(s).
List list.count(item) Returns the number of occurrences of the given item in the List.
List list.index(item[, start[, end]]) Returns the lowest index in the List where the given item is found within the slice s[start:end]. Optional arguments start and end are interpreted as in slice notation. Similar to str.index(). list.index() throws an error if index not within the List.
List list.pop([index]) Returns the item at index position in List and also removes it from the List. If no index provided, returns and removes the last item in the list. list.pop() throws an error if index not within the List.
Tuple tuple.count(item) Returns the number of occurrences of the given item in the Tuple.
Tuple tuple.index(item[, start[, end]]) Returns the lowest index in the Tuple where the given item is found within the slice s[start:end]. Optional arguments start and end are interpreted as in slice notation.
Dictionary dict.fromkeys(seq[, value]) Creates a new dictionary with keys from seq and all values set to value. value defaults to None.
Dictionary dict.get(key[, default]) Returns the value for key if the key is in the dictionary, else default. If a default value is not given, it defaults to None.
Dictionary dict.items() Returns a List of Tuples of (key, value)

Jinja Blocks

Jinja code blocks, defined using the syntax {% blocktype %} … {% endblocktype %}, serve as essential organizational and semantic elements within templates. They enhance code readability and modularity by grouping related code into distinct functional blocks. While Jinja supports eight block types, template importing is currently not implemented, rendering the block block type less practical.

Block Types

  • Set

    • If Blocks: The if block enables conditional rendering based on boolean expressions. It includes elif (else if) and else clauses for additional conditions and default behavior. The block is terminated with {% endif %}.

jinja {# If block number one #} {% if 1 < 1 %} One is less than one. {% elif 1< 2 %} One is less than two {% else %} one is greater or equal to two {% endif %}

Output: one is less than two

Because the first condition fails, the second one gets evaluated. It evaluates to true, hence the statements following it gets rendered.

OR

Using Inline Expression

jinja {% set x = 'Project' if 13 is even else 'Task' %} {{ x }}

Output: Task

  • For Blocks : The for block facilitates iteration over elements within iterables. It begins with {% for myItem in myIterable %}, where myIterable represents the iterable to be traversed. The subsequent lines define actions to be performed on each element. An optional {% else %} block can be included to handle cases where the iterable is empty. The loop is terminated with {% endfor %}

jinja {% for x in [] %} {{ 'Field' }} {%- else %} {{ 'Object' }} {% endfor %}

Output: Object

jinja {% for x in [1,2] %} {{ x }} {% else %} {{ 'Object' }} {% endfor %}

Output: 1 2

Recursive For Loop : Jinja supports recursive loop with ‘recursive’ flag at the end of for loop statement. Such for loops can use loop(List) function, which repeats the for loop with the recursive flag for each item in List. This allows you to effectively work with nested data structures.

jinja {% set x = [11, [21, 22], [ [23] ] ] %} {% for item in x recursive %} {% if item is iterable %} {{ loop(item) }} {% else %} {{ item ~ ' says hello from depth: ' ~ loop.depth }} {% endif %} {% endfor %}

Output: 11 says hello from depth: 1 21 says hello from depth: 2 22 says hello from depth: 2 23 says hello from depth: 3

For loop filtering : When using a for loop with the recursive flag, the recursive flag should be placed at the end of the statement, following the filter. This ensures that the filtering operation is applied correctly within the recursive context.

jinja {% for item in ['hello', [42], 13, 'world'] if (item is not string) recursive %} {{ loop(item) if item is iterable else item ~ ', depth: ' ~ loop.depth }} {% endfor %}

Output: 42, depth: 2 // 'hello' and 'world' were filtered out 13, depth: 1 // 42 is in extra List, therefore deeper

For blocks loop variables : When inside for loop, a special variable ‘loop’ is available with the properties listed in the following table.

Property Name Type Returns
loop.first Integer True if it is the first iteration, else false
loop.last Integer True if it is the last iteration, else false
loop.length Integer Number of total iterations
loop.depth Integer Current depth in a loop with ‘recursive’ tag. Starts at level 1. See Recursive for loop.
loop.depth0 Integer Current depth in a loop with ‘recursive’ tag. Starts at level 0. See Recursive for loop.
loop.index Integer Current index starting from 1
loop.index0 Integer Current index starting from 0
loop.revindex Integer Current index from end starting from 1. (On first iteration: loop.revindex == loop.length, on last iteration: loop.revindex == 1)
loop.revindex0 Integer Current index from end starting from 0. (On first iteration: loop.revindex0 == loop.length - 1, on iteration loop: loop.revindex0 == 0)
loop.cycle(arg1, arg2, …) Function For each iteration n in current loop, returns n-th item in the sequence of arguments.
  • loop.cycle() : The loop.cycle() is a helper function that for each iteration n in current loop, returns n-th item in the provided sequence of arguments.

jinja {% for item in range(3) %} {{ loop.cycle('hello', 'SnapApp') }} // multiple calls within same loop iteration {{ loop.cycle('hello', 'SnapApp') }} // return item on same n-th position {% endfor %}

output: hello // 1st iteration hello SnapApp // 2nd iteration SnapApp hello // 3rd iteration hello

Macros

Macros in Jinja serve as callable code blocks, analogous to functions. They enhance code clarity and efficiency by encapsulating reusable logic.

Macros are defined using the syntax {% macro macroName(args) %} … {% endmacro %}. Parameters, referred to as arguments, are passed to the macro during invocation. This enables customization and flexibility in macro execution.

{% macro printMood(day, mood='happy') %}
{{ (day | title) ~ ', Weather feels ' ~ mood ~ '!' }}
{% endmacro %}
...
{% set currMood = 'amazing' %}
{% set currDay = 'today' %}
{{ printMood(currDay, currMood) }}
Output:
Today, Weather feels amazing!

Call

The call block, closely related to macros, provides a mechanism for passing content directly to a macro. When a call block is used within a macro invocation, its content is wrapped as a macro and assigned to the ‘caller’ argument. This allows for dynamic content injection and customization within macros.

To access the passed content within the macro, the caller() function is used. This enables the macro to incorporate the provided content into its execution logic, creating more flexible and adaptable templates.

{# This one throws error because argument caller was not declared #}
{% macro errorMacro(arg1) %}
{{ errorMacro.name ~ "'s arg1 is " ~ arg1 }}
{% endmacro %}
...
{% call errorMacro() %}
This is the content of the call block.
{% callend %}

{# This one works because argument caller was declared #}
{% macro workingMacro(arg1='', caller='') %}
{{ workingMacro.name ~ "'s args are " ~ arg1 ~ ' and "' ~ caller() ~ '"' }}
{% endmacro %}
...
{% call workingMacro() %}
This is the content of the call block.
{% endcall %}
Output:
workingMacro's args are and "
This is the content of the call block."

Raw

The raw block treats its content as literal strings, preventing Jinja syntax evaluation. This is particularly useful when you need to display actual Jinja syntax within the template output.

{% raw  %}
{% set x = 5 %}
{% endraw %}
Output:
"{% set x = 5 %}"

Jinja in SnapApp

In SnapApp, this Jinja feature can be used in Page Builder. To access Page Builder, see more details here

jinja code

For example, the above code is to print the dynamic value of name and address using Jinja in Page Builder.

page

The above image shows dynamic the value of name and address field from the object along with the static value for a particular record ID.

Pre-conditions: Record must be present in the object in order to pass the value in Page.

SnapApp function in Jinja

In SnapApp, SnapApp function can also be used in Jinja in Page Builder. To create SnapApp function, see more details here

SnapApp function

For example, the above code is to select all the contacts from CONTACTS table

jinja code

The above image shows the values in the page iterated through the SnapApp Function.


Thank you for following these steps to configure your SnapApp components effectively If you have any questions or need further assistance, please don’t hesitate to reach out to our support team. We’re here to help you make the most out of your SnapApp experience.

For support, email us at snapapp@bluevector.ai


Generate Text
Profile image
Snapapp website agent