Struts2 Wysihtml5 editor - struts2

I have integrated Wysihtml5 text editor in my page.I have define parser rules from here (https://github.com/eyecatchup/wysihtml5/blob/master/parser_rules/allow-all-html5.js). When I get data from database,code formatted,link,image,bold,color,pre tag (define from textEditor) it is not working for me.It display text like here:
Only paragraph tag works.Can any one helps me?I am using struts2 and hibernate.
Here is description attribute in hibernate:
Blog.java
#Column(name="description",columnDefinition = "LONGTEXT",length = 65535)
public String getDescription() {
return this.description;
}
public void setDescription(final String description) {
this.description = description;
}
create.jsp
<div>
<s:textarea id="wysihtml5-editor" cssStyle="height:400px" name="blog.description"
key="blog.description" placeholder="Enter Description..."/>
</div>
<script>
var editor = new wysihtml5.Editor("wysihtml5-editor", {
toolbar: "wysihtml5-editor-toolbar",
stylesheets : ["<%=request.getContextPath()%>/css/editor.css"],
cleanUp: true,
parserRules: wysihtml5ParserRules,
useLineBreaks: false
});
</script>
Thanks!

Related

Render MVC form using reflection and Razor Tag Helper .Net 3.1

I want to create complicate form which will create structure of wizard, partial steps form, validation and submit. This structure have to use model attributes annotations to create one structure object over the model. So after reflection I have model and one other class with structure description. All properties within are strings with Fields which I have to pass on 'asp-for' tag helper. So part of the code is:
#foreach(var field in #group.Fields) {
<div class="col-12 col-md-6 col-lg-4">
<div class="form-group md-form md-outline">
<label asp-for="#field.Name" class="control-label"></label>
<input asp-for="#field.Name" class="form-control" />
<span asp-validation-for="#field.Name" class="text-danger"></span>
</div>
</div>
}
This is nor working because tag helper expect expression and generate wrong values which are not expected from me. The value in #field.Name is 'PostAddress.Street1'. If I replace all of "#field.Name" with "PostAddress.Street1" everything work properly how I expected.
It looks small issue but I'm trying many things and reading some theads in forums but didn't find the answer. What I tried:
Experiment 1
Tried to inherit InputTagHelper class from dotnet library and override property For but without success. It changed ModelExpression but no changes in interface. May be base class have some logic to skip this changed object or is not correct generated:
[HtmlAttributeName("asp-for")]
public new ModelExpression For
{
get
{
return base.For;
}
set
{
ModelExpression me = value;
if (value.Model != null)
{
var viewData = this.ViewContext.ViewData as ViewDataDictionary<AbnServiceModel>;
me = ModelExpressionProvider.CreateModelExpression<AbnServiceModel, string>(viewData, model => model.PostAddress.Street1);
}
base.For = me;
}
}
=================================================
2. Experiment 2
Try to get original implementation from .NET Core code and made some modification in code to fix the issue. But the code and dependencies with internal libraries were very complicated and I reject this idea.
Expiriment 3
Using HTML helpers
#Html.Label(#field.Name, "", new{ #class="control-label" })
#Html.Editor(#field.Name, new { htmlAttributes = new{ #class="form-control" } })
#Html.ValidationMessage(#field.Name,"",new { htmlAttributes = new{ #class="text-danger" } })
It render components correct into the browser but client side validation using jquery.validate.unobtrusive.js is not working. Not sure why.
Expiriment 4
Using HTML helpers:
#Html.LabelFor(m=>m.PostAddress.Street1, new{ #class="control-label" })
#Html.EditorFor(m=>m.PostAddress.Street1, new { htmlAttributes = new{ #class="form-control" } })
#Html.ValidationMessageFor(m=>m.PostAddress.Street1,"",new { htmlAttributes = new{ #class="text-danger" } })
The validation is working but class weren't applied well, may be my mistake. But other problem here is that I'm not using expression which is string which can get from model object. Also It doesn't catch all logic which is included in asp-for tag helper.
Experiment 5
Tried to create my own tag helper and using generator to create the content html. But this means that I have to implement all logic like helper in dotnet core to have all functionality which is same like Expiriment 2
So I didn't find good solution of this "simple" problem and lost some days to investigate and doing some code to resolve it. I'm surprised that no way to pass string variable with property name and it wouldn't work.
Can someone help me to fix this problem with real example? I didn't find the answer in all posts. I want to have all logic from asp-for tag helper but use variable to pass the expression. It cab be and tricky, just want to have some resolution to continue with my project.
Thank you
I resolved my issue.
Created one helper method:
public static class CommonHelperMethods
{
public static ModelExplorer GetModelExplorer(this ModelExplorer container, string field, IModelMetadataProvider modelMetadataProvider = null)
{
ModelExplorer result = container;
var fields = field.Split(".").ToList();
var match = Regex.Match(fields[0], #"(.+)\[(\d)+\]");
if (!match.Success)
{
fields.ForEach(x =>
{
result = result?.GetExplorerForProperty(x) ?? result;
});
}
else
{ //List have to create own Property browser
string proName = match.Groups[1].Value;
int idx = Convert.ToInt32(match.Groups[2].Value);
var model = ((IList)result?.GetExplorerForProperty(proName).Model)[idx];
var targetProperty = model.GetType().GetProperty(fields[1]);
var targetValueModel = targetProperty.GetValue(model);
var elementMetadata = modelMetadataProvider.GetMetadataForProperty(model.GetType(), fields[1]);
return new ModelExplorer(modelMetadataProvider, container, elementMetadata, targetValueModel);
}
return result;
}
}
And just override the tag helper class with this:
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace GetTaxSolutions.Web.Infrastructure.TagHelpers
{
[HtmlTargetElement("input", Attributes = ForAttributeName, TagStructure = TagStructure.WithoutEndTag)]
public class InputTextGtTaxHelper : InputTagHelper
{
private const string ForAttributeName = "asp-for";
[HtmlAttributeName("not-exp")]
public bool NotExpression { get; set; } = false;
[HtmlAttributeName(ForAttributeName)]
public new ModelExpression For
{
get
{
return base.For;
}
set
{
ModelExpression me = value;
if (NotExpression)
{
var modelExplorertmp = value.ModelExplorer.Container.GetModelExplorer(value.Model.ToString(), ModelMetadataProvider);
var modelExplorer = new ModelExplorer(ModelMetadataProvider, value.ModelExplorer.Container, modelExplorertmp.Metadata, modelExplorertmp.Model);
me = new ModelExpression(value.Model.ToString(), modelExplorer);
}
base.For = me;
}
}
public IModelExpressionProvider ModelExpressionProvider { get; }
public IModelMetadataProvider ModelMetadataProvider { get; }
public IActionContextAccessor Accessor { get; }
public InputTextGtTaxHelper(
IHtmlGenerator generator,
IModelExpressionProvider modelExpressionProvider,
IModelMetadataProvider modelMetaDataProvider) : base(generator)
{
ModelExpressionProvider = modelExpressionProvider;
ModelMetadataProvider = modelMetaDataProvider;
}
}
}
Also should skip original class in tag helper registration:
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.InputTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
#removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.LabelTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
#removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.ValidationMessageTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
#removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.SelectTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
#addTagHelper GetTaxSolutions.Web.Infrastructure.TagHelpers.*, GetTaxSolutions.Web
And when use model in expression just have to pass attribute 'no-exp' on input elements. Otherwise will work like original tag helper.
<input not-exp="true" asp-for="#field.Name" class="form-control" />
Also you have to do same with label, select and other used tag helpers which you want to support this way of model passing.

Select2 Asp.net MVC - Dropdownlist is not valued with initial data

I am using a select2 using MVC 5 and C#.
I'm having trouble with the dropdownlist (select2) loading with initial data of the model.
The items passed in the corresponding binding field properly valued, but they are not shown in select2!
I mean, despite the list of the ViewModel field correctly valued by the controller, the dropdownlist (select2) is not valued correctly, as if the binding model did not work.
Needless to say, I'm googling for 1,5 days.
Fortunately (:)) I have no problem at the loading of select2 with all items, the dropdownlist works correctly even on the post, even I can take the selected items.
Many Thanks to all
P.s: Now that I'm writing, I have a doubt; Could be that select2 doesn't work with List ?
View
#section scripts{
...
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
}
#Html.DropDownListFor(model => model.MezziStraSelect, Model.MezziStraOrdinari, new { style = "width: 100%", #class = "form-control" })
JS
$(document).ready(function () {
//...
$("#MezziStraSelect").select2({
placeholder: "Select one or more items",
multiple: true,
allowClear: true
});
#if ( Model.MezziStraSelect == null)
{
<text>$("#MezziStraSelect").val(null).trigger("change");</text>
}
}
ViewModel
public Guid[] MezziStraSelect { get; set; }
public MultiSelectList MezziStraOrdinari { get; set; }
Controller
//Load List MezziStraOrdinari
var _stra = m.GetMezziStraordinari().Select(x => new
{
id = x.VoceSpesaID,
desc = x.VoceSpesa
}).ToList();
//view model set field
vm.MezziStraOrdinari = new MultiSelectList(_stra, "id", "desc");
//Load array Mezzi used from item selected
List<Guid> _mezziStraUsati = new List<Guid>();
var elems = dc.ItemSelected.FirstOrDefault(x => x.ItemID.ToString() == _guidSelected);
if (elems!=null)
{
elems.VociSpese.ToList().ForEach(x =>
{
if (x.VociSpesa.Straordinario == true)
_mezziStraUsati.Add(x.VoceSpesaViaggioID); //VoceSpesaViaggioID is GUID
});
if (_mezziStraUsati.Count>0)
vm.MezziStraSelect = _mezziStraUsati.ToArray(); //Guid[]
}
The initial loading of the Select2 unfortunately the binding cannot set the initial values!
So reading the Select2 documentation to this link , I had to proceed with a manual upload via JS.
Practically to try to do an human thing (I hope it is), I created an Extension method in the backend of the Model field
Extension Method
public static string ToSelect2Array<T>(this T[] values)
{
var resp = string.Empty;
values.ToList().ForEach(x => resp += $"'{x.ToString()}',");
if (resp.Length > 0)
resp = resp.Substring(0, resp.Length - 1);
return resp;
}
and then in JS client side, I call it like this:
VIEW (script JS)
#if ( Model.MezziStraSelect != null)
{
<text>
$("#MezziStraSelect").val([#Html.Raw(Model.MezziStraSelect.ToSelect2Array())]);
$("#MezziStraSelect").trigger('change');
</text>
}
else
{
<text>$("#MezziStraSelect").val(null).trigger("change");</text>
}
This is working quite well, but is it possible that there is no way to get it to bind automatically?
I would be curious to know other solutions, more elegant than this I would be grateful for!

Error in displaying ckeditor in MVC

In my MVC application I want to display the extracted text in CKEDITOR.
but the text is getting diplayed in textarea and not in editor
My controller code is:
public ActionResult ExtractText(string fn)
{
string extFile = Server.MapPath(_fileUploadPath + fn);
string filePath = Path.Combine(HttpContext.Server.MapPath(_fileUploadPath), Path.GetFileName(fn));
if (filePath != null)
{
SautinSoft.PdfFocus f = new SautinSoft.PdfFocus();
f.OpenPdf(System.IO.File.ReadAllBytes(filePath));
string text = f.ToText();
string sValue = "<textarea id = \"temp_edit\" name=\"content\" cols=\"73\" rows=\"15\">" + text + "</textarea> <script type=\"text/javascript\">CKEDITOR.replace('temp_edit');</script><input class=\"margin-top-05 green_button\" type=\"button\" value=\"Save\" onclick=\"save_file()\" /><a class=\"close\" onclick=\"parent.$.fancybox.close();\"><img class=\"close_image\" title=\"close\" src=\"../images/closelabel.gif\" style=\"width: auto; height: auto; position: absolute; bottom: 0px; right: 0px;\"></a>";
return Content(sValue);
}
else
{
TempData["UploadValidationMessage_Failure"] = "File does not exist";
return View();
}
}
The textarea styles, javascript events can be done on your view. Pass the text to the view and show it on the textarea. All your events and styles can be written on the view. The ckeditor can be loaded to the textarea on ready function. Please go through the following.
CK Editor for .Net MVC
For a better way to implement CKEditor in your project, please go through the aswer in the following link
CKEditor MVC 3 implementaion Help needed
Edit..
<%= Html.ActionLink("Extract Text", "ExtractText", new { fn = file })%>
takes you to your function.
Lets say, you have a model NewContent
public class NewContent
{
public string Text
{
get;
set;
}
}
Return the NewContent object with you text from the controller.
public ActionResult ExtractText(string fn)
{
string extFile = Server.MapPath(_fileUploadPath + fn);
string filePath = Path.Combine(HttpContext.Server.MapPath(_fileUploadPath), Path.GetFileName(fn));
if (filePath != null)
{
SautinSoft.PdfFocus f = new SautinSoft.PdfFocus();
f.OpenPdf(System.IO.File.ReadAllBytes(filePath));
string text = f.ToText();
NewContent content = new NewContent();
content.Text = text;
return View(content);
}
else
{
TempData["UploadValidationMessage_Failure"] = "File does not exist";
return View();
}
}
In your view, add the following
<script src="ckeditor/ckeditor.js"></script>
<script src="ckeditor/adapters/jquery.js"></script>
<%=Html.TextAreaFor(c => c.Text) %>
<script type="text/javascript">
$(function () {
$('#Text').ckeditor();
});
</script>
You will get your text from controller in the view in a ck editor. Make sure you have all necessary ckeditor scripts and its location provided correctly

How can I ignore case in a RegularExpression?

I have an asp.net MVC application. There is an entity called File that it has a property called Name.
using System.ComponentModel.DataAnnotations;
public class File {
...
[RegularExpression(#"([^.]+[.](jpg|jpeg|gif|png|wpf|doc|docx|xls|xlsx ..., ErrorMessage = "Invali File Name"]
public string Name{ get; set; }
...
}
There is a RegularExpressionValidator that checks file extensions.
Is there a quick way I can tell it to ignore the case of the extension without having to explicitly add the upper case variants to my validation expression?
I need this RegularExpressionValidator for both Server-side and client-side.
"(?i)" can be used for Server-side, but this doesn't work client-side
One way I can think of is writing a custom validation attribute:
public class IgnorecaseRegularExpressionAttribute : RegularExpressionAttribute, IClientValidatable
{
public IgnorecaseRegularExpressionAttribute(string pattern): base("(?i)" + pattern)
{ }
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ValidationType = "icregex",
ErrorMessage = ErrorMessage
};
// Remove the (?i) that we added in the pattern as this
// is not necessary for the client validation
rule.ValidationParameters.Add("pattern", Pattern.Substring(4));
yield return rule;
}
}
and then decorate your model with it:
[IgnorecaseRegularExpression(#"([^.]+[.](jpg|jpeg|gif|png|wpf|doc|docx|xls|xlsx", ErrorMessage = "Invalid File Name"]
public string Name { get; set; }
Finally write an adapter on the client:
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
jQuery.validator.unobtrusive.adapters.add('icregex', [ 'pattern' ], function (options) {
options.rules['icregex'] = options.params;
options.messages['icregex'] = options.message;
});
jQuery.validator.addMethod('icregex', function (value, element, params) {
var match;
if (this.optional(element)) {
return true;
}
match = new RegExp(params.pattern, 'i').exec(value);
return (match && (match.index === 0) && (match[0].length === value.length));
}, '');
</script>
#using (Html.BeginForm())
{
#Html.EditorFor(x => x.Name)
#Html.ValidationMessageFor(x => x.Name)
<input type="submit" value="OK" />
}
Of course you could externalize the client rules into a separate javascript file so that you don't have to repeat it everywhere.
Umh could you show the Client Validation?
umh I think that you can create your own Attribute that derives from RegularExpression and add the functionallity to ignore case.

MVC3 unobtrusive validation group of inputs

I need to validate 3 or more input fields (required at lest one). For example I have Email, Fax, Phone.
I require at least ONE to be filled in. I need both server and client 'unobtrusive validation'. please help. I looked into "Compare" method and tried modifying it but no luck. please help.
thanks
You could write a custom attribute:
public class AtLeastOneRequiredAttribute : ValidationAttribute, IClientValidatable
{
private readonly string[] _properties;
public AtLeastOneRequiredAttribute(params string[] properties)
{
_properties = properties;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (_properties == null || _properties.Length < 1)
{
return null;
}
foreach (var property in _properties)
{
var propertyInfo = validationContext.ObjectType.GetProperty(property);
if (propertyInfo == null)
{
return new ValidationResult(string.Format("unknown property {0}", property));
}
var propertyValue = propertyInfo.GetValue(validationContext.ObjectInstance, null);
if (propertyValue is string && !string.IsNullOrEmpty(propertyValue as string))
{
return null;
}
if (propertyValue != null)
{
return null;
}
}
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessage,
ValidationType = "atleastonerequired"
};
rule.ValidationParameters["properties"] = string.Join(",", _properties);
yield return rule;
}
}
which could be used to decorate one of your view model properties (the one you want to get highlighted if validation fails):
public class MyViewModel
{
[AtLeastOneRequired("Email", "Fax", "Phone", ErrorMessage = "At least Email, Fax or Phone is required")]
public string Email { get; set; }
public string Fax { get; set; }
public string Phone { get; set; }
}
and then a simple controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
Rendering the following view which will take care of defining the custom client side validator adapter:
#model MyViewModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
jQuery.validator.unobtrusive.adapters.add(
'atleastonerequired', ['properties'], function (options) {
options.rules['atleastonerequired'] = options.params;
options.messages['atleastonerequired'] = options.message;
}
);
jQuery.validator.addMethod('atleastonerequired', function (value, element, params) {
var properties = params.properties.split(',');
var values = $.map(properties, function (property, index) {
var val = $('#' + property).val();
return val != '' ? val : null;
});
return values.length > 0;
}, '');
</script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(false)
<div>
#Html.LabelFor(x => x.Email)
#Html.EditorFor(x => x.Email)
</div>
<div>
#Html.LabelFor(x => x.Fax)
#Html.EditorFor(x => x.Fax)
</div>
<div>
#Html.LabelFor(x => x.Phone)
#Html.EditorFor(x => x.Phone)
</div>
<input type="submit" value="OK" />
}
Of course the custom adapter and validator rule should be externalized into a separate javascript file to avoid mixing script with markup.
I spent more than 36 hours why the code did not work for me.. At the end , I found out that in my case , I was not supposed to use the property names in this line of code
[AtLeastOneRequired("Email", "Fax", "Phone", ErrorMessage = "At least Email, Fax or Phone is required")]
But I had to use the HTMl element Ids in place of the property names and it worked like magic.
Posting this here if it might help somebody.
Since you are using MVC 3, take a look at great video Brad Wilson had on mvcConf. There's everything you need to create client + server Unobtrusive Validation
#Darin Dimitrov 's solution is probably the standard of creating a custom validation attribute that works with unobtrusive validation. However, using custom validation attributes for unobtrusive validation have some disadvantages such as:
The custom validation attribute is only attached to one properties, so client validation will not work if there's a change event on the other two inputs.
The error message works fine with ValidationSummary, but if you want to display 1 error message for the whole group (which I think is the norm), it would be nearly impossible.
To avoid the first problem, we can add custom validation attribute to each element in the group, which will cause another problem: we have to validate all elements of the group, instead of stopping at the first invalid group element. And of course, the second problem - separate error messages for each element - still remains.
There's another way to handle client side validation of group of inputs, using groups setting in jquery validator (https://jqueryvalidation.org/validate/#groups). The only problem (and a big one) is that unobtrusive validation doesn't support jquery validation's groups by default, so we have to customize a little bit.
Although this answer is hardly "unobtrusive", in my opinion, it is worth trying to get rid of unnecessary complication of code, if your final goal is to validate a group of inputs while using Microsoft's unobtrusive validator library.
First, because groups settings of default jquery validator is not available in jquery unobtrusive validator, we have to override unobtrusive settings (ref. How can I customize the unobtrusive validation in ASP.NET MVC 3 to match my style?)
$("form").on('submit', function () {
var form = this;
var validator = $(this).data("validator");
if (validator.settings && !validator.settings.submitHandler) {
$.extend(true, validator.settings.rules, validationSettings.rules);
$.extend(true, validator.settings.groups, validationSettings.groups);
initGroups(validator);
var fnErrorReplacement = validator.settings.errorPlacement;
validator.settings.errorPlacement = function (error, element) {
validationSettings.errorPlacement(error, element, fnErrorReplacement, form);
}
validator.settings.submitHandler = formSubmitHandler;
}
});
function formSubmitHandler(form) {
form.submit();
}
After that, override unobtrusive validator's groups, rules and errorPlacement settings.
var validationSettings = {
groups: {
checkboxgroup: "Email Fax Phone"
},
rules: {
Email: {
required: function () {
return validateCheckboxGroup(["#Email", "#Fax", "#Phone"]);
}
},
Fax: {
required: function () {
return validateCheckboxGroup(["#Email", "#Fax", "#Phone"]);
}
},
Phone: {
required: function () {
return validateCheckboxGroup(["#Email", "#Fax", "#Phone"]);
}
}
}
,
errorPlacement: function (error, element, fnUnobtrusive, form) {
switch (element.attr("name")) {
case "Email":
case "Fax":
case "Phone":
onGroupError(error, "CheckBoxGroup", form);
break;
default:
fnUnobtrusive(error, element);
break;
}
}
}
function validateCheckboxGroup(names) {
var result = true;
$.each(names, function (index, value) {
if ($(value).is(":checked")) {
result = false;
}
});
return result;
}
Because unobtrusive validator does not implement groups setting of jquery validator, we need to reuse two functions from the two libraries to: (1).split group names (reusing code from jquery validator) and (2) append error element without remove 'input-validation-error' class (reusing function onError from unobtrusive library).
function initGroups(validators) {
validators.groups = {};
$.each(validators.settings.groups,
function (key, value) {
if (typeof value === "string") {
value = value.split(/\s/);
}
$.each(value,
function (index, name) {
validators.groups[name] = key;
});
});
}
function onGroupError(error, inputElementName, form) {
var container = $(form).find("[data-valmsg-for='" + inputElementName + "']"),
replaceAttrValue = container.attr("data-valmsg-replace"),
replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null;
container.removeClass("field-validation-valid").addClass("field-validation-error");
error.data("unobtrusiveContainer", container);
if (replace) {
container.empty();
error.appendTo(container);
}
else {
error.hide();
}
}
Finally, use HtmlExtensions.ValidationMessage to create error span of the checkbox group.
#Html.ValidationMessage("CheckBoxGroup", new { #class = "text-danger" })
The keeping of "input-validation-error" class is necessary, so that jquery validator will validate all 3 elements (Email, Phone, Fax) of checkbox group as a whole, instead of validating one by one. The unobtrusive validation library remove this class by default on function onError, so we have to customize this as shown in function onGroupError above.

Resources