From 8af94e00d95fdf511dfe1cb3355f2a806d18578d Mon Sep 17 00:00:00 2001 From: Tim Otten <totten@civicrm.org> Date: Fri, 2 Jun 2017 17:06:09 -0700 Subject: [PATCH] Add chapters for AngularJS --- docs/framework/angular.md | 99 ++++++++++++++++++++++ docs/framework/angular/files.md | 70 ++++++++++++++++ docs/framework/angular/loader.md | 94 +++++++++++++++++++++ docs/framework/angular/modify.md | 14 ++++ docs/framework/angular/quickstart.md | 120 +++++++++++++++++++++++++++ 5 files changed, 397 insertions(+) create mode 100644 docs/framework/angular.md create mode 100644 docs/framework/angular/files.md create mode 100644 docs/framework/angular/loader.md create mode 100644 docs/framework/angular/modify.md create mode 100644 docs/framework/angular/quickstart.md diff --git a/docs/framework/angular.md b/docs/framework/angular.md new file mode 100644 index 00000000..2598672a --- /dev/null +++ b/docs/framework/angular.md @@ -0,0 +1,99 @@ +# 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: + + * __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.) + * __Build__: The process of building or activating modules should meet + administrators' expectations. They should be managed by the PHP + application. (This means that you won't see `gulp` or `grunt` managing + the final build -- because PHP logic fills that role.) + * __Web Services__: 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: <script type="text/javascript" src="**all the Javascript files**"></script> + 4: <link rel="stylesheet" href="**all the CSS files**" /> + 5: </head> + 6: <body> + 7: <div>...site wide header...</div> + 8: <div ng-app="crmApp"></div> + 9: <div>...site wide footer...</div> +10: </body> +11: </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 JS/CSS files in lines 3-4. (It does this in a + way which allows extensions to add new JS/CSS files.) + * CiviCRM ensures that the page includes the site-wide elements, such as + lines 7 and 9. (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/general.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](angular/quickstart.md): How to create a new screen in the CiviCRM-Angular UI + * [File Names](angular/files.md): How AngularJS files are named in `civicrm-core` and `civix` + * [Loader](angular/loader.md): How JS/CSS files are loaded + * [Modifications](angular/modify.md): How a third-party can modify the content of a CiviCRM-Angular UI diff --git a/docs/framework/angular/files.md b/docs/framework/angular/files.md new file mode 100644 index 00000000..0fc28b31 --- /dev/null +++ b/docs/framework/angular/files.md @@ -0,0 +1,70 @@ +# 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 *HTML partials*. + +For sake of predictability, these files follow a naming convention. All +major Angular files are placed in the `ang/` folder. + +!!! note "How does this work with `civix`?" + When you generate Angular code via `civix`, the files are + named according to convention. + + One of the files, `ang/{mymodule}.ang.php` provides instructions for the + file-loader. + +!!! note "What if my code doesn't follow the naming convention? What if I don't use `civix`?" + 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 + +Some Angular modules have a very 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) + +## Full convention + +Some Angular modules have 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. + +__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). + +__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). + +__Service Files__ + + * `ang/{mymodule}/{FooBar}.js` - The declaration and logic for a service named `mymoduleFooBar`. + +!!! 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'}`. + +<!-- + Proposed Amendment + Put documentation and examples for each directive or controller in a `*.md` file, adjacent to the `*.js` file. The format is handy for reading/writing/code-snippets, and it won't bloat the final `*.js` output. +--> diff --git a/docs/framework/angular/loader.md b/docs/framework/angular/loader.md new file mode 100644 index 00000000..05bf3415 --- /dev/null +++ b/docs/framework/angular/loader.md @@ -0,0 +1,94 @@ +# AngularJS: Loader + +What happens when a user visits a CiviCR-Angular page, such as +`https://example.org/civicrm/a/#/mailing/new`? Broadly speaking, two steps: + + 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. + +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 default base page (`civicrm/a`) + +CiviCRM includes a default base-page -- any module can add new routes on +this page. For example, the `crmMailing` module defines a route +`mailing/new`. You can view this at: + + * `https://example.org/civicrm/a/#/mailing/new` + +The default base-page is special because *all registered Angular modules +will be included by default*. You can expect the markup to look roughly +like this: + +```html +<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> +``` + +The PHP application instantiates `AngularLoader` + +```php +$loader = new \Civi\Angular\AngularLoader(); +$loader->setPageName('civicrm/a'); +$loader->setModules(array('crmApp')); +$loader->load(); +``` + +The `load()` function determines the necessary JS/CSS/HTML/JSON resources +and loads them on the page. Roughly speaking, it outputs: + + + +More specifically, the `load()` function gets a list of all available +Angular modules (including their JS/CSS/HTTML files). Then it loads the +files for `crmApp` as well as any dependencies (like `crmUi`). + +The most important thing to understand is how it *gets the list of Angular +modules*. A few Angular modules are bundled with core (eg `crmUi` or +`crmUtil`), but most new Angular modules should be loaded via +[hook_civicrm_angularModules](/hooks/hook_civicrm_angularModules.md) + +For example, if you created an extension `org.example.foobar` with an +Angular module named `myBigAngularModule`, then the hook might look like: + +```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 "`civix` code-generator" + In practice, you usually don't need to implement. The `civix` + code-generator creates a file named `ang/{mymodule}.ang.php` and + automatically loads as part of `hook_civicrm_angularModules`. + + diff --git a/docs/framework/angular/modify.md b/docs/framework/angular/modify.md new file mode 100644 index 00000000..cc0ca5f4 --- /dev/null +++ b/docs/framework/angular/modify.md @@ -0,0 +1,14 @@ +# AngularJS: Alteration + +```php +function example_civicrm_alterAngular($angular) { + $angular->add(\Civi\Angular\ChangeSet::create('mychanges') + ->alterHtml('~/crmMailing/EditMailingCtrl/2step.html', function(phpQueryObject $doc) { + $doc->find('[ng-form="crmMailingSubform"]')->attr('cat-stevens', 'ts(\'wild world\')'); + }) + ); +} +``` + + +cv ang:html:show \ No newline at end of file diff --git a/docs/framework/angular/quickstart.md b/docs/framework/angular/quickstart.md new file mode 100644 index 00000000..7c1fde4b --- /dev/null +++ b/docs/framework/angular/quickstart.md @@ -0,0 +1,120 @@ +# 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. + + +The first file, `ang/aboutme.ang.php`, configures the file-loader: + +```php +return array( + 'requires' => array('crmUi', 'crmUtil', 'ngRoute'), + 'js' => array( + 'ang/aboutme.js', + 'ang/aboutme/*.js', + 'ang/aboutme/*/*.js', + ), + 'css' => array ('ang/aboutme.css'), + 'partials' => array('ang/aboutme'), + 'settings' => array(), +); +``` + +And the second file, `ang/aboutme.js`, declares the Angular module's dependencies. + +```js + angular.module('aboutme', [ + 'crmUi', 'crmUtil', 'ngRoute' + ]); +``` + +!!! tip "Tip: angular.module() and angular.crmDepends()" + The list of dependencies is declared once in PHP and once in JS. To + remove this duplication, change `angular.module('aboutme',...)` to + `angular.crmDepends('aboutme');` + +!!! note "Note: `ang/` folder" + By convention, AngularJS source code is stored in the `ang/` folder. + The convention is discussed in more detail in [AngularJS: File + Names](/framework/angular/files.md) + +## Add an Angular route, etal + +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](/dev-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. -- GitLab