diff --git a/CRM/Api4/Page/Api4Explorer.php b/CRM/Api4/Page/Api4Explorer.php index d78ef91ddc55..655800bfe596 100644 --- a/CRM/Api4/Page/Api4Explorer.php +++ b/CRM/Api4/Page/Api4Explorer.php @@ -48,13 +48,10 @@ public function run() { ->addScriptFile('civicrm', 'bower_components/google-code-prettify/bin/prettify.min.js') ->addStyleFile('civicrm', 'bower_components/google-code-prettify/bin/prettify.min.css'); - $loader = new Civi\Angular\AngularLoader(); - $loader->setModules(['api4Explorer']); - $loader->setPageName('civicrm/api4'); - $loader->useApp([ - 'defaultRoute' => '/explorer', - ]); - $loader->load(); + Civi::service('angularjs.loader') + ->addModules('api4Explorer') + ->useApp(['defaultRoute' => '/explorer']); + parent::run(); } diff --git a/CRM/Contact/Page/DashBoard.php b/CRM/Contact/Page/DashBoard.php index 55d48127df4e..48ea3ac19cec 100644 --- a/CRM/Contact/Page/DashBoard.php +++ b/CRM/Contact/Page/DashBoard.php @@ -44,24 +44,21 @@ public function run() { } } - $loader = new Civi\Angular\AngularLoader(); + $loader = Civi::service('angularjs.loader'); + $loader->addModules('crmDashboard'); $loader->setPageName('civicrm/dashboard'); // For each dashlet that requires an angular directive, load the angular module which provides that directive - $modules = []; foreach (CRM_Core_BAO_Dashboard::getContactDashlets() as $dashlet) { if (!empty($dashlet['directive'])) { foreach ($loader->getAngular()->getModules() as $name => $module) { if (!empty($module['exports'][$dashlet['directive']])) { - $modules[] = $name; + $loader->addModules($name); continue; } } } } - $loader->setModules($modules); - - $loader->load(); return parent::run(); } diff --git a/CRM/Core/Region.php b/CRM/Core/Region.php index c7eab5c58418..4d9f733068c6 100644 --- a/CRM/Core/Region.php +++ b/CRM/Core/Region.php @@ -61,6 +61,8 @@ public function render($default, $allowCmsOverride = TRUE) { $this->snippets['default']['markup'] = $default; } + Civi::dispatcher()->dispatch('civi.region.render', \Civi\Core\Event\GenericHookEvent::create(['region' => $this])); + $this->sort(); $cms = CRM_Core_Config::singleton()->userSystem; diff --git a/CRM/Export/Form/Map.php b/CRM/Export/Form/Map.php index 9e4e5fbaf1f5..4bbc28bbc5bb 100644 --- a/CRM/Export/Form/Map.php +++ b/CRM/Export/Form/Map.php @@ -69,10 +69,9 @@ public function preProcess() { ], ]); - // Bootstrap angular and load exportui app - $loader = new Civi\Angular\AngularLoader(); - $loader->setModules(['exportui']); - $loader->load(); + // Add exportui app + Civi::service('angularjs.loader') + ->addModules('exportui'); } public function buildQuickForm() { diff --git a/Civi/Angular/AngularLoader.php b/Civi/Angular/AngularLoader.php index 60252c83b383..6ddd90ed0909 100644 --- a/Civi/Angular/AngularLoader.php +++ b/Civi/Angular/AngularLoader.php @@ -78,8 +78,18 @@ public function __construct() { } /** - * Register resources required by Angular. + * Calling this method from outside this class is deprecated. * + * The correct way to use this class is as a service, which will load automatically. E.g.: + * + * ``` + * Civi::service('angularjs.loader') + * ->addModules('moduleFoo') + * ->useApp(); // Optional, if Civi's routing is desired (full-page apps only) + * ``` + * + * @internal + * @deprecated * @return AngularLoader */ public function load() { @@ -88,9 +98,7 @@ public function load() { if ($this->crmApp !== NULL) { $this->addModules($this->crmApp['modules']); - $region = \CRM_Core_Region::instance($this->crmApp['region']); - $region->update('default', ['disabled' => TRUE]); - $region->add(['template' => $this->crmApp['file'], 'weight' => 0]); + $this->res->addSetting([ 'crmApp' => [ 'defaultRoute' => $this->crmApp['defaultRoute'], @@ -216,6 +224,9 @@ public function useApp($settings = []) { 'file' => 'Civi/Angular/Page/Main.tpl', ]; $this->crmApp = array_merge($defaults, $settings); + $region = \CRM_Core_Region::instance($this->crmApp['region']); + $region->update('default', ['disabled' => TRUE]); + $region->add(['template' => $this->crmApp['file'], 'weight' => 0]); return $this; } @@ -327,6 +338,10 @@ public function getModules() { } /** + * Replace all previously set modules. + * + * Use with caution, as it can cause conflicts with other extensions who have added modules. + * * @param array $modules * @return AngularLoader */ @@ -335,4 +350,14 @@ public function setModules($modules) { return $this; } + /** + * @param \Civi\Core\Event\GenericHookEvent $e + */ + public function onRegionRender($e) { + if ($e->region->_name === $this->region && ($this->modules || $this->crmApp)) { + $this->load(); + $this->res->addScriptFile('civicrm', 'js/crm-angularjs-loader.js', 200, $this->getRegion(), FALSE); + } + } + } diff --git a/Civi/Core/Container.php b/Civi/Core/Container.php index 7a02a56b4517..39749a81bbda 100644 --- a/Civi/Core/Container.php +++ b/Civi/Core/Container.php @@ -123,6 +123,9 @@ public function createContainer() { )) ->setFactory([new Reference(self::SELF), 'createAngularManager'])->setPublic(TRUE); + $container->setDefinition('angularjs.loader', new Definition('Civi\Angular\AngularLoader', [])) + ->setPublic(TRUE); + $container->setDefinition('dispatcher', new Definition( 'Civi\Core\CiviEventDispatcher', [] @@ -351,6 +354,7 @@ public function createAngularManager() { */ public function createEventDispatcher() { // Continue building on the original dispatcher created during bootstrap. + /** @var CiviEventDispatcher $dispatcher */ $dispatcher = static::getBootService('dispatcher.boot'); $dispatcher->addListener('civi.core.install', ['\Civi\Core\InstallationCanary', 'check']); @@ -370,6 +374,7 @@ public function createEventDispatcher() { $dispatcher->addListener('hook_civicrm_eventDefs', ['\Civi\API\Events', 'hookEventDefs']); $dispatcher->addListener('hook_civicrm_eventDefs', ['\Civi\Core\Event\SystemInstallEvent', 'hookEventDefs']); $dispatcher->addListener('hook_civicrm_buildAsset', ['\Civi\Angular\Page\Modules', 'buildAngularModules']); + $dispatcher->addListenerService('civi.region.render', ['angularjs.loader', 'onRegionRender']); $dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Utils_VisualBundle', 'buildAssetJs']); $dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Utils_VisualBundle', 'buildAssetCss']); $dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Core_Resources', 'renderMenubarStylesheet']); diff --git a/ang/crmDashboard.ang.php b/ang/crmDashboard.ang.php index 65f312a7e201..ea27d015c4c3 100644 --- a/ang/crmDashboard.ang.php +++ b/ang/crmDashboard.ang.php @@ -10,7 +10,7 @@ 'css' => ['css/dashboard.css'], 'partials' => ['ang/crmDashboard'], 'partialsCallback' => ['CRM_Contact_Page_DashBoard', 'angularPartials'], - 'basePages' => ['civicrm/dashboard'], + 'basePages' => [], 'requires' => ['crmUi', 'crmUtil', 'ui.sortable', 'dialogService', 'api4'], 'settingsFactory' => ['CRM_Contact_Page_DashBoard', 'angularSettings'], 'permissions' => ['administer CiviCRM'], diff --git a/ext/afform/admin/CRM/AfformAdmin/Page/Base.php b/ext/afform/admin/CRM/AfformAdmin/Page/Base.php index b01400badb77..d9395e9a9706 100644 --- a/ext/afform/admin/CRM/AfformAdmin/Page/Base.php +++ b/ext/afform/admin/CRM/AfformAdmin/Page/Base.php @@ -23,10 +23,9 @@ public function run() { CRM_Utils_System::appendBreadCrumb([$breadCrumb]); // Load angular module - $loader = new Civi\Angular\AngularLoader(); - $loader->setPageName('civicrm/admin/afform'); + $loader = Civi::service('angularjs.loader'); $loader->useApp(); - $loader->load(); + parent::run(); } diff --git a/ext/search/CRM/Search/Page/Admin.php b/ext/search/CRM/Search/Page/Admin.php index 0d6cc55fad4d..315a0efc960e 100644 --- a/ext/search/CRM/Search/Page/Admin.php +++ b/ext/search/CRM/Search/Page/Admin.php @@ -24,12 +24,9 @@ public function run() { CRM_Utils_System::appendBreadCrumb([$breadCrumb]); // Load angular module - $loader = new Civi\Angular\AngularLoader(); - $loader->setPageName('civicrm/admin/search'); - $loader->useApp([ - 'defaultRoute' => '/list', - ]); - $loader->load(); + Civi::service('angularjs.loader') + ->useApp(['defaultRoute' => '/list']); + parent::run(); } diff --git a/ext/search/CRM/Search/Page/Search.php b/ext/search/CRM/Search/Page/Search.php index ffd4ce40cde7..d73e1b274f53 100644 --- a/ext/search/CRM/Search/Page/Search.php +++ b/ext/search/CRM/Search/Page/Search.php @@ -18,13 +18,7 @@ class CRM_Search_Page_Search extends CRM_Core_Page { public function run() { - Civi::resources()->addBundle('bootstrap3'); - - // Load angular module - $loader = new Civi\Angular\AngularLoader(); - $loader->setPageName('civicrm/search'); - $loader->useApp(); - $loader->load(); + Civi::service('angularjs.loader')->useApp(); if (CRM_Core_Permission::check('administer CiviCRM')) { CRM_Utils_System::appendBreadCrumb([['title' => E::ts('Search Kit'), 'url' => CRM_Utils_System::url('civicrm/admin/search')]]); diff --git a/js/crm-angularjs-loader.js b/js/crm-angularjs-loader.js new file mode 100644 index 000000000000..5a2fe57f2198 --- /dev/null +++ b/js/crm-angularjs-loader.js @@ -0,0 +1,11 @@ +// http://civicrm.org/licensing +(function($, _) { + "use strict"; + + $(document).on('crmLoad', function(e) { + $('crm-angular-js', e.target).not('.ng-scope').each(function() { + angular.bootstrap(this, $(this).attr('modules').split()); + }); + }); + +})(CRM.$, CRM._); diff --git a/templates/CRM/Export/Form/Map.tpl b/templates/CRM/Export/Form/Map.tpl index dda47d32d2e4..24eb7a98167c 100644 --- a/templates/CRM/Export/Form/Map.tpl +++ b/templates/CRM/Export/Form/Map.tpl @@ -22,9 +22,9 @@ {include file="CRM/common/WizardHeader.tpl"}
-