var EntityFormUX = { // Option Set - Dropdown Format // Hide an Option from dropdown RemoveOptionsFromOptionSetDropdown: function(attributeName, optionValue) { // jQuery show/hide doesn't work. Hence Bootstrap Hidden Class is used $("#" + attributeName + " option[value='" + optionValue + "']").addClass("hidden"); }, // Show the previously hidden option using RemoveOptionsFromOptionSetDropdown ShowOptionsFromOptionSetDropdown: function(attributeName, optionValue) { $("#" + attributeName + " option[value='" + optionValue + "']").removeClass("hidden"); }, // Assuming the options are in a range, so you don't need to code them one by one ShowHideOptionsFromDropdownRange: function(attributeName, minOptionValue, maxOptionValue, show) { for (var i = minOptionValue; i <= maxOptionValue; i++) { if (show) { EntityFormUX.ShowOptionsFromOptionSetDropdown(attributeName, i); } else { EntityFormUX.RemoveOptionsFromOptionSetDropdown(attributeName, i); } } }, // Option Set - Radio Button Format // Hide an Option Radio Button RemoveOptionsFromOptionSetRadioButtons: function(attributeName, optionValue) { var option = $("input[name$='" + attributeName + "'][value='" + optionValue + "']"); if (option.length > 0) { var radioButtonId = option.attr('id'); option.hide(); $("label[for='" + radioButtonId + "']").hide(); } }, // Show the previous hidden option using RemoveOptionsFromOptionSetRadioButtons ShowOptionsFromOptionSetRadioButtons: function(attributeName, optionValue) { var option = $("input[name$='" + attributeName + "'][value='" + optionValue + "']"); if (option.length > 0) { var radioButtonId = option.attr('id'); option.show(); $("label[for='" + radioButtonId + "']").show(); } }, // Option Set General ReplaceOptionSetLabelValue: function(attributeName, optionValue, overrideLabel) { $("#" + attributeName + " option[value='" + optionValue + "']").text(overrideLabel); }, SetOptionSetDropdownReadonly: function(attributeName) { $("#" + attributeName).attr('readonly', 'readonly').attr('disabled', 'disabled').addClass('readonly'); }, // This only works if the Option Set Field is set to read-only using SetOptionSetDropdownReadonly // If the field is read-only at CRM Form, Entity Form will not save any changes on the value RemoveOptionSetDropdownReadonly: function(attributeName) { $("#" + attributeName).removeAttr('readonly').removeAttr('disabled').removeClass('readonly'); }, // Radio Buttons - Works for Both Two Options and Option Set // Set Radio Button Selection SetRadioButtonSelectionValue: function(attributeName, settingValue) { $('input[name$="' + attributeName + '"][value="' + settingValue + '"]').prop('checked', true); $("#" + attributeName).trigger("change"); }, // Reset Radio Buttons Selections - Clear Selection ResetRadioButtonSelection: function(attributeName) { $("input[name$='" + attributeName + "']:checked").removeAttr("checked").trigger("change"); }, // Get Radio Buttons selected value GetRadioButtonSelectionValue: function(attributeName) { return $("input[name$='" + attributeName + "']:checked").val(); }, // Two-Options - Checkbox // Set Checkbox Values // Expecting true/false boolean for settingValue SetCheckboxFieldValue: function(attributeName, settingValue) { var $field = $("#" + attributeName); if ($field.length == 0) { return; } if (settingValue) { $field.attr("checked", "checked"); } else { $field.removeAttr("checked"); } $field.trigger("change"); }, // Lookup SetLookupReadOnly: function (attributeName) { var attributeTextBox = $("#" + attributeName + "_name"); if (attributeTextBox.length > -1) { attributeTextBox.addClass("aspNetDisabled").attr("disabled", "disabled"); var lookupBtnGroup = attributeTextBox.parent().find(".input-group-btn"); if (lookupBtnGroup.length > -1) { lookupBtnGroup.addClass("hidden"); } } }, // Clear a lookup field value ClearLookupFieldValue: function(attributeName) { EntityFormUX.SetLookupFieldValue(attributeName, '', '', ''); }, // Set a lookup field value SetLookupFieldValue: function(attributeName, entityLogicalName, settingValue, settingName) { var $field = $("#" + attributeName); if ($field.length == 0) { return; } var $nameField = $("#" + attributeName + "_name"); if ($nameField.length == 0) { return; } var $lookupTargetField = $("#" + attributeName + "_entityname"); if ($lookupTargetField.length == 0) { return; } $nameField.val(settingName); $lookupTargetField.val(entityLogicalName); $field.val(settingValue).trigger("change"); }, // Date SetDateFieldReadOnly: function(attributeName) { var datetimePickerTextbox = $("input[aria-labelledby='" + attributeName + "_label']"); datetimePickerTextbox.attr('readonly', 'readonly'); datetimePickerTextbox.addClass('readonly'); var pickerIconSpan = datetimePickerTextbox.parent().find(".input-group-addon"); if (pickerIconSpan.length > 0) { pickerIconSpan.attr('style', 'display:none;'); } }, // This only works if the Date Field is set to read-only using SetDateFieldReadOnly // If the field is read-only at CRM Form, Entity Form will not save any changes on the value RemoveDateFieldReadOnly: function(attributeName) { var datetimePickerTextbox = $("input[aria-labelledby='" + attributeName + "_label']"); datetimePickerTextbox.removeAttr('readonly', 'readonly'); datetimePickerTextbox.removeClass('readonly'); var pickerIconSpan = datetimePickerTextbox.parent().find(".input-group-addon"); if (pickerIconSpan.length > 0) { pickerIconSpan.removeAttr('style', 'display:none;'); } }, // This only works on early 7.x version SetDateAttributeValue: function(attributeName, dateStringValue) { // It's the same as Text Box Value. Just need to get the date time format correct // Beware of passing values. If Attribute only accepts date only, please ensure the value does not contain time. // Looking for Format like 2017-04-26T00:00:00.0000000 $("#" + attributeName).val(dateStringValue); $("#" + attributeName).trigger("change"); }, // DatePicker Method of 7.0.18 / 8.x // Please refer to http://eonasdan.github.io/bootstrap-datetimepicker/version3/ for references (7.0.18 and later) // 2017-08-21 - updated documentation here: http://eonasdan.github.io/bootstrap-datetimepicker/#bootstrap-3-datepicker-v4-docs // Use NULL to clear value SetDatePickerValue: function(attributeName, dateValue) { var targetDatetimepickerTextbox = $("input[aria-labelledby='" + attributeName + "_label']"); var targetDatetimepickerParentDiv = targetDatetimepickerTextbox.parent(); targetDatetimepickerParentDiv.data("DateTimePicker").date(dateValue); $("#" + attributeName).trigger("change"); }, // Extension - Copy Date on Change CopyDateOnChange: function(sourceAttributeName, targetAttributeName) { var sourceDatetimepickerTextbox = $("input[aria-labelledby='" + sourceAttributeName + "_label']"); var sourceDatetimepickerParentDiv = sourceDatetimepickerTextbox.parent(); var targetDatetimepickerTextbox = $("input[aria-labelledby='" + targetAttributeName + "_label']"); var targetDatetimepickerParentDiv = targetDatetimepickerTextbox.parent(); sourceDatetimepickerParentDiv.on('dp.change', function (e) { EntityFormUX.SetDatePickerValue(targetAttributeName, e.date); }); }, ValidateDate1MustBeLaterThanDate2: function(date1AttributeName, date2AttributeName) { var date1DatetimepickerTextbox = $("input[aria-labelledby='" + date1AttributeName + "_label']"); var date1DatetimepickerParentDiv = date1DatetimepickerTextbox.parent(); var date2DatetimepickerTextbox = $("input[aria-labelledby='" + date2AttributeName + "_label']"); var date2DatetimepickerParentDiv = date2DatetimepickerTextbox.parent(); if (EntityFormUX.ValidateDateTimeHasValue(date1AttributeName) && EntityFormUX.ValidateDateTimeHasValue(date2AttributeName)) { // getDate throws an error. Parse the date instead. var date1 = date1DatetimepickerParentDiv.data("DateTimePicker").date(); var date2 = date2DatetimepickerParentDiv.data("DateTimePicker").date(); return (date2 < date1); } // Either one of the field has no value. Leave it to other validators return true; }, ValidateDateIsOrLaterThanToday: function(attributeName) { var sourceDatetimepickerTextbox = $("input[aria-labelledby='" + attributeName + "_label']"); var sourceDatetimepickerParentDiv = sourceDatetimepickerTextbox.parent(); if (EntityFormUX.ValidateDateTimeHasValue(attributeName)) { var today = new Date().setHours(0, 0, 0); // midnight var sourceDate = sourceDatetimepickerParentDiv.data("DateTimePicker").date(); return (today < sourceDate); } // The field does not have any value. Leave it to other validators return true; }, // CRM Min Date Allowed is Jan 1, 1900 // Example // var minDate = moment("01/01/1990", "MM/DD/YYYY"); // EntityFormUX.SetDatePickerMinDateAllowed("adoxio_birthdate", minDate); SetDatePickerMinDateAllowed: function(attributeName, minDate) { $("#" + attributeName).next().data("DateTimePicker").minDate(minDate); }, // Some Business Requirement Requires limited date, say 18 years or older // var eighteenYearsAgo = moment().subtract(18, 'years'); // var maxDate = eighteenYearsAgo; // EntityFormUX.SetDatePickerMaxDateAllowed("adoxio_birthdate", maxDate); SetDatePickerMaxDateAllowed: function(attributeName, maxDate) { $("#" + attributeName).next().data("DateTimePicker").maxDate(maxDate); }, /* // This is just a sample. And it only does format check. If you need additional logic, please add accordingly // The error message is only in English. Consider using Snippets which will provide message in corresponding language. // Call this method on form init for date picker validation SampleAddValidateDatePickerFormatOnChange: function(attributeName) { // Call this otherwise, the first load might render Chinese instead of default language. // It can be in the moment.locale("en"); var datetimePickerTextbox = $("input[aria-labelledby='" + attributeName + "_label']"); var attributeTextbox = $("#" + attributeName); var dateFormatConverter = window.dateFormatConverter; var dateOnly = attributeTextbox.data('type') === 'date'; var dateFormat = dateFormatConverter.convert(attributeTextbox.closest('[data-dateformat]').data('dateformat') || 'M/d/yyyy', dateFormatConverter.dotNet, dateFormatConverter.momentJs); var timeFormat = dateFormatConverter.convert(attributeTextbox.closest('[data-timeformat]').data('timeformat') || 'h:mm tt', dateFormatConverter.dotNet, dateFormatConverter.momentJs); var dateTimeFormat = dateOnly ? dateFormat : (dateFormatConverter.convert(attributeTextbox.closest('[data-datetimeformat]').data('datetimeformat'), dateFormatConverter.dotNet, dateFormatConverter.momentJs) || (dateFormat + ' ' + timeFormat)); var keyMap = { 'tab': 9, 9: 'tab', 'escape': 27, 27: 'escape', 'enter': 13, 13: 'enter' }; function ValidateEnteredDateFormat() { $("#" + attributeName + "_formatnotice").remove(); var val = datetimePickerTextbox.val().trim(); var textbox = $("#" + attributeName); if (val) { var d = moment(val, dateTimeFormat, true); if (!d.isValid()) { textbox.before("Date Entered was: " + val + ". Please enter date in format of " + dateTimeFormat + ""); } } } datetimePickerTextbox.bindFirst("change", ValidateEnteredDateFormat); datetimePickerTextbox.bindFirst("keydown", function (e) { if (e.keyCode == keyMap.enter) { ValidateEnteredDateFormat(); } }); }, */ // Text // Set textbox values // Applicable to Textbox, TextAreas, DateTime (Early 7.x), Option Set as Select(Dropdown) SetTextboxFieldValue: function(attributeName, settingValue) { var $field = $("#" + attributeName); if ($field.length == 0) { return; } $field.val(settingValue).trigger("change"); }, SetTextboxReadOnly: function(attributeName) { $("#" + attributeName).attr('readonly', 'readonly'); $("#" + attributeName).addClass('readonly'); }, // This only works if the field is set as Readonly using SetTextboxReadOnly // If the field is read-only at CRM Form, Entity Form will not save any changes on the value RemoveTextboxReadOnly: function(attributeName) { $("#" + attributeName).removeAttr('readonly'); $("#" + attributeName).removeClass('readonly'); }, // Attributes - General // This is using HTML Tooltip. // OOB Tooltip at Entity Form will display the description field of the attribute // Use this if you want to override AddToolTip: function(attributeName, toolTipValue) { $("#" + attributeName + "_label").attr("title", toolTipValue); }, // This will enable the Bootstrap Tooltip and will only show the label of the field EnableBootstrapToolTip: function(attributeName) { $("#" + attributeName + "_label").attr("data-toggle", "tooltip"); }, // Hide the entire tab where the attribute control is located HideControlTab: function(attributeName) { EntityFormUX.AddStyleToClosestTabColumn(attributeName, "hidden"); }, // Entity Form Metadata adding a CSS hidden to the control only hides the control but not the label // This will hide the control with the label HideControlWithLabel: function(attributeName) { EntityFormUX.AddStyleToClosestTd(attributeName, "hidden"); }, // Show the control with its label // This only works if the control is hidden using HideControlWithLabel // This will NOT work if the Control is not in the CRM Form Layout, or its label is set to not visible at CRM ShowControlWithLabel: function(attributeName) { EntityFormUX.RemoveStyleToClosestTd(attributeName, "hidden"); }, HighlightControl: function(attributeName, highlightAlertClass) { EntityFormUX.AddStyleToClosestTd(attributeName, "alert"); EntityFormUX.AddStyleToClosestTd(attributeName, highlightAlertClass); }, RemoveHighlightOnControl: function(attributeName, highlightAlertClass) { EntityFormUX.RemoveStyleToClosestTd(attributeName, "alert"); EntityFormUX.RemoveStyleToClosestTd(attributeName, highlightAlertClass); }, SetAttributeLabel: function(attributeName, labelValue) { var labelControl = $("#" + attributeName + "_label"); if (labelControl.length > 0) { labelControl.html(labelValue); } }, // Tab // Hide the entire tab // For this to work, one must add a Entity Form metadata for the tab // At the Label, Change it to whatever it is called and then append the following //
// For example: // By having this, it allows the JavaScript to find the h2 tag and toggle appearance HideTab: function(tabName) { var tabDiv = $('div[data-name$="' + tabName + '"]'); if (!tabDiv.hasClass("hidden")) { tabDiv.addClass("hidden"); var tabLabelDiv = $(".tab-" + tabName); var tabLabelH2 = tabLabelDiv.parent(); tabLabelH2.addClass("hidden"); } }, // Section // Hide Section By Name HideSectionByName: function(sectionName) { EntityFormUX.AddClassToSection(sectionName, "hidden"); }, // Show Section By Name // This only works if the section was hidden programmatically using HideSectionByName // Hidden Section at CRM Form Configuration will not be shown on Entity Forms ShowSectionByName: function(sectionName) { EntityFormUX.RemoveClassFromSection(sectionName, "hidden"); }, // Subgrid // When toggling on the Search functionality at the subgrid // CRM force the subgrid label to become visible // And it's sometimes redundant to the already visible Section Label HideSubgridLabel: function(subgridSchemaName) { $('label[for="' + subgridSchemaName + '"]').hide(); }, // Starting from 7.0.21ish, Entity List added a table-fluid class on load by JavaScript // This class is added after the on-load event and does not have any event trigger // The CSS behavior is to make the table become rows instead of rendering columns when the width of the device is too narrow // To maintain the table render as a table with columns, use this method to remove that table-fluid class RemoveEntityListTableFluidClass: function() { $(".entity-grid").ready(function () { setTimeout(RemoveTableFluidClassFromSubgrid, 1000, ""); }); }, RemoveSubgridTableFluidClass: function (subgridName) { $("#" + subgridName).ready(function () { setTimeout(RemoveTableFluidClassFromSubgrid, 1000, subgridName); }); }, RemoveTableFluidClassFromSubgrid: function(subgridName) { var entitygridComponent = $(".entity-grid"); if (subgridName != "") { var mySubgrid = $("#" + subgridName); entitygridComponent = mySubgrid.find(".entity-grid"); } var tableFluidComponent = entitygridComponent.find(".table-fluid"); if (tableFluidComponent.length) { tableFluidComponent.removeClass("table-fluid"); } else { setTimeout(EntityFormUX.RemoveTableFluidClassFromSubgrid, 500, subgridName); } }, RemoveIFrameTableFluidClass: function(iFrameName) { $("iframe#" + iFrameName).ready(function () { setTimeout(EntityFormUX.RemoveTableFluidClassFromIFrame, 3000, iFrameName); }); }, RemoveTableFluidClassFromIFrame: function(iFrameName) { var myIFrame = $("iframe#" + iFrameName); var myIFrameContents = myIFrame.contents(); var entitygridComponent = myIFrameContents.find(".entity-grid"); var tableFluidComponent = entitygridComponent.find(".table-fluid"); if (tableFluidComponent.length) { tableFluidComponent.removeClass("table-fluid"); } else { setTimeout(EntityFormUX.RemoveTableFluidClassFromIFrame, 500, iFrameName); } }, // Validations // Required At Least 1 Validator at the Form already. Otherwise, the Page_Validators will not be available // Add red asterisk to the Control AddRedAsteriskToRequiredControl: function(attributeName) { var controlLabel = $("#" + attributeName + "_label"); if (controlLabel.length > 0) { var controlLabelParent = $("#" + attributeName + "_label").parent(); if (!controlLabelParent.hasClass("required")) { controlLabelParent.addClass("required"); } } }, RemoveRedAsteriskFromRequiredControl: function(attributeName) { var controlLabel = $("#" + attributeName + "_label"); if (controlLabel.length > 0) { var controlLabelParent = $("#" + attributeName + "_label").parent(); if (controlLabelParent.hasClass("required")) { controlLabelParent.removeClass("required"); } } }, // Ignore Hidden Fields IsControlHidden: function(attributeName) { var control = $("#" + attributeName); if (control.length > 0) { if (control.closest("td").hasClass("hidden")) { return true; } return false; } return true; }, ToggleValidator: function(attributeName, enable) { var validatorId = "CustomRequiredFieldValidator" + attributeName; if (enable) { EntityFormUX.AddRedAsteriskToRequiredControl(attributeName); } else { EntityFormUX.RemoveRedAsteriskFromRequiredControl(attributeName); } for (i = 0; i < Page_Validators.length; i++) { if (Page_Validators[i].id == validatorId) { // toggle Validator ValidatorEnable(Page_Validators[i], enable); return true; } } return false; }, // To Remove a required validation, execute // EntityFormUX.RemovePageRequiredFieldValidatorsFor("firstname"); RemovePageRequiredFieldValidatorsFor: function(attributeName) { EntityFormUX.ToggleValidator(attributeName, false); }, AddPageRequiredFieldValidatorsFor: function(attributeName, attributeControlType, invalidateMessage, validationGroup) { if (typeof (Page_Validators) == 'undefined') return; if (validationGroup == null) { validationGroup = ""; } if (EntityFormUX.ToggleValidator(attributeName, true)) { return; } if (invalidateMessage == null || invalidateMessage == "") { var attributeLabel = $("#" + attributeName + "_label"); if (attributeLabel.length > 0) { invalidateMessage = attributeLabel.text() + " is a required field."; } } var aValidator = document.createElement('span'); aValidator.style.display = "none"; aValidator.id = "CustomRequiredFieldValidator" + attributeName; aValidator.controltovalidate = attributeName; aValidator.errormessage = "" + invalidateMessage + ""; aValidator.validationGroup = validationGroup; aValidator.initialvalue = ""; aValidator.evaluationfunction = function () { switch (attributeControlType) { case "datetime": return EntityFormUX.ValidateDateTimeHasValue(attributeName); break; case "checkbox": return EntityFormUX.ValidateCheckboxHasChecked(attributeName); break; case "radio": return EntityFormUX.ValidateOptionSetRadioHasSelectedValue(attributeName); break; case "optionsetasradio": return EntityFormUX.ValidateOptionSetRadioHasSelectedValue(attributeName); break; case "optionsetasdropdown": return EntityFormUX.ValidateOptionSetDropdownHasSelectedValue(attributeName); break; case "lookup": return EntityFormUX.ValidateLookupControlHasValue(attributeName); break; default: return EntityFormUX.ValidateTextboxHasValue(attributeName); break; } }; // Add the new validator to the page validators array Page_Validators.push(aValidator); }, // Expecting attributeSchemaNameDelimitedString in ""+helpText+"
"+ "Help"+ "