Good morning from Canada. I am trying to create an extension with angularjs forms. I have followed the development guide and have created the angularjs page but I cannot get it to display in CiviCRM. I have a Tabset function used with Events, it calls the php page, which in turn I have the AngularLoader configuration. When it gets to the useApp() command I get an Unknown Path error. The examples in the doc don't go as far as to show how to do this within an extension or as a new tab under event. Even if you can point me to an example extension, I can analyse it to try and figure it out. The default_route value in the useApp() command I'm assuming is the ang entry in angular? When I do a cv ang:html:list the page I want is listed (as well as ang:module:list has the module). Thanks for any advance or help you can give. I ran all of the suggested civix commands from the documentation. From the php called from the extension: ...

$loader = new \Civi\Angular\AngularLoader();
//$loader->useApp(['defaultRoute' => '/trialadmin']);

And in my module I have

if ($eventID) {
$url = CRM_Utils_System::url( 'civicrm/event/manage/TrialAdmin', "reset=1&force=1&eventid=$eventID&snippet=5&angularDebug=1" );
$tab['Administration'] = array(
  'title' => "Administration",
  'link' => $url,
  'valid' => 1,
  'active' => 1,
  'current' => false,

output from cv ang:module:list trialadmin | civicrm/a | crmUi, crmUtil, ngRoute

output from cv ang:html:list trialadmin/TrialDetails.html

I've tried different arrangements of the defaultroute but I'm not clear on why I'm getting the unknown path error. If I comment out the useApp line it continues on to use the *.tpl that I originally had in place. I wanted to do this with the newer forms format rather than the quickforms as suggested in the docs.

2 Answers 2


Ok, sorted it out with some suggestions and examples.

$loader = new \Civi\Angular\AngularLoader(); $loader->setModules(array('trialadmin')); $loader->setPageName('Administration'); $loader->useApp(['activeRoute' => '/trialadmin/trialdetails']); $loader->load();

Worked with this, but I also used the trace and debug in the browser to determine there likely was another issue with the form actually loading. Regardless, I've been able to sort it out.


You are trying to inject an angular snippet into an otherwise non-angular page, and you're wanting to place it into a new tab in the Manage Events page. This is doable but there are a few things to understand:

  1. Yours might not be the only Angular snippet on the page. Other extensions may want to add them in the future.
  2. Angular routing requires control of the URL, which your snippet shouldn't assume it has, so I don't recommend using crmApp or ng-route.
  3. Tab content loads via ajax by default, but it's also possible to pre-load it by supplying a "template" property. This makes Angular stuff simpler.

I recently faced these same challenges while adding this feature to the new Form Builder (Afform) extension, and ended up improving the way AngularJS loads in Civi. It's available in version 5.37+ and I highly recommend you start using this new service:

In your hook you'd do this:

function my_hook_civicrm_tabset($tabsetName, &$tabs, $context) {
  //check if the tab set is Event manage
  if ($tabsetName == 'civicrm/event/manage') {
    if (!empty($context['event_id'])) {
      // Add our Angular module to the page
      // Add the tab
      $tab['Administration'] = [
        'title' => "Administration",
        'template' => 'path/to/your/template.tpl',
        'valid' => 1,
        'active' => 1,
        'current' => false,

Note that I didn't supply a URL but instead gave a 'template' which will cause the tab content to load immediately. So now you've got your module loaded and don't have to worry about ajax for the tab content.

In your tpl file you need to initialize your Angular module. This will look a little different than the docs which use crmApp and ng-route. It's actually going to be a lot simpler. All you do is add a tag which tells Civi to boostrap Angular and load your module, then just have your module do it's thing.

So skip all the stuff about routes and controllers. Best practice is to just define a module and a component.


(function(angular, $, _) {
  angular.module('mymodule', CRM.angRequires('mymodule'));

  angular.module('mymodule').component('myComponent' {
    // do your thing
})(angular, CRM.$, CRM._);

Then your .tpl file would look like this:

<crm-angular-js modules="mymodule">
  • Fantastic answer - thank you. I really am looking forward to the afform product, looks like it will make things easier. I gave up on the angular form and went back to what I've used in the past - the quickforms. Not ideal but at least these work really well. May 26, 2021 at 19:23
  • Well, sort of gave up - I'm looking at the changes you suggest above and will give it a go. I did try to install/activate the afform extension but it gave me errors and didn't look very nice when it was finished. I assumed because its alpha/beta it isn't ready. May 26, 2021 at 20:03
  • FYI the extension didn't install properly because of a DB error trying to insert into option_group_en_CA with the message no default. if you install into option_group it fixes the issue (option_group_en_CA is only a view to option_group). May 27, 2021 at 0:35
  • Did you get it working? FYI - nothing to do with Angular, but you shouldn't do direct db queries in your installer. Use the api and it will take care of the the localized columns/views.
    – Coleman
    May 27, 2021 at 12:18
  • I was using the debug output from Civicrm to track the errors. There were two actually. Both related to option_group not having a default set for values. I set default to "" in option_group and option_group_value in order to get the install to work and not constantly throw errors throughout the system. I can share more details if you like. It's now installed and looking good and I will spend some time this morning on making my form. May 27, 2021 at 12:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.