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.
- Add an angular module to your extension:
civix generate:angular-module
- 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.
- 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';
}
- 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"