I don't have the experience of working with Helpers so I am bit stuck in using a code at hand.
My requirement is simple and all I need is optgroup functionality in DropDownListFor extension method. While searching, I came across this Answer and have copied this as it is in a file named MyExtensionClass.cs.
But, I don't know how to use this or call the extension method defined in this. Please tell me how can i use this with my list.
Right now, following is the controller code for a selectlist for which i want to use the extension methods.
ViewBag.ParentCategoryId = new SelectList(db.Categories, "Id", "Name");
And this is my view code
#Html.DropDownListFor(model => model.Product.CategoryId,
(IEnumerable<SelectListItem>)ViewBag.CategoryId, "---Choose Category---",
new { #class = "required" })
Please help me upgrade this to extension method with optgroup.
We use Serge Zab's helper for optgroup dropdowns. Here is a sample:
The viewmodel:
public class OurViewModel
{
public int? TypeId { get; set; }
public IEnumerable<GroupedSelectListItem> GroupedTypeOptions { get; set; }
}
The controller:
public ActionResult Add()
{
var model = new OurViewModel
{
// fill with initial values
};
PutTypeDropDownInto(model);
return View(model);
}
[NonAction]
private void PutTypeDropDownInto(OurViewModel model)
{
model.GroupedTypeOptions = _repos.GetTypes()
.OrderBy(t => t.Category.EnglishName).ThenBy(t => t.EnglishName)
.Select(t => new GroupedSelectListItem
{
GroupKey = t.Category.RevisionId.ToString(),
GroupName = t.Category.EnglishName,
Text = t.EnglishName,
Value = t.RevisionId.ToString()
}
);
}
The view
#Html.DropDownGroupListFor(m => m.TypeId, Model.GroupedTypeOptions,
"[Select a type]")
Note that you can't use a regular SelectList. You have to use a collection of his GroupedSelectListItem class. Also, our solution doesn't use viewbag. The dropdown list is strongly typed on the viewmodel.
Update
To get the html helper to work in your view, the view needs to be able to find it. You can either add a #using directive at the top of the view with your MyExtensionClass.cs namespace, or add the namespace to the view-specific web.config, like so:
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="Microsoft.Web.Mvc" />
<add namespace="Namespace.For.MyExtensionClass" />
</namespaces>
</pages>
This was added to ASP.NET MVC at version 5.2!
Property Group in SelectListItem allows you to specify a group for each item
Html.DropDownList() and DropDownListFor() now generate optgroup elements based on the groups included on the list of items.
Related
I use the following to do validations on a form in blazor(server)
<EditForm Model="#place" OnValidSubmit="HandleValidSubmit" Context="placesEdit" >
<DataAnnotationsValidator />
<InputText #bind-Value="place.Name"/>
<ValidationMessage For="() => place.Name" />
</EditForm>
#{place=new Place(); }
the property Name as a [required] - Attribute. This works fine. When submitting the form I see the error message and the HandleValidSubmit isn't called
But when I try to do the same with a List the validation isn't happening. No error is displayed and HandleValidSubmit is called instead even if the requirements are not met:
<EditForm Model="#places" OnValidSubmit="HandleValidSubmit" Context="placesEdit" >
<DataAnnotationsValidator />
#foreach(var place in places) {
<InputText #bind-Value="place.Name"/>
<ValidationMessage For="() => place.Name" />
}
</EditForm>
#{places=new List<Place>(); }
What has do be done that the Validator also works in the loop?
Try if this helps:
Add the Microsoft.AspNetCore.Components.DataAnnotations.Validation NuGet package.
This is a pre-release package and latest version is 3.2.0-rc1.20223.4. There is a plan to include this on the native Blazor SDK but that version should work up to .NET 5. https://github.com/dotnet/aspnetcore/issues/22238#issuecomment-634266426
Replace your DataAnnotationsValidator with ObjectGraphDataAnnotationsValidator
You can check if your issue gets resolved with Step 2. If not, continue to Step 3.
Annotate your list property with ValidateComplexType.
You will need to create a class that will contain your list property.
check the docs for more information: https://learn.microsoft.com/en-us/aspnet/core/blazor/forms-validation#nested-models-collection-types-and-complex-types
If you're using IValidatableObject like me, the above solution won't work. The workaround is to create another property to link the validation into.
e.g.
public class MyModel : IValidatableObject
{
public List<Place> Places { get; } = new List<Place>();
public object PlacesValidation { get; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var isValid = ...;
if (isValid)
yield return new ValidationResult("Places is invalid.", new[] { nameof(PlacesValidation ) });
}
}
<ValidationMessage For="() => Model.PlacesValidation"/>
I would like to use custom #Html.ActionLink
I am trying to use the following code:-
public static class LinkExtensions
{
public static MvcHtmlString MyActionLink(
this HtmlHelper htmlHelper,
string linkText,
string action,
string controller)
{
var currentAction = htmlHelper.ViewContext.RouteData.GetRequiredString("action");
var currentController = mlHelper.ViewContext.RouteData.GetRequiredString("controller");
if (action == currentAction && controller == currentController)
{
var anchor = new TagBuilder("a");
anchor.Attributes["href"] = "#";
anchor.AddCssClass("currentPageCSS");
anchor.SetInnerText(linkText);
return MvcHtmlString.Create(anchor.ToString());
}
return htmlHelper.ActionLink(linkText, action, controller);
}
}
From Custom ActionLink helper that knows what page you're on
But I am getting
System.Web.Mvc.HtmlHelper' does not contain a definition for
'ActionLink' and no extension method 'ActionLink' accepting a first
argument of type 'System.Web.Mvc.HtmlHelper' could be found (are you
missing a using directive or an assembly reference?
Add this using System.Web.Mvc.Html; on top of your file
Make sure you have the namespace for your extensions class included in your web.config. For example:
namespace MyProject.Extensions
{
public static class LinkExtensions
{
//code
}
}
In your site Web.config and/or Web.config located in your "Views" folder:
<system.web>
<pages>
<namespaces>
<add namespace="MyProject.Extensions" />
</namespaces>
</pages>
</system.web>
Otherwise include a "using" block for the namespace at the top of your view page can work but for common namespaces I would do the above.
ASPX:
<%# Import namespace="MyProject.Extensions" %>
RAZOR:
#using MyProject.Extensions
Don't forget that the first parameter only accepts string. It will show you this error if it's NOT.
Make sure that you have following using in your class file:
using System.Web.Mvc.Html;
This is needed because the HtmlHelper class is located in System.Web.Mvc namespace but the ActionLink extension method is located in System.Web.Mvc.Html namespace.
If your using nopcommerce add this using statement at the top of your view file.
#using Nop.Web.Framework.UI
My issue was, I had incomplete syntax, "#Html.actionLink", in a view. Looks like I had started to add an action link and went a different direction but forgot to remove the partial action link, this caused the same error as above.... So check your syntax as that will throw the same runtime error. Good luck!
how to put class="active" within <li> depending on selected controller?
<li >Home</li>
<li >Cars</li>
Blessings
I generally create an action link html helper for accomplishing this task. Note, that I mark the link itself as "selected" vs the list item.
public static class ActionLinkHelpers
{
public static MvcHtmlString SelectedActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName)
{
var controller = (string) helper.ViewContext.RouteData.Values["controller"];
if (string.Compare(controller, controllerName, StringComparison.InvariantCultureIgnoreCase) == 0)
{
return helper.ActionLink(linkText, actionName, controllerName, null, new { Class = "selected" });
}
return helper.ActionLink(linkText, actionName, controllerName);
}
}
After you have an action link helper setup within your project your list would look as follows:
<li>#Html.SelectedActionLink("Home", "index", "Home")</li>
<li>#Html.SelectedActionLink("Cars", "index", "Car")</li>
EDIT:
In order to use a custom helper MVC must be aware of it. Add a new folder to your project "HtmlHelpers" for example and place this class inside of it. From there you need to add a line to the /Views/Web.config:
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="YourNameSpace.HtmlHelpers"/>
</namespaces>
</pages>
Currently, I have an MVC 3 app using the Razor View engine. I have unobtrusive validation enabled. The problem is that for some reason, on page load, my Edit View is displaying errors for required fields (even though the fields have a value). Has anyone else ran into this? Any suggestions for resolving this? Thanks.
Sample Field with problem:
<div class="full">
<label>Description:</label>
#Html.EditorFor(x=>x.Description, new{#class="super-textarea"})
#Html.ValidationMessageFor(x => x.Description)
</div>
Data Annotations on Model:
[Required, DataType(DataType.MultilineText)]
public virtual string Description { get; set; }
WebConfig enabled settings:
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
And of course the proper jquery files....
You can also clear the errors from the ModelState
ModelState.Clear();
Ok. Found the problem. Validation was happening due to Model binding attempting to take place. This was happening because our Get Method looks like this.
[HttpGet, RequestedObjectFilter]
public virtual ViewResult Edit(TKey id, T requestedObject)
{
return View(requestedObject);
}
A feature of .NET MVC is that anytime a reference value is passed as a parameter in the Method Signature of a ViewResult, ModelBinding is triggered, which in turn fires off validation. The reason that we were passing in the object to our method was due to our RequestedObjectFilter which would fetch the related entity from our abstracted repository, and pass it in to this method via the ActionParameters property. We refactored our RequestedObjectFilter to set the ViewModel instead, allowing us to remove the parameter from the method, thus solving the problem. Now our method looks like this:
[HttpGet, RequestedObjectFilter]
public virtual ViewResult Edit(TKey id)
{
return View();
}
I am trying to use the following helper class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;
using System.Web.Mvc;
using System.Web.Routing;
namespace myNamespace
{
public static class ValidationHelper
{
public static MvcHtmlString ValidationImage(this HtmlHelper helper, string name)
{
if (helper.ViewData.ModelState[name] == null || helper.ViewData.ModelState[name].Errors == null || helper.ViewData.ModelState[name].Errors.Count == 0)
{
return MvcHtmlString.Empty;
}
var tag = new TagBuilder("img");
tag.Attributes.Add("src", "../Content/Images/check_bg_invalid.png");
tag.Attributes.Add("alt", helper.ViewData.ModelState[name].Errors[0].ErrorMessage);
tag.Attributes.Add("title", helper.ViewData.ModelState[name].Errors[0].ErrorMessage);
tag.Attributes.Add("class", HtmlHelper.ValidationMessageCssClassName);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.SelfClosing));
}
}
}
And using this in a view:
#Html.ValidationImage("FirstName")
to try and get an image to fire when there is a validation error.
So I have my:
#Html.TextBox("FirstName", null, new { #class = "my_input" })
in the same view (all within the #Html.BeginForm)
This is my model.cs class:
public class QuoteModel
{
[Required(ErrorMessage = "You must specify a First Name.")]
public string First Name { get; set; }
}
And this is my controller.cs class:
public ActionResult Quote()
{
//return View();
var model = new QuoteModel();
return View(model);
}
[HttpPost]
public ActionResult Quote(QuoteModel model)
{
if (ModelState.IsValid)
{
try
{
}
catch (Exception)
{
return View(model);
}
}
return RedirectToAction("QuoteSuccess");
}
public ViewResult QuoteSuccess()
{
return View();
}
Bear in mind, I may have made a typo in all of the preceding code blocks.
Originally, I used
#Html.ValidationMessage("FirstName")
and the Error Message would fire after clicking send. I am trying to now use the above helper for ValidationImage, but nothing fires (and the Error Message doesn't show). Nor is anything output with an <img> tag. No errors are thrown on the page.
As an aside, I have two other questions:
1) I would also like to display a validation success message (i.e., a little image with "OK"), but am having trouble just getting above to work in the first instance.
2) I was previously unsuccessful (before using helper) to get client side validation to show.
Am using
#{ Html.EnableClientValidation(); }
in that same view. I placed it at the top (after #model). Does placement matter?
I also used the following in my _layout.cshtml
<script language="javascript" src="#Url.Javascript("jquery.validate.min")" type="text/javascript"></script>
<script language="javascript" src="#Url.Javascript("jquery.validate.unobtrusive.min")" type="text/javascript"></script>
Note that I use a helper, so no need for ".js" extension - this has worked in the past and shows up in view source. So no problems there.
Finally, I do have my web.config set up thusly:
<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
Any thoughts on either of my problems (particularly the main one) are much appreciated.
It looks to me like your validation image will only be output in the page if the server-side validation fails. This means your client-side validation can't possibly show the image, as it won't exist in the HTML markup (return MvcHtmlString.Empty;)
If you want the validation to work client-side (and show the image), you might have to do some work to hook into the existing validation. There's an article here about custom validation:
http://www.codeproject.com/Articles/98376/Including-custom-client-side-validations-in-your-A.aspx
Though it might be a little overly complicated - there may be an easier way to hook into it (though I can't find one just now).