Skip to content

Lucit Designer Formatting Functions Guide

Overview

The most powerful JavaScript functions in the Lucit Template Designer are Designer Formatting Functions.

These functions are executed during the template rendering process and can operate on one or multiple elements at the same time.

They also receive more data than standard text formatting functions and can apply to text, image, video, object, and SVG elements.

How Designer Formatting Functions Work

During rendering, any Designer Formatting Functions that have been registered with registerDesignerFormattingFunction on the JS tab of the Code Editor are executed.

These functions are applied to elements based on the CSS selector provided when the function is registered. Lucit calls your function once for each element that matches the selector.

Creating a Designer Formatting Function

To create Designer Formatting Functions, add them in the JS (JavaScript) tab in the Code Editor at Templates → {Template} → Canvas → Action Bar → <>.

To register a Designer Formatting Function, use registerDesignerFormattingFunction with the following three arguments:

  • name – A name for the function. Use alphanumeric and _ characters only; no spaces or special characters other than _.
  • fn – The callback function that will be executed on each element matched by the selector. It accepts up to four parameters:
  • el – The element that the function is being applied to.
  • dataValue – The value of the data-value attribute after macro replacements (or the x-placeholder value when macros have not been replaced). For example, if data-value is {item.price}, then dataValue will be the resolved numeric price.
  • dataObject – If dataValue is serialized JSON, this will contain the parsed object version of that JSON; otherwise it simply contains an object like { "value": dataValue }.
  • elementSettings – If the element has x-element-settings and the user has configured it to produce x-element-setting-values, then elementSettings contains a JSON object of key/value pairs for each setting value, or the placeholder if the placeholder was defined for the key in x-element-settings.
  • selector – The CSS selector used to select one or more elements to apply this function to.

Data flow overview

  1. You set a data-value attribute on your element (for example, data-value="{item.price}").
  2. Lucit replaces any macros in data-value with their actual values.
  3. The resulting value is passed as dataValue to your function.
  4. If dataValue is JSON, Lucit parses it and passes the parsed object as dataObject. Otherwise, dataObject is { value: dataValue }.

Example – Format price fields in red

In this example, we select by the object code and then format the field in red if the price is less than 30,000.

registerDesignerFormattingFunction(
  "makeCheapVehiclesRed",
  (el, dataValue) => {
    const price = parseFloat(dataValue);

    if( price < 30000 ){
      el.innerHTML = '<span style="color:red">' + el.innerHTML + "</span>";
    }
  },
  '[x-objectcode="item_price"]'
);

Note that this function does not use dataObject or elementSettings, but it expects that a dynamic element with x-objectcode="item_price" has been added to the template.

Example – Monthly Payment Calculator

This is an example of a configurable monthly payment calculator element.

In this particular case, the {{item.price}} macro is embedded in the JavaScript and not in the HTML of the element.

This shows the power of these functions, as you can embed macros anywhere in the HTML, CSS, or JavaScript as needed.

Also Note, that when uysing macros in JavaScript, we MUST use the double curly brace format like this {{item.price}} not like this {item.price}

The x-element-settings attribute is used by the user editing this template. They can right-click on the element on the canvas, choose Edit Settings, and then configure the values for each one.

<div
  id="obj_monthly_payment_123mo_sbhpqzfq"
  title="Monthly Payment : $123/Mo"
  class="lc_ut_designer lc_dt_element lc_dt_text lc_dt_text_editable lc_format_fit_text"
  x-element-settings='{"fields":[{"key":"months","name":"No. of Months","type":"int","placeholder":"60"},{"key":"rate","name":"Interest Rate","type":"float","placeholder":"6.5"},{"key":"prefix","name":"Prefix","type":"text","placeholder":"&amp;#36;"},{"key":"suffix","name":"Suffix","type":"text","placeholder":"\/Month"}]}'
  x-fieldname="monthly_payment_123mo"
  x-optional-field="false"
  x-element-setting-values='{"months":"60","rate":"4.5","prefix":"&#36;","suffix":"/Month"}'
  x-placeholder="monthly_payment_123mo">
  $123/Mo
