Skip to content
Snippets Groups Projects
Commit cbfcf6b4 authored by totten's avatar totten
Browse files

"AngularJS: Loader" - Various updates

parent 52eabe30
Branches
No related tags found
No related merge requests found
......@@ -3,49 +3,33 @@
What happens when a user visits a CiviCR-Angular page? For example, let's
consider this URL:
* `https://example.org/civicrm/a/#/mailing/new`
* `https://example.org/civicrm/a/#/caseType`
Broadly speaking, two things happen:
1. (Server-side) CiviCRM processes the request for `civicrm/a`. It
displays a web-page with all your Angular modules.
2. (Client-side) AngularJS processes the request for `mailing/new`.
It uses an HTML template to setup the UI.
displays a web-page with all your Angular modules -- such as
`crmAttachment`, `crmCaseType`, `crmUi`, and so on.
2. (Client-side) AngularJS processes the request. It finds that the
module `crmCaseType` includes a route for `#/caseType` and loads the
appropriate HTML template.
The client-side behavior is well-defined by Angular
[ngRoute](https://docs.angularjs.org/api/ngRoute). We'll explore the
server-side in greater depth because that is unique to the CiviCRM-Angular
integration.
## The library of AngularJS modules
!!! caution "Discusses new/experimental interfaces"
CiviCRM needs a list of available AngularJS modules. Technically, these
modules are defined via
[hook_civicrm_angularModules](/hooks/hook_civicrm_angularModules.md), e.g.
Some elements of this document have been around since CiviCRM v4.6
and should remain well-supported. Other elements are new circa v4.7.21
and are still flagged experimental.
```php
/**
* Implements hook_civicrm_angularModules.
*/
function foobar_civicrm_angularModules(&$angularModules) {
$angularModules['myBigAngularModule'] = array(
'ext' => 'org.example.foobar',
'basePages' => array('civicrm/a'),
'requires' => array('crmUi', 'crmUtils', 'ngRoute'),
'js' => array('ang/myBigangularModule/*.js'),
'css' => array('ang/myBigangularModule/*.css'),
'partials' => array('ang/myBigangularModule'),
);
}
```
## Library of modules
!!! tip "Tip: Generating skeletal code with `civix`"
In practice, this skeletal code can be autogenerated usin `civix`.
For details, see [AngularJS: Quick Start](/framework/angular/quickstart.md).
The list of available modules varies depending on your system configuration:
if you install more CiviCRM extensions, then you might have more Angular
modules. Use `cv` to inspect the list of available Angular modules:
The CiviCRM-Angular loader needs a list of available AngularJS modules.
This list depends on your system configuration (e.g. which CiviCRM
extensions are enabled). To view the current list, run `cv`:
```
$ cv ang:module:list
......@@ -60,42 +44,58 @@ For a full list, try passing --user=[username].
| crmAttachment | civicrm/a | angularFileUpload, crmResource |
| crmAutosave | civicrm/a | crmUtil |
| crmCaseType | civicrm/a | ngRoute, ui.utils, crmUi, unsavedChanges, crmUtil, crmResource |
...
...snip...
```
!!! tip "Tip: More options for `cv ang:module:list`"
* Use `--columns` to specify which columns to display. Ex: `cv ang:module:list --columns=name,ext,extDir`
* Use `--out` to specify an output format. Ex: `cv ang:module:list --out=json-pretty`
* Use `--user` to specify a login user using with. This may reveal permission-dependent modules. Ex: `cv ang:module:list --user=admin`
Use `--columns` to specify which columns to display. Ex:
## The default base page (`civicrm/a`)
```
$ cv ang:module:list --columns=name,ext,extDir
```
CiviCRM includes a default base-page named `civicrm/a`. Any module can add new routes on
this page. The page might look like this:
Use `--out` to specify an output format. Ex:
```html
<!-- URL: https://example.org/civirm/a -->
<html>
<head>
<script type="text/javascript" src="https://example.org/.../angular.js"></script>
<script type="text/javascript" src="https://example.org/.../angular-route.js"></script>
<script type="text/javascript" src="https://example.org/.../crmApp.js"></script>
<script type="text/javascript" src="https://example.org/.../crmCaseType.js"></script>
<script type="text/javascript" src="https://example.org/.../crmMailing.js"></script>
<script type="text/javascript" src="https://example.org/.../crmUi.js"></script>
...
<link rel="stylesheet" href="https://example.org/.../crmUi.css" />
...
</head>
<body>
<div ng-app="crmApp"></div>
</body>
</html>
```
$ cv ang:module:list --out=json-pretty
```
Use `--user` to specify a login user. This may reveal permission-dependent modules. Ex:
```
$ cv ang:module:list --user=admin
```
Under-the-hood, this library of modules is defined via
[hook_civicrm_angularModules](/hooks/hook_civicrm_angularModules.md), e.g.
```php
/**
* Implements hook_civicrm_angularModules.
*/
function foobar_civicrm_angularModules(&$angularModules) {
$angularModules['myBigAngularModule'] = array(
'ext' => 'org.example.foobar',
'basePages' => array('civicrm/a'),
'requires' => array('crmUi', 'crmUtils', 'ngRoute'),
'js' => array('ang/myBigangularModule/*.js'),
'css' => array('ang/myBigangularModule/*.css'),
'partials' => array('ang/myBigangularModule'),
);
}
```
!!! note "In practice, the JS files may be aggregated and/or minimized."
!!! tip "Tip: Generating skeletal code with `civix`"
In practice, one usually doesn't need to implement this hook directly.
Instead, generate skeletal code with `civix`. For details, see
[AngularJS: Quick Start](/framework/angular/quickstart.md).
## Default base-page
The markup is generated by a PHP class, `AngularLoader`, using logic like this:
CiviCRM includes a "base-page" named `civicrm/a`. By default, this page
includes the core AngularJS files as well as all the modules in the library.
The page is generated with a PHP class, `AngularLoader`, using logic like this:
```php
$loader = new \Civi\Angular\AngularLoader();
......@@ -109,10 +109,73 @@ and loads them on the page. This will include:
* Any AngularJS modules explicitly listed in `setModules(...)`. (Ex: `crmApp`)
* Any AngularJS modules with matching `basePages`. (Ex: The value `civicrm/a`
is specified by both `setPageName(...)` and `myBigAngularModule` [above].)
is specified by both `setPageName(...)` [above] and `myBigAngularModule` [above].)
* Any AngularJS modules transitively required by the above.
!!! note "What makes `civicrm/a` special?"
When declaring a module, the property `basePages` will default to
`array('civicrm/a')`. In other words, if you don't specify otherwise,
all modules are loaded on `civicrm/a`.
all modules are loaded on `civicrm/a`.
## Custom base-pages
Loading all Angular modules on one page poses a trade-off. On one hand, it
warms up the caches and enables quick transitions between screens. On the
other hand, the page could become bloated with modules that aren't actually
used.
If this is a concern, then you can create new base-pages which are tuned to
a more specific use-case. For example, suppose we want to create a page
which *only* has the CiviCase administrative UI.
First, create a skeletal CiviCRM page:
```
$ civix generate:page CaseAdmin civicrm/caseadmin
```
Second, edit the new PHP file and update the `run()` function. Create an
`AngularLoader` to load all the JS/CSS files:
```php
public function run() {
$loader = new \Civi\Angular\AngularLoader();
$loader->setModules(array('crmCaseType'));
$loader->setPageName('civicrm/caseadmin');
$loader->load();
parent::run();
}
```
Third, initialize AngularJS on the client-side. You might put this
in the Smarty template:
```html
<div ng-app="crmCaseType">
<div ng-view=""></div>
</div>
```
Finally, flush the cache and visit the new page.
```
# Flush the cache
$ cv flush
# Lookup the URL (Drupal 7 example)
$ cv url 'civicrm/caseadmin/#/caseType'
"http://dmaster.l/civicrm/caseadmin/#/caseType"
```
!!! seealso "See Also: AngularJS Bootstrap Guide"
There are a few ways to boot Angular, such as `ng-app="..."` or
`angular.bootstrap(...);`. These techniques are discussed more in the
[AngularJS Bootstrap Guide](https://code.angularjs.org/1.5.11/docs/guide/bootstrap).
!!! caution "Embedding Angular in other contexts"
The `AngularLoader` does not require a standalone page -- for
example, you might inject Angular onto a pre-existing, non-Angular page
by using `hook_civicrm_pageRun` and `AngularLoader`. Some extensions do
this -- though it remains to be seen whether this is *wise*.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment