That's my URL:
http://localhost:7071/TODO?page=1&codcat=56
It returns me a simple form , it is my form of research:
<div class="col-md-8">
#using (Html.BeginForm("Index", "TODO", FormMethod.Get))
{
<p>
Find in ...: #Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
<input type="submit" value="Search" />
</p>
}
<br />
</div>
The Search method in TODO control is :
public async Task<ActionResult> Index(string sortOrder, string currentFilter, string searchString, int? page, int? codcat)
{
ViewBag.CurrentSort = sortOrder;
ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "title_desc" : "";
ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";
if (searchString != null)
{
page = 1;
}
else
{
searchString = currentFilter;
}
ViewBag.CurrentFilter = searchString;
var todos = from v in db.TODOs
join cv in db.TODO_CATEGORIA on v.ID equals cv.ID_TODO
where cv.ID_CATEGORIA == codcat select v;
if (!String.IsNullOrEmpty(searchString))
{
todos = todos.Where(s => s.TODO_TITLE.Contains(searchString)
|| s.DESCRIPTION.Contains(searchString)
);
}
switch (sortOrder)
{
case "title_desc":
todos = todos.OrderByDescending(s => s.TODO_TITLE);
break;
case "Date":
todos = todos.OrderBy(s => s.DATE);
break;
case "date_desc":
todos = todos.OrderByDescending(s => s.DATE);
break;
default:
todos = todos.OrderBy(s => s.TODO_TITLE);
break;
}
int pageSize = 21;
int pageNumber = (page ?? 1);
return View(todos.ToPagedList(pageNumber, pageSize));
}
The problem is that the codcat parameter is sent by the form as null.
How could I do to the form keeping the value of the parameter codcat and send the parameter research.
The one way is to use hidden fields.
<input type="hidden" name="codcat" value="#Request.QueryString["codcat"]" />
When submitting a form the POST request is sent to the server, and all your GET request's parameters are left behind.
OR to use this:
public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, object routeValues, FormMethod method);
OR this overload of BeginForm:
public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, FormMethod method, object htmlAttributes);
Related
I found something strange and i need a professional opinion.. i have ASP.NET MVC application has views for data with sorting, search, and pagination. and i used MultipleButton Data Annotation so i can have multiple actions with the same parameters as the followings:
[SessionAuthorize]
[AuthorizationFilter(Page = PageEnum.EgyptianEmployees, PageAction = PageActionEnum.View)]
public ActionResult Index(string sortOrder, string searchString, string currentFilter,int? searchType , int? filtertype, int? page)
{
//Normal View index with filter, sorting, and pagination
}
[SessionAuthorize]
[AuthorizationFilter(Page = PageEnum.EgyptianEmployees, PageAction = PageActionEnum.View)]
[MultipleButton(Name = "action", Argument = "IndexSearch")]
public ActionResult IndexSearch(string sortOrder, string searchString, string currentFilter, int? searchType, int? filtertype, int? page)
{
return RedirectToAction("Index", new { sortOrder = sortOrder, searchString = searchString, currentFilter = currentFilter, searchType = searchType, filtertype = filtertype, page = page });
}
[SessionAuthorize]
[MultipleButton(Name = "action", Argument = "IndexReport")]
[AuthorizationFilter(Page = PageEnum.EgyptianEmployees, PageAction = PageActionEnum.View)]
public ActionResult IndexReport(string searchString, int? searchType)
{
//Some Reporting Technique
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MultipleButtonAttribute : ActionNameSelectorAttribute
{
public string Name { get; set; }
public string Argument { get; set; }
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
var isValidName = false;
var keyValue = string.Format("{0}:{1}", Name, Argument);
var value = controllerContext.Controller.ValueProvider.GetValue(keyValue);
if (value != null)
{
controllerContext.Controller.ControllerContext.RouteData.Values[Name] = Argument;
isValidName = true;
}
return isValidName;
}
}
And for my view was like this:
#using (Html.BeginForm("", "EgyptionEmployee", FormMethod.Get))
{
<div class="col-lg-12">
#Html.DropDownList("searchType", (SelectList)ViewBag.SearchList, new { #class = "form-control" })
</div>
<hr />
<div class="col-lg-12">
<div class="input-group" dir="ltr">
<input type="text" id="SearchString" value="#ViewBag.CurrentFilter" name="SearchString" class="form-control" dir="rtl" placeholder="Search ...">
<span class="input-group-btn">
<button class="btn btn-secondary" type="submit" value="Search" name="action:IndexSearch">Search</button>
<button class="btn btn-primary" type="submit" value="Report" name="action:IndexReport">Download Search Report</button>
</span>
</div>
</div>
}
But i got and error of:
The current request for action 'Index' on controller type 'ForignEmployeeController' is ambiguous between the following action methods:
System.Web.Mvc.ActionResult IndexSearch(System.String, System.String, System.String, System.Nullable'1[System.Int32], System.Nullable'1[System.Int32], System.Nullable'1[System.Int32]) on type Tourism.Controllers.ForignEmployeeController
System.Web.Mvc.ActionResult Index(System.String, System.String, System.String, System.Nullable'1[System.Int32], System.Nullable'1[System.Int32], System.Nullable'1[System.Int32]) on type Tourism.Controllers.ForignEmployeeController
After hours of struggling
I Just replaced the Data Annotations Order
I put MultipleButton before AuthorizationFilter like this:
[MultipleButton(Name = "action", Argument = "IndexSearch")]
[AuthorizationFilter(Page = PageEnum.EgyptianEmployees, PageAction = PageActionEnum.View)]
My Question is why that fixed the problem and is there any type order should i follow in the future to avoid this kind of errors?
In certain cases I want to display SelectList object not using DropDownListFor helper. Instead, I want to create a helper that iterates over the SelectListItems, and draw something different.
I have created an EditorTemplate:
#model RadioButtonOptions
<div class=" switch-field noselect" style="padding-left: 0px;">
#foreach (SelectListItem op in Model.Values.Items)
{
var idLabelF = ViewData.TemplateInfo.GetFullHtmlFieldId("") + "_" + op.Value;
var esChecked = "";
if (op.Selected)
{
esChecked = "checked";
}
<input type="radio" id="#idLabelF" name="#(ViewData.TemplateInfo.GetFullHtmlFieldName(""))" value="#op.Value" #esChecked />
<label for="#idLabelF" style="width: 100px;">#op.Text</label>
}
</div>
The RadioButtonOptions class is a ViewModel:
public class RadioButtonOptions
{
public SelectList Values { get; set; }
}
The final resul looks like this:
My ViewModel is like this (simplified):
public class MainVisitVM
{
public MainVisit Visit { get; set; }
public RadioButtonOptions VisitOptions { get; set; }
}
And I use it in Razor View as:
<div class="clearfix">
#Html.LabelFor(x=> x.Visit.Tipo)
<div class="input">
#Html.EditorFor(x=> x.VisitOptions ) //HERE
</div>
</div>
The problem I have is that I want this to work more like the DropDownListFor, so the lamda expresion I pass is the property holding the selected value, and then just pass the SelectList object (or a custom list).
<div class="clearfix">
#Html.LabelFor(x=> x.Visit.Tipo)
<div class="input">
#Html.CustomDropDownListFor(x=> x.Visit.Tipo, Model.VisitOptions ) //This would be ideal!!
</div>
</div>
So, I think doing this using EditorTemplates will not be possible.
Any idea in how to accomplish this?
Thanks to #StephenMuecke suggestion, I ended up with this HtmlHelper extension method:
public static MvcHtmlString RadioButtonForSelectList<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
SelectList listOfValues)
{
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
if (listOfValues == null) return MvcHtmlString.Create(string.Empty);
var wrapperDiv = new TagBuilder("div");
wrapperDiv.AddCssClass("switch-field noselect");
wrapperDiv.Attributes.Add("style", "padding-left: 0px;");
var sb = new StringBuilder();
foreach (SelectListItem item in listOfValues)
{
var idLabelF = htmlFieldName.Replace(".","_") + "_" + item.Value;
var label = htmlHelper.Label(idLabelF, item.Text, new { style = "width: 100px;" }).ToHtmlString();
var radio = htmlHelper.RadioButtonFor(expression, item.Value, new { id = idLabelF }).ToHtmlString();
sb.AppendFormat("{0}{1}", radio, label);
}
wrapperDiv.InnerHtml = sb.ToString();
return MvcHtmlString.Create(wrapperDiv.ToString());
}
Not particulary proud of my htmlFieldName.Replace(".","_"), but works.
I have a form for upload images in asp.net mvc 5 .
Every thing is OK , but there is a problem with Validation Error Message. If I select image in upload control or not , My webApplication always give me alert message error happened , your data is not valid .
but when I delete [Required(ErrorMessage = "select image plz")] from MainGoodMetaData.cs it works fine !
Could any one help me please ? Thanks a lot
MainGoodMetaData.cs
[Required(ErrorMessage = "select image plz")]
[DisplayName("good image")]
[Display(Name = "good image")]
[DataType(System.ComponentModel.DataAnnotations.DataType.ImageUrl)]
public string GoodImage { get; set; }
AddMainGood.cshtml
<div class="form-group">
<div class="col-md-10">
#Html.Upload("UploadImage")
#Html.ValidationMessageFor(model => model.GoodImage)
</div>
#Html.LabelFor(model => model.GoodImage, new { #class = "control-label col-md-2" })
</div>
Admin controller
[HttpPost]
public ActionResult AddMainGood(MainGood maingood, HttpPostedFileBase UploadImage)
{
MainGoodRepositories blMainGood = new MainGoodRepositories();
string path2 = "";
var fileName2 = "";
var rondom2 = "";
if (UploadImage != null)
{
fileName2 = Path.GetFileName(UploadImage.FileName);
string pic = System.IO.Path.GetFileName(UploadImage.FileName);
rondom2= Guid.NewGuid() + fileName2;
path2 = System.IO.Path.Combine(
Server.MapPath("~/Images/MainGoods"), rondom2);
maingood.GoodImage = rondom2;
}
if (ModelState.IsValid)
{
UploadImage.SaveAs(path2);
maingood.GoodImage = rondom2;
if (blMainGood.Add(maingood))
{
return JavaScript("alert('added');");
}
else
{
return JavaScript("alert('didn't add');");
}
}
else
{
return JavaScript("alert('error happened , your data is not valid');");
}
}
UploadHelper.cs
public static class UploadHelper
{
public static MvcHtmlString Upload(this HtmlHelper helper, string name, object htmlAttributes = null)
{
TagBuilder input = new TagBuilder("input");
input.Attributes.Add("type", "file");
input.Attributes.Add("id", helper.ViewData.TemplateInfo.GetFullHtmlFieldId(name));
input.Attributes.Add("name", helper.ViewData.TemplateInfo.GetFullHtmlFieldName(name));
if (htmlAttributes != null)
{
var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
input.MergeAttributes(attributes);
}
return new MvcHtmlString(input.ToString());
}
public static MvcHtmlString UploadFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, object htmlAttributes = null)
{
var data = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
TagBuilder input = new TagBuilder("input");
input.Attributes.Add("type", "file");
input.Attributes.Add("id", helper.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(expression)));
input.Attributes.Add("name", helper.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression)));
if (htmlAttributes != null)
{
var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
input.MergeAttributes(attributes);
}
return new MvcHtmlString(input.ToString());
</div>
There are a lot of ways for validating a file input both server side and client side, it depends on the situation you're working on. for example, if you want a complex validation on the server side you can create a custom validation attribute. But if you want just a simple required file input using client side validation you should change your model:
[Required(ErrorMessage = "Select a file")]
public HttpPostedFileBase GoodImage { get; set; }
As you can see, I the GoodImage property to HttpPostedFileBase, then inside your view we just simply add required validation manually:
<div class="form-group">
<div class="col-md-10">
<input type="file" data-val="true" data-val-required="please select a file" name="GoodImage" />
#Html.ValidationMessage("file")
</div>
#Html.LabelFor(model => model.GoodImage, new {#class = "control-label col-md-2"})
</div>
<input type="submit" value="Send"/>
And your action method:
[HttpPost]
public ActionResult AddMainGood(MainGood model)
{
if (!ModelState.IsValid)
{
return View(model);
}
string path2 = "";
var fileName2 = "";
var rondom2 = "";
fileName2 = Path.GetFileName(model.GoodImage.FileName);
string pic = System.IO.Path.GetFileName(model.GoodImage.FileName);
rondom2 = Guid.NewGuid() + fileName2;
path2 = System.IO.Path.Combine(
Server.MapPath("~/Images/MainGoods"), rondom2);
// other code
}
I am getting :
The parameter conversion from type 'System.String' to type 'System.Web.Mvc.SelectListItem' failed because no type converter can convert between these types.
error when posting a form to controller , below is the code i am using :
Model
public IList<SelectListItem> Gender { get; set; }
public IList<SelectListItem> Disablity { get; set; }
Controller
public ActionResult Index()
{
FormViewModel objStudentModel = new FormViewModel();
List<SelectListItem> genderNames = new List<SelectListItem>();
genderNames.Add(new SelectListItem { Text = "Male", Value = "1" });
genderNames.Add(new SelectListItem { Text = "Female", Value = "2" });
genderNames.Add(new SelectListItem { Text = "Prefer not to say", Value = "3" });
objStudentModel.Gender = genderNames1;
objStudentModel.Disablity = genderNames1;
return PartialView("ApplicationForm", objStudentModel);
}
[HttpPost]
public ActionResult HandleFormSubmit(string[] gender, string[] disability, FormViewModel model)
{
//model not valid, do not save, but return current umbraco page
if (ModelState.IsValid == false) // this always comes false
{
return CurrentUmbracoPage();
}
string form = "Values + Environment.NewLine;"
if (gender != null)
{ form += "Gender: " + gender[0] + Environment.NewLine; }
else
{ form += "Gender: " + "Prefer not to say" + Environment.NewLine; }
if (disability != null)
{ form += "Disability: " + disability[0] + Environment.NewLine; }
else
{ form += "Disability: " + "Prefer not to say" + Environment.NewLine; }
return RedirectToCurrentUmbracoPage();
}
View
using (Html.BeginUmbracoForm<FormSurfaceController>("HandleFormSubmit"))
{
//var errors = ModelState.Values.SelectMany(v => v.Errors);
#Html.ValidationSummary();
<div class="col-12 col-sm-12 col-lg-12 ">
<label>Are you:</label>
#foreach (var names1 in #Model.Gender)
{
var tdId = "rd" + names1.Value;
<input type="radio" id="#tdId" class="chkclass" value="#names1.Text" name="gender" style="padding-right:5px;"/>
#names1.Text
}
</div>
</div>
<div class="col-12 col-sm-12 col-lg-12 ">
#foreach (var names in #Model.Disablity)
{
var tdId = "rd" + names.Value;
<input type="radio" id="#names.Value" class="chkclass" value="#names.Text" name="disability" style="padding-right:5px;"/>
#names.Text
}
</div>}
I have debugged using Visual stdio , the error is generating on the Gender list which is failing the modelstate. Any ideas where I am getting it wrong , coz i have tried all the possible solutions within my approach without any success, Any assistance and help will be appreciated .
Thanks
Your model properties Gender and Disablity would conflicts when submit form post.
MVC try to convert radio inputs selected values (which is of type string[]) to your ViewModel property type IList<SelectListItem> Gender
rename the selected values or add new properties
Model properties
public IList<SelectListItem> Gender { get; set; }
public IList<SelectListItem> Disablity { get; set; }
public string[] SelectedGender { get; set; }
public string[] SelectedDisablity { get; set; }
View
rename your input to "SelectedGender" and "SelectedDisablity"
#foreach (var names1 in #ViewBag.Gender)
{
var tdId = "rd" + names1.Value;
<input type="radio" class="chkclass" value="#names1.Text" name="SelectedGender" />
#names1.Text
}
Action
also in action parameters
public ActionResult HandleFormSubmit(string[] selectedGender, string[] selectedDisability, FormViewModel model){ //...
I'm using ASP.NET MVC 2, but I don't think I've heard about this being fixed in MVC 3 or 4, but anyway:
This is my test view code:
<br />
<%= Html.LabelFor( m => m.FieldFoo ) %>
<%= Html.TextBoxFor( m => m.FieldFoo ) %>
<br />
<%= Html.LabelFor( m => m.CustomFieldValues[0].Value ) %>
<%= Html.TextBoxFor( m => m.CustomFieldValues[0].Value ) %>
And this is what is rendered:
<br />
<label for="FieldFoo">Foo?</label>
<input id="FieldFoo" name="FieldFoo" type="text" value="foo" />
<br />
<label for="CustomFieldValues[0]_Value">Value</label>
<input id="CustomFieldValues_0__Value" name="CustomFieldValues[0].Value" type="text" value="bar" />
Spot the difference: the indexed property CustomFieldValues is not having its [ and ] characters replaced with _ for the for="" attribute. Why?
I stepped inside the LabelFor code and saw that it calls html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName)); whereas MVC's internal InputHelper has its own logic using TagBuilder.CreateSanitizedId() which explains why it's getting different id="" attribute values.
Is there any workaround for this in MVC 2?
ASP.NET MVC 2 doesn't have LabelFor extension method with htmlAttributes parameteres. See this blog post. We can create an extension which will add this paramter as well as solve your above issue. Here is the sample,
public static class MyTagBuilder
{
public static string CreateSanitizedId(string originalId)
{
return CreateSanitizedId(originalId, HtmlHelper.IdAttributeDotReplacement);
}
public static string CreateSanitizedId(string originalId, string invalidCharReplacement)
{
if (String.IsNullOrEmpty(originalId))
{
return null;
}
if (invalidCharReplacement == null)
{
throw new ArgumentNullException("invalidCharReplacement");
}
char firstChar = originalId[0];
if (!Html401IdUtil.IsLetter(firstChar))
{
// the first character must be a letter
return null;
}
StringBuilder sb = new StringBuilder(originalId.Length);
sb.Append(firstChar);
for (int i = 1; i < originalId.Length; i++)
{
char thisChar = originalId[i];
if (Html401IdUtil.IsValidIdCharacter(thisChar))
{
sb.Append(thisChar);
}
else
{
sb.Append(invalidCharReplacement);
}
}
return sb.ToString();
}
private static class Html401IdUtil
{
private static bool IsAllowableSpecialCharacter(char c)
{
switch (c)
{
case '-':
case '_':
case ':':
// note that we're specifically excluding the '.' character
return true;
default:
return false;
}
}
private static bool IsDigit(char c)
{
return ('0' <= c && c <= '9');
}
public static bool IsLetter(char c)
{
return (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'));
}
public static bool IsValidIdCharacter(char c)
{
return (IsLetter(c) || IsDigit(c) || IsAllowableSpecialCharacter(c));
}
}
}
public static class LabelExtensions
{
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
{
return LabelFor(html, expression, new RouteValueDictionary(htmlAttributes));
}
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
if (String.IsNullOrEmpty(labelText))
{
return MvcHtmlString.Empty;
}
TagBuilder tag = new TagBuilder("label");
tag.MergeAttributes(htmlAttributes);
tag.Attributes.Add("for", MyTagBuilder.CreateSanitizedId(html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName)));
tag.SetInnerText(labelText);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
}
<%# Import Namespace="NameSpaceOfTheExtensionMethodClass" %>
<%= Html.LabelFor(m => m.CustomFieldValues[0].Value, new { })%>
<%= Html.TextBoxFor( m => m.CustomFieldValues[0].Value )%>