</div>
registerDesignerFormattingFunction(
  "format_obj_monthly_payment_123mo_sbhpqzfq",
  (el, dataValue, dataObject, elementSettings) => {
    // At render time, {{item.price}} will be replaced with the numeric item price
    const priceRaw = "{{item.price}}";
    const price = parseFloat(priceRaw);

    const months = isNaN(parseInt(elementSettings?.months))
      ? 60
      : parseInt(elementSettings?.months);
    const rate = isNaN(parseFloat(elementSettings?.rate))
      ? 6.5
      : parseFloat(elementSettings?.rate);
    const suffix = elementSettings?.suffix ?? "";
    const prefix = elementSettings?.prefix ?? "";

    if (months <= 0) {
      throw new Error("Number of months must be greater than zero.");
    }

  if (!priceRaw || isNaN(price)) {
      return; //No price, so, display placeholder
    }

    const monthlyRate = rate / 100 / 12;

  // If the interest rate is 0%, simple division
    if (monthlyRate === 0) {
      el.innerHTML =
        prefix + Math.floor(price / months).toLocaleString() + suffix;
    }

    // Formula: M = P * (r(1 + r)^n) / ((1 + r)^n - 1)
    const numerator = price * monthlyRate * Math.pow(1 + monthlyRate, months);
    const denominator = Math.pow(1 + monthlyRate, months) - 1;
    const monthlyPayment = Math.floor(numerator / denominator).toLocaleString();

    el.innerHTML = prefix + monthlyPayment + suffix;
  },
  '[id="obj_monthly_payment_123mo_sbhpqzfq"]'
);

More Complete Example – On Sale Items

In this example, we have a feed that provides a price and an original price.

We use these values to either display or hide both the "on sale" SVG element and the "on sale" text. The item is considered on sale only when item.original_price is greater than item.price.

<div
  id="rectangle_default_bg"
  title=""
  x-objectcode="rectangle"
  class="lc_ut_designer lc_dt_object lc_dt_default_background"
  x-fieldname="rectangle"
  x-optional-field="false"
  x-element-settings="{}"
  x-element-setting-values="{}"></div>

