C3 AI Documentation Home

Transform API arguments with transformArgs

Transforms shape both what users see and what the backend receives. Use transform to format data before it renders. Use transformArgs when the user enters values that require conversion before the system makes a backend request. This gives you control over query logic, letting you normalize input strings, map readable labels to enum values, or inject state-based filters.

Understand transform vs. transformArgs

Use transform to change backend data before rendering it in the UI. Use transformArgs to change UI input before sending it to the backend.

If a user types "Active" into a filter:

  • transformArgs maps "Active" to 0, the backend enum.

  • The backend fetches data where status = 0.

  • transform maps 0 back to "Active" before the UI renders it.

Use both functions to support backend logic while keeping the UI readable.

Prepare the dataset and entity

The following tabs show the entity and dataset required for this example. The status field stores an enum as a number, which the UI maps to a readable label.

This entity defines the WindTurbine structure used in the filter and backend query.

Type
entity type WindTurbine {
  manufacturer: string
  location: string
  manufacturerDate: datetime
  status: int
}

Configure the data grid to use the transform

Reference the transform in your UI component. This applies transformArgs during fetch and transform to format the result.

JSON
/* examplePackage/ui/c3/meta/examplePackage.WindTurbineGrid.json */

{
  "type": "UiSdlConnected<UiSdlDataGrid>",
  "component": {
    "header": {
      "title": "Wind Turbine Dataset"
    },
    "paginationConfig" : {
      "pageSize" : 2,
      "pagination" : true
    },
    "dataSpec": {
      "dataType": "WindTurbine",
      "dataTransforms" : [ "WindTurbineDataTransform" ],
      "columnFields": [{ 
        "fieldName": "id", 
        "label": "ID"
      }, { 
        "fieldName": "location", 
        "label": "Location"
      },{ 
        "fieldName": "manufacturer", 
        "label": "Manufacturer"
      },{ 
        "fieldName": "manufacturerDate", 
        "label": "Manufacturer Date"
      }, { 
        "fieldName": "status", 
        "label": "Status"
      }]
    }
  }
}

This configuration connects the component to the WindTurbine entity and sets up the transform. Once you reference the transform in the UI, define the Type and implement the transformArgs logic.

Define the transform Type

Create a transform that supports both transformArgs. The backend expects numeric status values. The UI displays readable strings.

Type
// examplePackage/src/entity/WindTurbineDataTransform.c3typ

@typeScript
type WindTurbineDataTransform mixes UiSdlDataTransform<FetchResult<WindTurbine>, FetchResult<WindTurbine>>, Value {
  transformArgs: ~ ts-client
}

Implement the transformArgs function

Start with a basic transformArgs function that rewrites a single input. In this example, the UI passes a readable status label, such as "Active". The backend expects a numeric enum instead. This transform converts the label to the correct enum value before sending the query.

This example does not use component state or Redux. It shows how to manipulate the args object directly.

TypeScript
// examplePackage/src/entity/WindTurbineDataTransform.ts

// Define the transformArgs function to modify query arguments before sending them to the backend
export function transformArgs(
  args: Record<string, any> // The arguments passed into the backend method
): Record<string, any> {
  // Define a mapping from readable labels to backend enum values
  const statusMap = {
    active: 0,
    inactive: 1,
    maintenance: 2,
  };

  // Simulates a value entered in the UI near the input assignment
  const input = "active";

  // Look up the enum value that corresponds to the user-entered label
  const enumValue = statusMap[input];

  // If the label does not match any known value, return the arguments unchanged
  if (enumValue === undefined) return args;

  // Return a new arguments object with the enum value under the expected field name
  return {
    ...args,
    status: enumValue,
  };
}

This example uses a static argument. In the next example, you can pass dynamic values from component state.

Using component state

This function modifies the outgoing request before it reaches the backend. It reads the current input from component state, maps that value to an enum, and appends the correct filter to the query. This ensures that readable UI input like "Active" gets translated into the backend's expected value.

TypeScript
// examplePackage/src/entity/WindTurbineDataTransform.ts

// Import required types: the result wrapper, the WindTurbine entity, and Redux state
import { FetchResult, WindTurbine, UiSdlReduxState } from '@c3/types';

// Import a helper that retrieves component-level configuration from Redux state
import { getConfigFromState } from '@c3/ui/UiSdlConnected';

// Define the transformArgs function to rewrite query arguments before calling a backend action
export function transformArgs(
  searchArguments: Record<string, any>, // Existing arguments that will be sent to the backend
  componentId: string,                  // The component ID to identify the relevant UI element
  state: UiSdlReduxState                // The global Redux state that holds UI component values
): Record<string, any> {
  // Retrieve the current value from the component state (e.g., what the user typed or selected)
  const searchQuery = getConfigFromState(componentId, state, ['value']);

  // Define a reverse mapping from human-readable UI labels to backend enum values
  const statusEnumReverseMap = {
    active: 0,
    inactive: 1,
    maintenance: 2,
  };

  // Specify the field that the filter will target in the backend query
  const fieldName = 'status';

  // Exit early if the user did not input a value
  if (!searchQuery) return searchArguments;

  // Normalize and map the UI label to its corresponding enum value
  const enumValue = statusEnumReverseMap[searchQuery.toLowerCase()];

  // Exit if the input does not match a valid status
  if (enumValue === undefined) return searchArguments;

  // Build the filter expression that checks for the enum value as a string match
  const newFilter = `contains(lowerCase(string(${fieldName})), "${enumValue}")`;

  // If an existing filter exists, append the new filter using logical OR
  if (searchArguments?.spec?.filter) {
    searchArguments.spec.filter += ' || ' + newFilter;
  } else {
    // If no filter exists, create a new spec block with the filter
    searchArguments.spec = {
      ...searchArguments.spec, // Preserve any existing keys
      filter: newFilter,
    };
  }

  // Return the modified query arguments
  return searchArguments;
}

This implementation allows users to filter the data grid using labels like "Active", while still sending valid enum filters to the backend.

Was this page helpful?