Skip to content
Snippets Groups Projects
Commit cdeea026 authored by Sean Madsen's avatar Sean Madsen Committed by GitHub
Browse files

Merge pull request #172 from totten/master-angular

CRM-20600 - Add chapters for AngularJS and AssetBuilder
parents 49120c19 0a33f126
No related branches found
No related tags found
No related merge requests found
# AngularJS: Changesets
!!! caution "Work in progress"
This documentation is still a work in progress.
The [Quick Start](quickstart.md) and [Loader](loader.md) provide examples of
creating *new* screens. But what if you need to alter an *existing* screen?
CiviCRM allows third-parties to define *changesets* which programmatically
manipulate Angular content before sending it to the client.
## Background
Most AngularJS tutorials focus on idealized projects where a single
developer or product-owner exercises full authority over their application.
But CiviCRM is an _ecosystem_ with a range of stakeholders, including many
developers (authoring indpendent extensions) and administrators (managing
independent deployments with independent configurations).
...
## tldr
```php
function mailwords_civicrm_alterAngular(\Civi\Angular\Manager $angular) {
$changeSet = \Civi\Angular\ChangeSet::create('inject_mailwords')
// ->requires('crmMailing', 'mailwords')
->alterHtml('~/crmMailing/BlockSummary.html',
function (phpQueryObject $doc) {
$doc->find('.crm-group')->append('
<div crm-ui-field="{name: \'subform.mailwords\', title: ts(\'Keywords\')}">
<input crm-ui-id="subform.mailwords" class="crm-form-text" name="mailwords" ng-model="mailing.template_options.keywords">
</div>
');
});
$angular->add($changeSet);
}
```
```
cv ang:html:list
cv ang:html:show <file>
cv ang:html:show <file> --diff
```
# AngularJS: File names
As a developer working with CiviCRM-Angular, you write *Angular modules* --
these modules are composed of various JS/CSS/HTML files which define the
*services*, *directives*, *controllers*, and *routes*.
For sake of predictability, these files are placed in the `ang/` folder, and
they follow a naming convention.
!!! note "How does this work with `civix`?"
When you generate Angular code via `civix`, the files are
named according to convention.
One file, `ang/{mymodule}.ang.php`, provides instructions for the
file-loader. It lists any files which match the naming
convention.
!!! note "What if I don't use `civix`? What if my code doesn't follow the naming convention?"
The file-loader needs some information about the name and location of
your AngularJS code, but you don't need to follow the convention. You
can configure it via hook. See: [AngularJS: Loading](/framework/angular/loader.md).
## Abridged convention
The abridged convention applies to small Angular modules with a narrow
purpose -- such as defining a singular `service` or `directive`. These
modules only have 2 or 3 files.
* `ang/{mymodule}.ang.php` - General metadata about the module (per [hook_civicrm_angularModules](/hooks/hook_civicrm_angularModules.md)).
* `ang/{mymodule}.js` - All Javascript for the module.
* `ang/{mymodule}.css` - All CSS for the module (if applicable).
* `ang/{mymodule}.md` - Developer documentation about the module (if applicable).
## Full convention
The full convention applies to bigger Angular modules which serve a broader
purpose -- such as defining a new screen with a series of related
`directive`s, `controller`s, and `service`s. Each of these elements may
have multiple aspects (JS/HTML/CSS).
__Module Files__
* `ang/{mymodule}.ang.php` - General metadata about the module (per [hook_civicrm_angularModules](/hooks/hook_civicrm_angularModules.md)).
* `ang/{mymodule}.js` - General metadata about the module.
* `ang/{mymodule}.css` - General CSS that applies throughout the module (if applicable).
* `ang/{mymodule}.md` - Developer documentation about the module (if applicable).
__Directive Files__
* `ang/{mymodule}/{FooBar}.js` - The declaration and logic for a directive named `mymoduleFooBar` or `<div mymodule-foo-bar>`.
* `ang/{mymodule}/{FooBar}.html` - The main/default template for the directive (if applicable).
* `ang/{mymodule}/{FooBar}/{Extra}.html` - If you have multiple templates used by the same directive (e.g. via `ng-include` or conditional logic), then put them in a subdir.
* `ang/{mymodule}/{FooBar}.css` - Any CSS specifically intended for `mymoduleFooBar` (if applicable).
* `ang/{mymodule}/{FooBar}.md` - Developer documentation about the directive (if applicable).
__Controller Files__ (These follow the same convention as directives, but they have the suffix `Ctrl`.)
* `ang/{mymodule}/{FooBar}Ctrl.js` - The declaration and logic for a controller named `MymoduleFooBarCtrl`.
* `ang/{mymodule}/{FooBar}Ctrl.html` - The main/default template for the controller (if applicable).
* `ang/{mymodule}/{FooBar}Ctrl/{Extra}.html` - If you have multiple templates used with the same controller (e.g. via `ng-include` or conditional logic), then put them in a subdir.
* `ang/{mymodule}/{FooBar}Ctrl.css` - Any CSS specifically intended for `MymoduleFooBarCtrl` (if applicable).
* `ang/{mymodule}/{FooBar}Ctrl.md` - Developer documentation about the controller (if applicable).
__Service Files__
* `ang/{mymodule}/{FooBar}.js` - The declaration and logic for a service named `mymoduleFooBar`.
* `ang/{mymodule}/{FooBar}.md` - Developer documentation about the service (if applicable).
!!! tip "Tip: Use tilde (`~`) to load HTML templates"
When writing code for Angular, you might use an expression like
`{templateUrl: 'https://example.org/FooBar.html'}`. However,
constructing a full URL that works in every Civi deployment would be
complex. Instead, use the tilde prefix. For example, `{templateUrl: '~/mymodule/FooBar.html'}`.
# AngularJS: Overview
AngularJS is a client-side framework for development of rich web
applications. The core CiviCRM application uses AngularJS for several
administrative screens, and extensions increasingly use AngularJS for
"leaps" that add or replace major parts of the application.
This documentation aims to explain how AngularJS works within a CiviCRM
context.
## Two cultures
CiviCRM is an extensible PHP application (similar to Drupal, Joomla, or
WordPress). In this culture, the common expectation is that an
*administrator* installs the main application. To customize it, they
download, evaluate, and configure a set of business-oriented modules. The
administrator's workflow is dominated by web-based config screens and CLI
commands.
AngularJS is a frontend, Javascript development framework. In this culture,
the expectation is that a *developer* creates a new application. To
customize it, they download, evaluate, and configure a set of
function-oriented libraries. The developer's workflow is dominated by CLI's
and code.
The CiviCRM-AngularJS integration must balance the expectations of these
two cultures. The balance works as follows:
* __Build/Activation__: The process of building or activating modules
should meet administrators' expectations. It should be managed by the
PHP application. (This means that you won't see `gulp` or `grunt`
orchestrating the final build -- because PHP logic fills that role.)
* __Frontend Code uses Angular (JS+HTML)__: The general structure of the
Javascript and HTML files should meet the frontend developers'
expectations. These files should be grounded in the same notations and
concepts as the upstream AngularJS framework. (This means that AngularJS
is not abstracted, wrapped, or mapped by an intermediary like
HTML_QuickForm, Symfony Forms or Drupal Form API.)
* __Backend Code uses Civi API (PHP)__: The general structure of
web-services should meet the backend developers' expectations. These are
implemented in PHP (typically with CiviCRM APIv3).
## Basics
AngularJS is a client-side Javascript framework, and it interacts with
CiviCRM in two major ways. To see this, let's consider an example AngularJS
page -- it's an HTML document that looks a lot like this:
```html
<!-- URL: https://example.org/civicrm/a -->
1: <html>
2: <head>
3: <link rel="stylesheet" href="**all the CSS files**" />
4: <script type="text/javascript" src="**all the Javascript files**"></script>
5: <script type="text/javascript">var CRM = {**prefetched settings/data**};</script>
6: </head>
7: <body>
8: <div>...site wide header...</div>
9: <div ng-app="crmApp"></div>
10: <div>...site wide footer...</div>
11: </body>
12: </html>
```
The first interaction comes when CiviCRM generates the initial HTML page:
* CiviCRM listens for requests to the path `civicrm/a`. (It does this in a
way which is compatible with multiple CMSs -- Drupal, Joomla, WordPress, etc.)
* CiviCRM builds the list of CSS/JS/JSON resources in lines 3-5. (It does this in a
way which allows extensions to add new CSS/JS/JSON. See also:
[Resource Reference](https://wiki.civicrm.org/confluence/display/CRMDOC/Resource+Reference).)
* CiviCRM ensures that the page includes the site-wide elements, such as
lines 8 and 10. (It does this in a way which is compatible with multiple CMSs.)
Once the page is loaded, it works just like any AngularJS 1.x application.
It uses concepts like `ng-app`, "module", "directive", "service", "component", and
"partial".
!!! seealso "Read more about AngularJS 1.x"
A good resource for understanding AngularJS concepts is [the
official AngularJS tutorial](https://code.angularjs.org/1.5.11/docs/tutorial).
The second interaction comes when the AngularJS application loads or stores
data. This uses the CiviCRM API. Key concepts in CiviCRM API include
"entity", "action", "params", the "API Explorer", and the bindings for PHP/Javascript/CLI.
!!! seealso "Read more about CiviCRM API"
A good resource for understanding CiviCRM API concepts is the [APIv3:
Intro](/api/index.md).
In the remainder of this document, we'll try to avoid in-depth discussion
about the internals of AngularJS 1.x or APIv3. You should be able to follow
the discussion if you have a beginner-level understanding of both.
## Additional pages
* [Quick Start](quickstart.md): How to create a new screen in the CiviCRM-Angular UI
* [File Names](files.md): How AngularJS files are named in `civicrm-core` and `civix`
* [Loader](loader.md): How to find and load JS/CSS files for CiviCRM-Angular
* [Changesets](changeset.md): How a third-party can modify the content of a CiviCRM-Angular UI
# AngularJS: Loader
What happens when a user visits a CiviCRM-Angular page? For example, let's
consider this URL:
* `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 -- such as
`ngRoute`, `crmAttachment`, `crmCaseType`, `crmUi`, and so on.
2. (Client-side) AngularJS processes the HTML/JS/CSS. 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.
!!! caution "Caution: Discusses new/experimental interfaces"
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.
## Library of 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
For a full list, try passing --user=[username].
+-------------------+-------------+------------------------------------------------------------------------------------+
| name | basePages | requires |
+-------------------+-------------+------------------------------------------------------------------------------------+
| angularFileUpload | civicrm/a | |
| bw.paging | (as-needed) | |
| civicase | (as-needed) | crmUi, crmUtil, ngRoute, angularFileUpload, bw.paging, crmRouteBinder, crmResource |
| crmApp | civicrm/a | |
| 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. This may reveal permission-dependent modules. Ex:
```
$ cv ang:module:list --user=admin
```
Under-the-hood, this library of modules is built 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'),
);
}
```
!!! 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
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();
$loader->setModules(array('crmApp'));
$loader->setPageName('civicrm/a');
$loader->load();
```
The `load()` function determines the necessary JS/CSS/HTML/JSON resources
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(...)` [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`.
!!! note "How does `load()` output the `<script>` tag(s)?"
`load()` uses [CRM_Core_Resources](https://wiki.civicrm.org/confluence/display/CRMDOC/Resource+Reference)
to register JS/CSS files.
## Other 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"
In the example, we created a new, standalone page. But you can use
`AngularLoader` in other ways -- eg, you might listen for
`hook_civicrm_pageRun` and embed Angular onto a pre-existing,
non-Angular page. Some extensions do this -- though it remains to be
seen whether this is *wise*.
# AngularJS: Quick Start
Let's create a new CiviCRM extension with an AngularJS page. It will present
a small "About Me" screen.
## Create a CiviCRM extension
First, we'll need a skeletal CiviCRM extension:
```
$ civix generate:module org.example.aboutme
Initalize module org.example.aboutme
Write org.example.aboutme/info.xml
Write org.example.aboutme/aboutme.php
Write org.example.aboutme/aboutme.civix.php
Write org.example.aboutme/LICENSE.txt
```
## Create an Angular module
Of course, AngularJS also has its own module system -- any Angular routes,
directives, or services need to live within an Angular `module`. Let's
create one.
```
$ cd org.example.aboutme
$ civix generate:angular-module
Initialize Angular module "aboutme"
Write ang/aboutme.ang.php
Write ang/aboutme.js
Write ang/aboutme.css
```
!!! tip "Tip: Angular module names"
By default, `civix` assumes that your Angular module name matches your
extension name. In this case, both are named `aboutme`. However, this
is not required -- the option `--am` can specify a different name. This
can be useful if you want to organize your code into multiple modules.
!!! note "Note: `ang/` folder"
By convention, AngularJS source code is stored in the `ang/` folder, and
each item is named after its module. The convention is discussed in
more detail in [AngularJS: File Names](/framework/angular/files.md)
The first file, `ang/aboutme.ang.php`, provides metadata for the PHP-based
file-loader, e.g.
```php
return array(
'requires' => array('ngRoute', 'crmUi', 'crmUtil'),
'js' => array('ang/aboutme.js', 'ang/aboutme/*.js', 'ang/aboutme/*/*.js'),
'css' => array('ang/aboutme.css'),
'partials' => array('ang/aboutme'),
'settings' => array(),
);
```
The second file, `ang/aboutme.js`, provides metadata for the JS-based
Angular runtime, e.g.
```js
angular.module('aboutme', [
'ngRoute', 'crmUi', 'crmUtil'
]);
```
!!! tip "Tip: angular.module() and CRM.angRequires()"
The list of dependencies is declared once in PHP and once in JS. To
remove this duplication, call `CRM.angRequires(...)`, as in:
```js
angular.module('aboutme', CRM.angRequires('aboutme'));
```
## Add an Angular-based page
Now let's add a new Angular-based page. This page will require a `route`
with a `controller` and an HTML template. The command
`civix generate:angular-page` will create each of these:
```
$ civix generate:angular-page EditCtrl about/me
Initialize Angular page "AboutmeEditCtrl" (civicrm/a/#/about/me)
Write ang/aboutme/EditCtrl.js
Write ang/aboutme/EditCtrl.html
```
If you inspect the code, you'll find a basic AngularJS app which uses
`$routeProvider`, `angular.module(...).controller(...)`, and so on.
The generated code will display a small "About Me" screen with the current
user's first-name and last-name.
!!! tip "Tip: Flush caches _or_ enable debug mode"
By default, CiviCRM aggregates AngularJS files and caches them. You can
flush this cache manually (`cv flush`). However, it may be easier to
disable some of the aggregation/caching features by [enabling debug
mode](/tools/debugging.md).
## Open the page
Finally, we'd like to take a look at this page in the web-browser.
By default, CiviCRM combines all Angular modules into one page, `civicrm/a`.
The URL of this page depends on your system configuration. Here are a few
examples:
```
# Example: Lookup the URL on Drupal 7
$ cv url 'civicrm/a/#/about/me'
"http://dmaster.l/civicrm/a/#/about/me"
# Example: Lookup the URL on WordPress
$ cv url 'civicrm/a/#/about/me'
"http://wpmaster.l/wp-admin/admin.php?page=CiviCRM&q=civicrm/a/#/about/me"
```
!!! tip "Tip: Open the browser from the command-line"
If you're developing locally on a Linux/OSX workstation, pass the
option `--open` to automatically open the page in a web browser.
# Asset Builder
The `AssetBuilder` manages lazily-generated assets, such as aggregated
JS/CSS files. The first time you request a lazy asset, the `AssetBuilder`
fires a hook which builds the content. The content is stored in a cache
file, and subsequent requests use the cache file.
Lazy assets are simultaneously dynamic and static:
* __Dynamic__: They vary depending on the current system configuration.
You cannot lock-in a singular version of the asset because each
deployment may need a slightly different version.
* __Static__: Within a given deployment, the asset is not likely to change.
It can even be served directly by the web-server (without the overhead
of PHP/CMS/Civi bootstrap)
!!! note "Example: Batch loading Angular HTML"
When visiting the AngularJS base page `civicrm/a`, one needs to load a
mix of many small HTML templates. It's ideal to aggregate them into one
bigger file and reduce the number of round-trip HTTP requests.
This asset is _dynamic_ because extensions can add or modify HTML
templates. Two different deployments would have different HTML
templates (depending on the mix of extensions). Yet, within a
particular deployment, the content is _static_ because the HTML doesn't
actually change at runtime.
!!! tip "Tip: Caching and development"
If you are developing or patching assets, then the caching behavior may
get distracting. To bypass the cache, navigate to
__Administer > System Settings > Debugging__ and enable debugging.
## Usage: Simple asset
There are generally two aspects to using `AssetBuilder` -- creating a URL
for the asset, and defining the content of the asset.
For example, suppose we wanted to define a static file named
`api-fields.json` which lists all the fields of all the API entities.
```php
// Get the URL to `api-fields.json`.
$url = \Civi::service('asset_builder')->getUrl('api-fields.json');
...
// Define the content of `api-fields.json` using `hook_civicrm_buildAsset`.
function mymodule_civicrm_buildAsset($asset, $params, &$mimeType, &$content) {
if ($asset !== 'api-fields.json') return;
$entities = civicrm_api3('Entity', 'get', array());
$fields = array();
foreach ($entities['values'] as $entity) {
$fields[$entity] = civicrm_api3($entity, 'getfields');
}
$mimeType = 'application/json';
$content = json_encode($fields);
}
```
!!! note "What does `getUrl(...)` do?"
In normal/production mode, `getUrl(...)` checks to see if the asset
already exists. If necessary, it fires the hook and saves the asset to
disk. Finally, it returns the direct URL for the asset -- which allows
the user to fetch it quickly (without extra PHP/CMS/Civi bootstrapping).
In debug mode, `getUrl(...)` returns the URL of a PHP script. The PHP
script will build the asset every time it's requested.
## Usage: Parameterized asset
What should you do if you need to create a series of similar assets, based on slightly
different permutations or configurations? Add parameters (aka `$params`).
For example, we might want a copy of `api-fields.json` which only includes a
handful of chosen entities. Simply pass the chosen entities into
`getUrl()`, then update the definition to use `$params['entities']`, as in:
```php
// Get the URL to `api-fields.json`. This variant only includes
// a few contact-related entities.
$contactEntitiesUrl = \Civi::service('asset_builder')
->getUrl('api-fields.json', array(
'entities' => array('Contact', 'Phone', 'Email', 'Address'),
)
);
// Get the URL to `api-fields.json`. This variant only includes
// a few case-related entities.
$caseEntitiesUrl = \Civi::service('asset_builder')
->getUrl('api-fields.json', array(
'entities' => array('Case', 'Activity', 'Relationship'),
)
);
...
// Define the content of `api-fields.json` using `hook_civicrm_buildAsset`.
function mymodule_civicrm_buildAsset($asset, $params, &$mimeType, &$content) {
if ($asset !== 'api-fields.json') return;
$fields = array();
foreach ($params['entities'] as $entity) {
$fields[$entity] = civicrm_api3($entity, 'getfields');
}
$mimeType = 'application/json';
$content = json_encode($fields);
}
```
!!! note "Note: Parmaters and caching"
Each combination of (`$asset`,`$params`) will be cached separately.
!!! tip "Tip: Economize on parameters"
In debug mode, all parameters are passed as part of the URL.
`AssetBuilder` will try to compress them, but it can only do so much.
Fundamentally, long `$params` will produce long URLs.
## Other considerations
!!! note "Compare: How does AssetBuilder differ from [Assetic](https://github.com/kriswallsmith/assetic)?"
Both are written in PHP, but they address differet parts of the process:
* `AssetBuilder` provides URL-routing, caching, and parameterization.
Its strength is defining a *lazy lifecycle* for the assets.
* `Assetic` provides a library of generators and filters. Its strength
is defining the *content* of an asset.
You could use them together -- e.g. in `hook_civicrm_buildAsset`,
declare a new asset and use `Assetic` to build its content.
!!! caution "Caution: Confidentiality and lazy assets"
The current implementation does not take aggressive measures to keep
assets confidential. For example, an asset built from public JS files
is fine, but an asset built from permissioned data (contact-records
or activity-records) could be problematic.
It may be possible to fix this by computing URL digests differently, but
(at time of writing) we don't have a need/use-case.
......@@ -16,12 +16,23 @@ available in CiviCRM 4.6+.
## Parameters
- &$angularModules - an array containing a list of all Angular
modules.
* `&$angularModules` - an array containing a list of all Angular modules. Each item is keyed by the Angular module name.
Each item `angularModules` may include these properties:
* `ext` (`string`): The name of the CiviCRM extension which has the source-code.
* `js` (`array`): List of Javascript files. May use the wildcard (`*`). Relative to the extension.
* `css` (`array`): List of CSS files. May use the wildcard (`*`). Relative to the extension.
* `partials` (`array`): List of HTML folders. Relative to the extension.
* `requires` (`array`): List of AngularJS modules required by this module. Default: `array()`. (`v4.7.21+`)
* `basePages` (`array`): Uncondtionally load this module onto the given Angular pages. (`v4.7.21+`)
* If omitted, the default is `array('civicrm/a')`. This provides backward compatibility with behavior since `v4.6+`.
* For a utility that should only be loaded on-demand, use `array()`.
* For a utility that should be loaded in all pages use, `array('*')`.
## Returns
- null
* `null`
## Example
......@@ -32,6 +43,8 @@ available in CiviCRM 4.6+.
);
$angularModules['myBigAngularModule'] = array(
'ext' => 'org.example.mymod',
'requires' => array('ngRoute', 'crmUi'),
'basePages' => array('civicrm/a'),
'js' => array('js/part1.js', 'js/part2.js'),
'css' => array('css/myAngularModule.css'),
'partials' => array('partials/myBigAngularModule'),
......
# hook_civicrm_buildAsset
## Description
This hook fires whenever the system builds a semi-dynamic asset. For more
discussion, see [AssetBuilder](/framework/asset-builder.md).
## Definition
hook_civicrm_buildAsset($asset, $params, &$mimeType, &$content)
## Parameters
* `$asset` (string): the logical file name of an asset (ex: `hello-world.json`)
* `$params` (array): an optional set of parameters describing how to build the asset
* `$mimeType` (string, output): the MIME type of the asset (ex: `application/json`)
* `$content` (string, output): the full content of the asset
## Returns
* null
## Example
```php
function mymodule_civicrm_buildAsset($asset, $params, &$mimeType, &$content) {
if ($asset === 'hello-world.json') {
$mimeType = 'application/json';
$content = json_encode(array('hello', 'world'));
}
}
```
\ No newline at end of file
......@@ -43,6 +43,13 @@ pages:
- Troubleshooting: extensions/troubleshooting.md
- Advanced patterns: extensions/advanced.md
- Framework Reference:
- AngularJS:
- "AngularJS: Intro": framework/angular/index.md
- "AngularJS: Quick Start": framework/angular/quickstart.md
- "AngularJS: File Names": framework/angular/files.md
- "AngularJS: Loader": framework/angular/loader.md
- "AngularJS: Changesets": framework/angular/changeset.md
- Asset Builder: framework/asset-builder.md
# Bootstrap: /framework/bootstrap.md
# Cache: /framework/cache.md
# Components: /framework/components.md
......@@ -168,6 +175,7 @@ pages:
- hook_civicrm_alterSettingsMetaData: hooks/hook_civicrm_alterSettingsMetaData.md
- hook_civicrm_angularModules: hooks/hook_civicrm_angularModules.md
- hook_civicrm_apiWrappers: hooks/hook_civicrm_apiWrappers.md
- hook_civicrm_buildAsset: hooks/hook_civicrm_buildAsset.md
- hook_civicrm_buildStateProvinceForCountry: hooks/hook_civicrm_buildStateProvinceForCountry.md
- hook_civicrm_check: hooks/hook_civicrm_check.md
- hook_civicrm_config: hooks/hook_civicrm_config.md
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment