diff --git a/docs/framework/angular/changeset.md b/docs/framework/angular/changeset.md new file mode 100644 index 00000000..7307a7d4 --- /dev/null +++ b/docs/framework/angular/changeset.md @@ -0,0 +1,44 @@ +# 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(' +
+ +
+ '); + }); + $angular->add($changeSet); +} +``` + +``` +cv ang:html:list +cv ang:html:show +cv ang:html:show --diff +``` diff --git a/docs/framework/angular/files.md b/docs/framework/angular/files.md new file mode 100644 index 00000000..f7acd243 --- /dev/null +++ b/docs/framework/angular/files.md @@ -0,0 +1,73 @@ +# 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 `
`. + * `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'}`. diff --git a/docs/framework/angular/index.md b/docs/framework/angular/index.md new file mode 100644 index 00000000..d20fb051 --- /dev/null +++ b/docs/framework/angular/index.md @@ -0,0 +1,101 @@ +# 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 + + 1: + 2: + 3: + 4: + 5: + 6: + 7: + 8:
...site wide header...
+ 9:
+10:
...site wide footer...
+11: +12: +``` + +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 diff --git a/docs/framework/angular/loader.md b/docs/framework/angular/loader.md new file mode 100644 index 00000000..3ba2473c --- /dev/null +++ b/docs/framework/angular/loader.md @@ -0,0 +1,186 @@ +# 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 `