HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideLegal TermsGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Create form element with validator

Shows how to create a form element with a custom validator.

📘

Note

Optimizely Forms is only supported by MVC-based websites and HTML5-compliant browsers.

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 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.

783

You must specify a validate handler to validate a form element on the client side. Serverside"s Fullnameof theValidatorinstance is used as object key (case-sensitive) to lookup theClientsidevalidate function. TheEpiForm` 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
      };
    },
  },
})

Element view

The default path of element views is 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 resource, it must implement the IElementRequireClientResources interface. The form element's resource is loaded after the 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>>();
}

📘

Note

The Constants.ModuleName comes from EPiServer.Forms.Samples namespace. You can replace it with your project namespace.

Customize 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 Optimizely Forms 4.4.2, you can use the ImageUrl attribute to change the big icon of a form element block like other block 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 of custom form elements, see https://github.com/episerver/EPiServer.Forms.Samples.