Loading...
Area: Episerver Add-ons
Applies to versions: 4.3 and higher

Creating form element with custom validator

Note: Episerver Forms is only supported by MVC-based websites and HTML5-compliant browsers.

This topic shows how to create a form element with a custom validator.

Form elements inherit from an ElementBlockBase class, which is the only type allowed in the form element Content Area. Inherit from ValidatableElementBlockBase to support custom validator.

By default, you can choose from available form element validators (that inherit ElementValidatorBase class). If you only need some of these validators, or you have your own validator, you can include or exclude them selectively by using the AvailableValidatorTypesAttribute attribute. The following element includes only the RequiredValidator along with its own validator. Apply MyCustomValidator to this element; do not make it available for others.

[ContentType(GUID = "{A4EE6053-3932-4300-8B3B-7BABF9AEAB67}", GroupName = ConstantsFormsUI.FormElementGroup, Order = 2230)]
[AvailableValidatorTypesAttribute(Include = new Type[] { typeof(RequiredValidator) })]
public class MyCustomElementBlock : ValidatableElementBlockBase {
	public virtual string CustomProperty{get; set;}
	
	/// 
	/// Always use a custom Validator to validate this element (along with builtin Validator RequiredValidator)
	/// The custom Validator is not visible to Editor (in EditView), but it still works to validate element value
	/// 
	[Display(GroupName = SystemTabNames.Content, Order = -5000)]
	public override string Validators
	{
		get
		{
			var customValidator = typeof(MyCustomValidator).FullName;
			var validators = this.GetPropertyValue(content => content.Validators);
			if (string.IsNullOrEmpty(validators))
			{
				return customValidator;
			}
			else
			{
				return string.Concat(validators, EPiServer.Forms.Constants.RecordSeparator, customValidator);
			}
		}
		set
		{
			this.SetPropertyValue(content => content.Validators, value);
		}
	}
}

MyCustomValidator:

public class MyCustomValidator: ElementValidatorBase
    {        
        private Injected<LocalizationService> _localizationService;
        protected LocalizationService LocalizationService { get { return _localizationService.Service; } }

        public override bool? Validate(IElementValidatable targetElement)
        {           
            return true;
        }

        /// 
        /// return false if we don't want to show this specific-validators to Editor. This validators always work programatically for AddressElement.
        public override bool AvailableInEditView
        {
            get
            {
                return false;
            }
        }

        /// 
        public override IValidationModel BuildValidationModel(IElementValidatable targetElement)
        {
            var model = base.BuildValidationModel(targetElement);
            if (model != null)
            {
                model.Message = this.LocalizationService.GetString("/episerver/forms/validators/elementselfvalidator/unexpectedvalueisnotaccepted");
            }

            return model;
        }
    }

Only the RequiredValidator appears in edit view; MyCustomValidator is included transparently.

To validate a form element at the client side, you must specify a validate handler. Serverside's Fullname of the Validator instance is used as object key (case-sensitive) to lookup the Clientside validate function. The EpiForm client code calls this function to validate the form element’s value before submitting the form.

// extend the EpiForm JavaScript API in ViewMode
$.extend(true, epi.EPiServer.Forms, {
    /// extend the Validator to validate Visitor's value in Clientside.        
    Validators: {
         "EPiServer.Forms.Samples.Implementation.Validation.MyCustomValidator": function(fieldName, fieldValue, validatorMetaData) {
	 // this function is used to validate visitor's data value in ViewMode
            
          if (!condition) {
              return { isValid: false, message: validatorMetaData.model.message };
          }
          return { isValid: true };
      },
   },
})

The element view

The default path of element views are found at modules\_protected\EPiServer.Forms\Views\ElementBlocks, but you can override this by adding custom views in the Views\Shared\ElementBlocks folder. If you want a custom view location, you can override the GetDefaultViewLocation method of CustomViewLocationBase class: 

[ServiceConfiguration(typeof(ICustomViewLocation))]
public class FormsSamplesViewLocation : CustomViewLocationBase
{
        public virtual string GetDefaultViewLocation()
        {            
            var defaultPathToBlockViews = "~" + ModuleHelper.ToResource(this.GetType(), "Views/ElementBlocks");

            return defaultPathToBlockViews;
        }
}

Form element resources

If a form element needs its own resource, it must implement the IElementRequireClientResources interface. The form element's resource is loaded after form's resources and external's resources. For example, the DateTime element requires jQuery datetime to work but it is not loaded until the element is dragged into the form. For example: 

public IEnumerable<Tuple<string, string>> GetExtraResources()
{
     if (!string.IsNullOrWhiteSpace(Settings.Instance.GoogleMapsApiV3Url))
     {
        var publicVirtualPath = ModuleHelper.GetPublicVirtualPath(Constants.ModuleName);
        var currentPageLanguage = FormsExtensions.GetCurrentPageLanguage();
        return new List<Tuple<string, string>>() {
            new Tuple<string, string>("script", string.Format(Settings.Instance.GoogleMapsApiV3Url, currentPageLanguage)),
            new Tuple<string, string>("script", publicVirtualPath + "/ClientResources/ViewMode/AddressesElementBlock.js")
        };
     }
     return new List<Tuple<string, string>>();
}

Customizing an element's icon and image

To display a form element's image in the Forms Element gadget, add an image with the same name as the element name inside modules\_protected\EPiServer.Forms.UI\{version}\ClientResources\epi-forms\themes\sleek\images\contenttypes. If you want a different location for a form element's big icons, you can override the default one (dojo part): 

var contentTypeService = dependency.resolve('epi.cms.ContentTypeService'),
    self = this;
aspect.around(contentTypeService, '_getImagePathByTypeIdentifier', function (originalFunction) {
    return function (/*String*/typeIdentifier, /*Array*/registeredTypes, /*String*/clientResourcePath) {
        var types = self._settings.registeredElementContentTypes;
        if (types instanceof Array && types.indexOf(typeIdentifier) >= 0) {
            // show the .png as big icon for creating FormElement in the ContentArea
            return self._settings.clientResourcePath + '/ClientResources/epi-formssamples/themes/sleek/images/contenttypes/' + typeIdentifier.split('.').pop() + '.png';
        }

        return originalFunction.apply(contentTypeService, arguments);
    };
});

From Episerver Forms 4.4.2, you can use the ImageUrl attribute to change the big icon of a form element block like other blocks types by adding this to the custom element block:

[ImageUrl("~/static/img/thumbnails/CustomTextArea.png")]

For the small icon in the Form Elements gadget, add a class as follows:

.epi-forms-icon.epi-forms-icon--small.epi-forms-mycustomelementblock__icon
{
background:url(images/mycustomelemeticon.png) 0px 0px no-repeat
}

For examples about custom form elements, see https://github.com/episerver/EPiServer.Forms.Samples.

Last updated: Jan 23, 2017

Feedback?