Skip to content

Embedding

Embedding Afforms

Embedding (AKA placement) of Afforms is very useful, and can go in two directions:

  1. An Afform can be embedded into places, e.g. standalone pages, contact summary tabs, modal popups, or dashboards.

  2. Items can be embedded into an Afform, notably SearchKit Displays, as well as other Afforms.

No-Code Placements

The FormBuilder UI includes a number of available placements, including the CiviCRM Dashboard, Contact Summary screen, Contact and Event tabs, etc. The code for these were all implemented using the methods described below.

Embedding Afforms in Afforms

Afforms can be embedded into other Afforms simply by including the directive for one Afform in the markup of another; Angular dependencies will be resolved automatically.

Embedding in Smarty Templates

Adding Afforms to traditional Smarty-based pages or forms can be a transitional step away from legacy screens

PHP Code

Civi::service('angularjs.loader')
  ->addModules('afformMyForm');

$this->assign('myAfformVars', $optionalVars);

Note - to figure out what to put in 'name' look at the name in the FormBuilder listing for your form. In the above example the 'name' would be the entire 'afformMyForm' (there isn't a magic prefix).

Template Code

<crm-angular-js modules="afformMyForm">
  <form id="bootstrap-theme">
    <afform-my-form options='{$myAfformVars|@json_encode}'></afform-my-form>
  </form>
</crm-angular-js>

Embedding an afform in a Smarty Page using Regions

An extension can embed an afform in a CiviCRM Smarty Page using the regions specified in the template. By default, a page would have 3 specified regions: page-header, page-body, page-footer. Additional regions can be added to the page using the {crmRegion} tag. The following steps would help in embedding an afform in any of the Smarty Page assigned regions:

  1. Identify the region on the page by which the afform would be attached. If the template doesn't have custom regions set, you can either set one or use the default page-body region. Using the default page-body would attach the afform to the bottom of the page right above the footer.
  2. Paste the following code in the pageRun Hook of the afform entity's extension (strings within {curly braces} are placeholders you must replace):
        if (get_class($page) === '{DISPLAY_PAGE_CLASS_NAME}') {
          Civi::service('angularjs.loader')->addModules('{afformModuleName}');
          CRM_Core_Region::instance(`{PAGE_REGION}`)->add([
            'markup' => '<crm-angular-js modules="{afformModuleName}"><form id="bootstrap-theme"><{afform-directive-name}></afform-directive-name></form></crm-angular-js>',
          ]);
        }
    
  3. {DISPLAY_PAGE_CLASS_NAME} is the class name for the Smarty page. This can be gotten from the Route entity using the url path to the page. For example, to get the class for the page with path /lorem/ipsum/text use: cv api4 Route.get '{"where":[["path","=","lorem/ipsum/text"]],"limit":25,"select":["page_callback"]}'
  4. {afformModuleName} and {afform-directive-name} can be gotten using the Afform entity and the name of the afform (which can be gotten from the formbuilder/search forms list). For example, to get the moduleName and directiveName for an afform named afTestAfform, use: cv api4 Afform.get '{"where":[["name","=","afTestAfform"]],"limit":25,"select":["module_name","directive_name"]}'
  5. {PAGE_REGION} is the specific region where you want the afform to be embedded. For pages with no specified region, using page-body should place the afform below the last component on the template before the footer.

  6. Refreshing the page should display the Afform in the position of the page region.

Passing data to an afform embedded in a Smarty Page to filter a Search table

Here are the steps to pass data into an embedded afform for filtering by a value such as id: 1. Add the data to the options attribute of the afform. In the example below, the embedded afform (afSearchTest) is passed an id with value 100 through the options attribute:

    if (get_class($page) === 'CRM_Contribute_Page_DashBoard') {
      Civi::service('angularjs.loader')->addModules('afSearchTest');
      CRM_Core_Region::instance(`{PAGE_REGION}`)->add([
        'markup' => '<crm-angular-js modules="afSearchTest"><form id="bootstrap-theme"><afsearch-test options="{id: 100}"></afsearch-test></form></crm-angular-js>',
      ]);
    }
2. In the generated afform html file in the ang directory, specify this id gotten from the options attribute as the filter-by value in the search display table:

  <crm-search-display-table search-name="MySavedSearch" display-name="MyListDisplay" filters="{contact_id: options.id}"></crm-search-display-table>

Embedding in Tabsets

Similar to the above, you can implement hook_civicrm_tabset to place an Afform in a tab. Note, however, that the "Contact Summary Tabs" and "Manage Event Tabs" already have no-code solutions and can be selected in the "Placement" menu in FormBuilder.

PHP Code

function example_civicrm_tabset($tabsetName, &$tabs, $context) {
  // Check if the tabset is Contribution Page
  if ($tabsetName == 'civicrm/admin/contribute') {
    Civi::service('angularjs.loader')->addModules('afformExampleForm');
    $tabs['afformExampleForm'] = [
      'id' => 'afformExampleForm',
      'title' => 'Example Tab',
      'weight' => 99,
      'icon' => 'crm-i fa-example-icon',
      'is_active' => TRUE,
      'template' => 'path/to/ExampleTemplate.tpl',
      'example_options' => $optionalVariables,
    ];
  }
}
Template Code
<crm-angular-js modules="afformExampleForm">
  <form id="bootstrap-theme">
    <afform-example-form options='{$block.example_options|@json_encode}'></afform-example-form>
  </form>
</crm-angular-js>