<div
  id="obj_award_1_ae341t49"
  title="Award 1"
  x-objectcode="award_1"
  class="lc_ut_designer lc_dt_element lc_dt_object lc_dt_object_svg on_sale"
  style=""
  x-fieldname="award_1"
  x-optional-field="false"
  x-element-settings="{}"
  x-element-setting-values="{}">
  <svg
    width="100%"
    height="100%"
    viewBox="0 0 1080 1080"
    preserveAspectRatio="none"
    pointer-events="none">
    <path
      d="m275.69,171.64c.26-23.01-8.72-48.32,8.82-60.97,23.18-16.71,43.56,5.66,60.46,18.84,46.13,35.96,
                        67.23,16.72,78.33-31.41,4.54-19.68,5.85-45.81,27.94-50.42,22.22-4.64,35.95,18.58,45.16,35.54,27.48,50.6,
                        52.62,56.85,82.56,2.32,9.52-17.34,20.53-40.44,43.33-38.45,25.64,2.24,24.46,28.84,28.64,48,10.53,48.27,32.08,
                        67.86,78.24,33.53,55.3-41.12,77.07-23.23,69.88,41.43-6.02,54.16,20.63,59.63,64.63,47.23,18.07-5.09,40.63-18.91,
                        57.49-.32,17.74,19.56.91,39.31-9.42,55.23-32.33,49.77-17.9,72.83,38.94,76.59,63.67,4.22,71.8,30.29,24.45,
                        71.24-46.11,39.89-27.14,61.45,17.43,80.84,18.11,7.88,44.95,12.78,47.22,34.33,3.08,29.2-28.37,32.42-47.66,
                        41.06-44.74,20.04-62.68,40.74-17.41,81.15,41.76,37.27,48.13,69.57-23.36,71.34-60.14,1.49-60.99,31.16-39.13,
                        74.66,29.46,58.61,8.48,76.17-49.76,57.67-57.44-18.25-66.88,10.7-63.22,58.1,1.51,19.54,9.43,45.1-10.22,
                        56.84-21.79,13.02-37.26-10.93-51.64-23.01-46.59-39.13-75.02-33.18-86.67,29.23-3.63,19.46-3.71,45.78-27.36,
                        50.13-23.43,4.31-34.38-19.87-43.55-36.89-34.55-64.12-61.04-42.34-88.38,5.45-8.55,14.95-19.06,34.97-39.14,
                        31.78-19.96-3.17-23.71-24.64-26.96-42.1-10.56-56.89-34.04-76.44-86.46-35.66-46.27,36-71.95,21.95-62.62-36.18,
                        10.64-66.29-14.71-75.22-71.02-56.19-47.06,15.91-71.86,4.45-44.56-50,25.55-50.97,
                        17.6-81.11-47.08-84.95-66.55-3.95-49.46-38.47-16.41-65.91,50.43-41.87,
                        42.28-66.42-15.95-86.81-17.95-6.28-43.83-13.12-41.02-38.41,2.47-22.22,26.14-30.99,44.99-36.53,63.22-18.6,
                        53.67-44.08,12.57-80.11-49.87-43.71-33.1-66.59,26.88-71.3,47-3.69,73.97-17.97,
                        42.49-70.87-10.59-17.8-31.67-40.36-10.59-61.96,16.33-16.73,39.9-3.84,57.79,2.37,49.54,17.19,75.43,10,61.34-46.47Z"></path>
  </svg>
</div>
<div
  id="obj_your_new_text_24mnfppl"
  title="Your New Text"
  x-objectcode="your_new_text"
  class="lc_ut_designer lc_dt_element lc_dt_text lc_dt_text_editable lc_format_fit_text on_sale"
  style=""
  x-fieldname="your_new_text"
  x-optional-field="false"
  x-element-settings="{}"
  x-element-setting-values="{}">
  ON SALE NOW!!!
</div>

<div
  id="data_source_text_kaxxxzfo"
  title=""
  x-objectcode="item_title"
  class="lc_ut_designer lc_dt_data lc_dt_text lc_format_fit_text"
  x-placeholder="Your Caption"
  x-optional-field="false"
  x-fieldname="Title"
  data-value="{item.title}"
  x-element-settings="{}"
  x-element-setting-values="{}">
  {item.title}
</div>

<div
  id="data_source_text_yov3y11r"
  title=""
  x-objectcode="item_price"
  class="lc_ut_designer lc_dt_data lc_dt_text lc_format_fit_text lc_format_price_us"
  x-placeholder="25968"
  x-optional-field="false"
  x-fieldname="Price"
  data-value="{item.price}"
  x-element-settings="{}"
  x-element-setting-values="{}">
  {item.price}
</div>
registerDesignerFormattingFunction(
  "show_on_sale_only_when_discounted",
  (el, dataValue, dataObject, elementSettings) => {
    // Macros will be replaced at render time with the numeric values
    const priceRaw = "{{item.price}}";
    const originalPriceRaw = "{{item.original_price}}";

    const price = parseFloat(priceRaw);
    const originalPrice = parseFloat(originalPriceRaw);

    // If we can't parse either value, leave the element visible
    if (isNaN(price) || isNaN(originalPrice)) {
      return;
    }

    // Item is considered ON SALE only if original_price > price
    const isOnSale = originalPrice > price;

    // We hide the .on_sale element when the item is NOT on sale
    if (!isOnSale) {
      el.style.visibility = "hidden";
    }
    // If isOnSale is true, we leave it visible as-is
  },
  ".on_sale"
);