Commit | Line | Data |
---|---|---|
64423b13 | 1 | # Embedding Forms: Afform as reusable building-block |
8a0ffdf9 TO |
2 | |
3 | In the [quick-start example](quickstart.md), we registered a new route (`"server_route": "civicrm/hello-world"`) -- this created a | |
4f4b6487 | 4 | simple, standalone page with the sole purpose of displaying the `helloWorld` form. What if we want to embed the form |
9b5234b7 | 5 | somewhere else -- e.g. as a dialog inside an event-listing or membership directory? Afforms are actually *re-usable |
8a0ffdf9 TO |
6 | sub-forms*. |
7 | ||
4f4b6487 | 8 | How does this work? Every `afform` is an *AngularJS directive*. For example, `hello-world` can be embedded with: |
8a0ffdf9 TO |
9 | |
10 | ```html | |
4f4b6487 | 11 | <div hello-world=""></div> |
8a0ffdf9 TO |
12 | ``` |
13 | ||
4f4b6487 | 14 | Moreover, you can pass options to `helloWorld`: |
8a0ffdf9 TO |
15 | |
16 | ```html | |
4f4b6487 | 17 | <div hello-world="{phaseOfMoon: 'waxing'}"></div> |
8a0ffdf9 TO |
18 | ``` |
19 | ||
4f4b6487 | 20 | Now, in `ang/helloWorld.aff.html`, you can use `options.phaseOfMoon`: |
8a0ffdf9 TO |
21 | |
22 | ```html | |
23 | Hello, {{routeParams.name}}. The moon is currently {{options.phaseOfMoon}}. | |
24 | ``` | |
25 | ||
64423b13 | 26 | ## Example: Contact record |
8a0ffdf9 TO |
27 | |
28 | Is this useful? Let's suppose you're building a contact record page. | |
29 | ||
30 | First, we should make a few building-blocks: | |
31 | ||
3e29642c TO |
32 | 1. `ang/myContactName.aff.html` displays a sub-form for editing first name, lastname, prefix, suffix, etc. |
33 | 2. `ang/myContactAddresses.aff.html` displays a sub-form for editing street addresses. | |
34 | 3. `ang/myContactEmails.aff.html` displays a sub-form for editing email addresses. | |
8a0ffdf9 | 35 | |
3e29642c | 36 | Next, we should create an overall `ang/myContact.aff.html` which uses these building-blocks: |
8a0ffdf9 TO |
37 | |
38 | ```html | |
39 | <div ng-form="contactForm"> | |
40 | <div crm-ui-accordion="{title: ts('Name')}"> | |
3e29642c | 41 | <div my-contact-name="{cid: routeParams.cid}"></div> |
8a0ffdf9 TO |
42 | </div> |
43 | <div crm-ui-accordion="{title: ts('Street Addresses')}"> | |
3e29642c | 44 | <div my-contact-addresses="{cid: routeParams.cid}"></div> |
8a0ffdf9 TO |
45 | </div> |
46 | <div crm-ui-accordion="{title: ts('Emails')}"> | |
3e29642c | 47 | <div my-contact-emails="{cid: routeParams.cid}"></div> |
8a0ffdf9 TO |
48 | </div> |
49 | </div> | |
50 | ``` | |
51 | ||
3e29642c | 52 | And we should create a `ang/myContact.aff.json` looking like |
a8cb7414 TO |
53 | |
54 | ```json | |
55 | { | |
56 | "server_route": "civicrm/contact", | |
3e29642c | 57 | "requires" : ["myContactName", "myContactEmails", "myContactAddresses"] |
a8cb7414 | 58 | } |
6a7e9d50 | 59 | ``` |
3e29642c | 60 | > *(FIXME: In the parent form's `*.aff.json`, we need to manually add `myContactName`, `myContactAddresses`, `myContactEmails` to the `requires` list. We should autodetect these instead.)* |
8a0ffdf9 | 61 | |
4f4b6487 TO |
62 | We've created new files, so we'll need to flush the file-index |
63 | ||
64 | ``` | |
65 | cv flush | |
66 | ``` | |
67 | ||
68 | and now we can open the page | |
69 | ||
70 | ``` | |
71 | cv open 'civicrm/contact?cid=100' | |
72 | ``` | |
73 | ||
3e29642c | 74 | What does this buy us? It means that a downstream admin (using APIs/GUIs) can fork `ang/myContactName.aff.html` -- |
8a0ffdf9 | 75 | but all the other components can cleanly track the canonical release. This significantly reduces the costs and risks |
36376a39 | 76 | of managing upgrades and changes. |