diff --git a/docs/framework/angular/loader.md b/docs/framework/angular/loader.md index 4041166b99010e34de41cc3e3b9efdc07d2fdc1d..6d1db0860561182182108c616e76f3510092ae0c 100644 --- a/docs/framework/angular/loader.md +++ b/docs/framework/angular/loader.md @@ -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*.