Skip to content

Afform Core

The core of the Afform extension is an AngularJS module factory. Traditionally, to add a new AngularJS module in CiviCRM, one must create an .ang.php file, a js module file, some controllers, components & templates, plus a page route to bootstrap AngularJS and load the module.

Afform Core does all that for you, all you need is a template .aff.html file and an optional configuration .aff.json file.

Creating a Form in an Extension

As an extension author, you can define a form along with its default, canonical content. Simply create a file ang/MYFORM.aff.html. In this example, we create a form named helloWorld:

  1. Make sure you're in the directory with your extension (cd /path/to/extension)
  2. Make an ang directory in your extension. (mkdir ang)
  3. Add some HTML/JS code to your aff.html file. echo '<div>Hello {{routeParams.name}}</div>' > ang/helloWorld.aff.html
  4. Add some JSON config to your aff.json file. echo '{"server_route": "civicrm/hello-world"}' > ang/helloWorld.aff.json
  5. Flush the caches so CiviCRM picks up the new form. cv flush

A few things to note:

  • The ang folder is the typical location for AngularJS modules in CiviCRM extensions.
  • We defined a route civicrm/hello-world. This appears in the same routing system used by CiviCRM forms. It also supports properties such as title (page title) and is_public (defaults to false).
  • After creating a new form or file, we should flush the cache.
  • If you're going to actively edit/revise the content of the file, then you should navigate to Administer > System Settings > Debugging and disable asset caching.
  • The extension *.aff.html represents an AngularJS HTML document. It has access to all the general features of Angular HTML (discussed more later).
  • In AngularJS, there is a distinction between a "module" (unit-of-code to be shared; usually appears as camelCase) and a "directive" (a custom HTML element; may appear as camelCase or as kebab-case depending on context). Afform supports a tactical simplification in which one *.aff.html corresponds to one eponymous module and one eponymous directive.

Now that we've created a form, we'll want to determine its URL. As with most CiviCRM forms, the URL depends on the CMS configuration. Here is an example from a local Drupal 7 site:

We can use cv to get full URLs for any civicrm/* URL like so:

  • cv url "civicrm/hello-world" returns http://dmaster.localhost/civicrm/hello-world
  • cv url "civicrm/hello-world/#/?name=world" returns http://dmaster.localhost/civicrm/hello-world/#/?name=world

Open the URLs and see what you get.

Form Overrides

Afform files inside an extension (aka a "Packaged" form), are considered the default state, or "base". If you fetch your form via API (e.g. the APIv4 Explorer), civicrm_api4('Afform', 'get') will return something like this:

[
  {
    name: "helloWorld" // this corresponds to file name minus .aff.html extensions
    server_route: "civicrm/hello-world" // as defined in helloWorld.aff.json
    has_base: true,
    has_local: false,
    ...
  },
  ...
]

The calculated field 'has_base' is true because the Afform files are packaged in an extension. 'has_local' is false for now. However, site builders can modify your form without touching your extensnion code simply by making a copy of the form in their local files directory. In that case the form would be considered overridden and Afform would automatically use the local copy in favor of the one on your extension and the same api call would return data from the new files with has_local: true.