1 # org.civicrm.afform (Early Proof of Concept)
3 ![Screenshot](/images/screenshot.png)
5 The Affable Administrative Angular Form Framework (`afform`) is a system for administering AngularJS-based forms
8 1. Allows developers to declaratively define a canonical, baseline form.
9 2. Allows administrators (or administrative tools) to use the CRUD API to customize the forms.
10 3. Allows developers to embed these forms in other CiviCRM-AngularJS apps.
11 4. (WIP; pending upstream support) Allow developers to apply [change sets](https://docs.civicrm.org/dev/en/latest/framework/angular/changeset/).
13 The extension is licensed under [AGPL-3.0](LICENSE.txt).
20 ## Installation (Web UI)
22 This extension has not yet been published for installation via the web UI.
24 ## Installation (CLI, Zip)
26 Sysadmins and developers may download the `.zip` file for this extension and
27 install it with the command-line tool [cv](https://github.com/civicrm/cv).
31 cv dl org.civicrm.afform@https://github.com/FIXME/org.civicrm.afform/archive/master.zip
34 ## Installation (CLI, Git)
36 Sysadmins and developers may clone the [Git](https://en.wikipedia.org/wiki/Git) repo for this extension and
37 install it with the command-line tool [cv](https://github.com/civicrm/cv).
40 git clone https://github.com/FIXME/org.civicrm.afform.git
44 ## Development: Quick Start
46 As an extension author, you can define a form along with its default,
47 canonical content. Simply create a folder named `afform/<MY-FORM>`. In
48 this example, we create a form named `foobar`:
51 $ cd /path/to/my/own/extension
52 $ mkdir -p afform/foobar
53 $ echo '{"server_route": "civicrm/pretty-page"}' > afform/foobar/meta.json
54 $ echo '<div>Hello {{routeParams.name}}</div>' > afform/foobar/layout.html
60 * We defined a route `civicrm/pretty-page`. This is defined in the same routing system used by CiviCRM forms.
61 * The file `layout.html` is an AngularJS HTML document. It has access to all the general features of Angular HTML (discussed more later).
62 * After creating a new form or file, we should flush the cache.
63 * If you're going to actively edit/revise the content of the file, then you should navigate
64 to **Administer > System Settings > Debugging** and disable asset caching.
66 Now that we've created a form, we'll want to determine its URL. As with most
67 CiviCRM forms, the URL depends on the CMS configuration. Here is an example
68 from a local Drupal 7 site:
71 $ cv url "civicrm/pretty-page"
72 "http://dmaster.localhost/civicrm/pretty-page"
73 $ cv url "civicrm/pretty-page/#/?name=world"
74 "http://dmaster.localhost/civicrm/pretty-page/#/?name=world"
77 Open the URLs and see what you get.
79 ## Development: Scopes, variables, and directives - oh my!
81 In AngularJS, the primary language for orchestrating a screen is HTML. You can embed code in here.
83 One key concept *scope* -- the *scope* defines the list of variables which you can access. By default, `afform`
84 provides a few variables within the scope of every form:
86 * `routeParams`: This is a reference to the [$routeParams](https://docs.angularjs.org/api/ngRoute/service/$routeParams)
87 service. In the example, we used `routeParams` to get a reference to a `name` from the URL.
88 * `meta`: The stored meta data (`meta.json`) for this form.
89 * `ts`: This is a utility function which translates strings, as in `{{ts('Hello world')}}`.
91 Additionally, AngularJS allows *directives* -- these are extra HTML attributes and HTML tags. For example:
93 * `ng-if` will conditionally create or destroy elements in the page.
94 * `ng-repeat` will loop through data.
95 * `ng-style` and `ng-class` will conditionally apply styling.
97 A full explanation of these features is out-of-scope for this document, but the key point is that you can use standard
100 ## Development: Display a contact record
102 Let's say we want `foobar` to become a basic "View Contact" page. A user
103 would request a URL like:
106 http://dmaster.localhost/civicrm/pretty-page/#/?cid=123
109 How do we use the `cid` to get information about the contact? Update `layout.html` to include data from APIv3:
112 <div ng-if="routeParams.cid"
113 afform-api3="['Contact', 'get', {id: routeParams.cid}]"
114 afform-api3-ctrl="apiData">
116 <div ng-repeat="contact in apiData.result.values">
117 <h1 crm-page-title="">{{contact.display_name}}</h1>
119 <h3>Key Contact Fields</h3>
121 <div><strong>Contact ID</strong>: {{contact.contact_id}}</div>
122 <div><strong>Contact Type</strong>: {{contact.contact_type}}</div>
123 <div><strong>Display Name</strong>: {{contact.display_name}}</div>
124 <div><strong>First Name</strong>: {{contact.first_name}}</div>
125 <div><strong>Last Name</strong>: {{contact.last_name}}</div>
127 <h3>Full Contact record</h3>
129 <pre>{{contact|json}}</pre>
134 This example is useful pedagogically and may be useful in a crunch -- but in the longer term,
135 we should have a richer library of directives so that typical user-managed forms don't drill-down
136 at this level of detail.
138 ## Development: Form CRUD API
140 Now that we've defined a baseline form, it's possible for administrators and
141 GUI applications to inspect the form using the API:
144 $ cv api afform.getsingle name=foobar
155 "Hello {{routeParams.name}}"
162 Additionally, you can also update the forms:
165 $ cv api afform.create name=foobar title="The Foo Bar Screen"
172 "title": "The Foo Bar Screen"
177 A few important things to note about this:
179 * The changes are only applied on this site.
180 * Once you make a change with the CRUD API, there will be two copies of the form:
181 * `[myextension]/afform/foobar/` is the default, canonical version.
182 * `[civicrm.files]/afform/foobar/` is the locally customized version.
183 * The `layout` field is stored as an Angular-style HTML document (`layout.html`), so you can edit it on disk like
184 normal Angular code. However, when CRUD'ing the `layout` through the API, it is presented in JSON-style.
187 ## Development: Every form is an AngularJS directive
189 In the quick-start example, we registered a new route (`"server_route": "civicrm/pretty-page"`) -- this created a
190 simple, standalone page with the sole purpose of displaying the `foobar` form. But that had very limited information.
191 What if we want control the environment a bit more?
193 There's no obligation to use the simple, standalone version of `foobar`. Think of `foobar` as a *re-usable sub-form*
194 or as a *directive*. If you've created an AngluarJS application, then you can embed `foobar` with two small steps:
196 1. In your application, declare a dependency on module `afformFoobar`. This is usually done in `ang/MYMODULE.ang.php`
197 and/or `ang/MYMODULE.js`.
198 2. In your HTML template, use the directive `<div afform-foobar=""></div>`.
200 This technique is particularly useful if you want to provide extra data for the form author to use. For example, your
201 application might pass in the current phase of the moon:
204 <div afform-foobar="{phaseOfMoon: 'waxing'}"></div>
207 Now, in `afform/foobar/layout.html`, you can use the `phaseOfMoon`:
210 Hello, {{routeParams.name}}. The moon is currently {{options.phaseOfMoon}}.
213 Or if you're not sure data will actually be provided:
216 Hello, {{routeParams.name ? routeParams.name : 'anonymous'}}. The moon is currently {{options.phaseOfMoon ? options.phaseOfMoon : 'on hiatus'}}.
221 * The code is currently written as a proof-of-concept. There are several `FIXME`/`TODO` declarations in the code
222 for checking pre-conditions, reporting errors, handling edge-cases, etc.
223 * Although afforms are can be used in AngularJS, they don't fully support tooling like `cv ang:html:list`
224 and `hook_civicrm_alterAngular` changesets. We'll need a core patch to allow that.
225 * We generally need to provide more services for managing/accessing data (e.g. `crm-api3`).
226 * Need to implement the `Afform.revert` API to undo local customizations.
227 * Haven't decided if we should support a `client_route` property (i.e. defining a skeletal controller and route for any form).
228 On the plus side, make it easier to add items to the `civicrm/a` base-page. On the flipside, we don't currently have
229 a strong use-case, and developers can get the same effect with `civix generate:angular-page` and embedding `<div afform-foobar/>`.