2

I'm following up on the questions about embedding javascript in an angular form (FormBuilder: alterAngular embed javascript), but specifically to do so with a core afform.

I'm trying to build an extension that allows people to choose the drupal theme they want to use when displaying a public afform.

Using hook_civicrm_alterAngular() I can easily insert the field to select the theme. But, I can't save the setting. Or to be more precise, I did manage to get the setting saved using "onchange" and some jquery code. But I couldn't get the field to display the current value for the theme. I think angular is somehow interfering with jquery when I try to update a value on the form (probably for the reasons coleman explains in the above answer).

@coleman suggests overriding the form with a local copy, but I'm hesitant to do that for fear of diverging from any changes during an upgrade.

I also tried using @demerit's trick (https://civicrm.stackexchange.com/a/31264/181). But given coleman's warning about caching it didn't seem viable.

I also tried shoving the angular code directly into the hook_civicrm_alterAngular call via the tag, but that didn't seem to work because angularjs complained about the controller not being defined. I think it processed the form before the javascript code.

So that's how I ended up with this question: Can I create an angular app that adds a new tab to the core afform form. There's a "+" button that lists all the things that can be added as tabs. Is there a way I can add my own?

Thanks!

2 Answers 2

4

At last. I figured out the more proper angular method for adding a new field to a core angular form and saving the data entered in the field.

  1. Add an angular module to your extension: civix generate:angular-module
  2. Add your new field via hook_civicrm_alterAngular():
function dynamic_theme_selector_civicrm_alterAngular($angular) {                                                                                              
  $angular->add(\Civi\Angular\ChangeSet::create('dts_theme_field')                                                                                        
    ->alterHtml('~/afGuiEditor/config-form.html', function(phpQueryObject $doc) {                                                                                                                                   
      $element = <<<END                                                                                     
      <div class="form-group" ng-if="editor.afform.is_public" ng-controller="dtsThemeCtrl">                                                                                                        
        <label for="af_theme">{{ ts('Select a theme') }}</label>                                           
        <div class="input-group">                                                                                                              
          <select id="af_theme"                                                                                                                          
            ng-model="af_theme"                                                                                                                      
            name="af_theme"                                                                                                                                                                 
            class="form-control"                                                                                                                                                              
            title="{{ ts('Select a theme') }}"                                                             
            ng-options="theme for theme in af_theme_options"                                                                                                 
            ng-change="save_theme()"                                                                                                                                   
          >                                                                                                                                                        
          </select>                                                                                                                                                                                                 
        </div>                                                                                                       
        <p class="help-block">Enter the theme you would like to use to display this form.</p>                                                                                                               
      </div>                                                                                               
END;                                                                                                                                              
      $doc->append($element);                                                                                                                               
    })                                                                                                                                                  
  );                                                                                                                                                                                            
}   

Important things to note in the code snippet above:

  • ~/afGuiEditor/config-form.html is the core angular form I'm modifying
  • dtsThemeCtrl is the name of the controller that I will referencing.
  • af_theme is the name of the model that I will be saving.
  1. Ensure the core form you are modifying requires your angular module via hook_civicrm_angularModules:
function dynamic_theme_selector_civicrm_angularModules(&$angularModules) {                                                                              
  $angularModules['afGuiEditor']['requires'][] = 'crmDynamicThemeSelector';                                                                                                                     
}
  1. Edit the angular module created in step one (in my case, ang/crmDynamicThemeSelector.js):
(function(angular, $, _) {                                                              
  angular.module('crmDynamicThemeSelector', CRM.angRequires('crmDynamicThemeSelector'));
  // Our only purpose is to extend the afGuidEditor so we can control the
  // theme field we insert via hook_civicrm_alterAngular.                             
  try {
    angular.module('afGuiEditor').controller('dtsThemeCtrl', function($scope, crmApi4) {
      $scope.af_theme_options = [];
                              
      $scope.init = function() {                                                                                                                                                                                       
        crmApi4('Setting', 'get', {                                                                                                                                                                                    
          // Get all possible customized formbuilder themes.                                                                                                                                                           
          select: ["dynamic_theme_selector_chosen_formbuilder_themes"]                                                                                                                                                 
        }).then(function(chosen_themes) {                                                                                                                                                                              
          var route = $scope.editor.afform.server_route;                                                                                                                                                               
          var chosen_theme = '';                                                                                                                                                                                       
          if (chosen_themes[0]['value'][route]) {                                                                                                                                                                      
            chosen_theme = chosen_themes[0]['value'][route];                                                                                                                                                           
          }                                                                                                                                                                                                            
          $scope.af_theme = chosen_theme;                                                                                                                                                                              
          // Get available themes to populate the drop down.                                                                                                                                                           
          return crmApi4('Dts', 'GetAvailableThemes', {});                                                                                                                                                             
        }).then(function(result) {                                                                                                                                                                                     
          Object.entries(result).forEach(([key, value]) => {                                                                                                                                                           
            $scope.af_theme_options.push(key);                                                                                                                                                                         
          });                                                                                                                                                                                                          
        });                                                                                                                                                                                                            
      };                                                                                                                                                                                                               
                                                                                                                                                                                                                     
      $scope.save_theme = function() {                                                                                                                                                                                 
        var route = $scope.editor.afform.server_route;                                                                                                                                                                 
        console.log("Updating route -> theme", route, $scope.af_theme);                                                                                                                                                
        CRM.api4('Dts', 'UpdateSelectedTheme', {                                                                                                                                                                       
          'entity': 'formbuilder',                                                                                                                                                                                     
          'theme': $scope.af_theme,                                                                                                                                                                                    
          'id': route                                                                                                                                                                                                  
        });                                                                                                                                                                                                            
      };                                                                                                                                                                                                               
      $scope.init();                                                                                                                                                                                                   
    });
  }
  catch(err) {
    console.log("Error registered", err);
  }                                                                                                                                                                                                                
})(angular, CRM.$, CRM._);  

Important things to note in the snippet above:

  • angular.module('afGuiEditor') references the angular module I'm modifying (it's the first part of the first argument passed to alterHtml above).
  • controller('dtsThemeCtrl' refers to the controller I named in the html snippet above.
  • $scope.af_theme is how I access the value of my field because I used ng-model="af_theme"
1
  • Well done Jamie!
    – Coleman
    Commented Sep 18 at 19:05
0

Well, it feels really hacky and 2010, but I managed to get it to work using hook_civicrm_pageRun. My field exists entirely outside of angular and is controlled via jquery. Hopefully there is a better answer using angularjs, but this works as a hack.

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.