I'm developing an ASP.NET MVC app using the latest .NET Framework and VS 2019.
In my page, I have an simple textbox and a search button. When I click the search button and postback the form, in controller, I get model.Code is NULL
Here is my code - my view model:
public class UserManagementViewModel
{
public string Code { get; set; }
// some other properties including simple and complex types
}
In View:
<td style="min-width: 300px">
#Html.TextBoxFor(m => m.Code)
</td>
In Controller:
[HttpPost]
public ActionResult UpdateUserPermissions(UserManagementViewModel model)
{
// model.Code is null here!!
return RedirectToAction("UserManagement");
}
After hours of research, I tried following:
I looked into html source and found
<input id="Code" name="Code">
Using Chrome dev tool, I change the name of the input to "Model.Code"
<input id="Code" name="Model.Code">
And this time, I got the entered value in textbox after postback.
I don't know why this happens and why the element name is not generated correctly!
What can I do?
Edit:
I have some other controls like checkbox and radio-buttons which I use pure HTMl code and name them beginning with "Model." like below instead of using #HTML helper.
And it works fine and I get the value in Action.
Workaround:
I found a workaround here: ASP.NET MVC 4 override emitted html name and id so I change my code to:
#Html.TextBoxFor(m => m.Code, new { #Name = "Model.Code" })
However this code helps me to fix my problem, I still don't know why the name generated by Razor engine is not working.
The helper method #Html.TextBoxFor works absolutely correctly. Basically, you need to use this method for almost all situations (except some situations when you need to use no-standard behavior).
Regarding your case, I think you have trouble with HtmlFieldPrefix.
This property is used for the generation of HTML input's name. We can review MVC source codes in GitHub...
MVC generated HTML input (GitHub link) using this code snippet:
private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes)
{
string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (String.IsNullOrEmpty(fullName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
}
TagBuilder tagBuilder = new TagBuilder("input");
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));
tagBuilder.MergeAttribute("name", fullName, true);
string valueParameter = htmlHelper.FormatValue(value, format);
bool usedModelState = false;
...
return tagBuilder.ToMvcHtmlString(TagRenderMode.SelfClosing);
}
As we can see name property calculated by method GetFullHtmlFieldName.
This method we can also inspect on GitHub (link):
public string GetFullHtmlFieldName(string partialFieldName)
{
if (partialFieldName != null && partialFieldName.StartsWith("[", StringComparison.Ordinal))
{
// See Codeplex #544 - the partialFieldName might represent an indexer access, in which case combining
// with a 'dot' would be invalid.
return HtmlFieldPrefix + partialFieldName;
}
else
{
// This uses "combine and trim" because either or both of these values might be empty
return (HtmlFieldPrefix + "." + (partialFieldName ?? String.Empty)).Trim('.');
}
}
This method calculates the name using the HtmlFieldPrefix property. So if you change this property you can modify your generated name.
I think you use partial Views and you need to render property with some prefix for correct binding.
Please review next links with examples and possible solutions:
ASP.NET MVC Partial Views with Partial Models
ASP.NET MVC3 add a HtmlFieldPrefix when calling Controller.PartialView
Define a custom HTML prefix for your .NET MVC models
in other to sumbmit model back you have to use form tags. MVC sometimes counts name as model property, but it is better to do it explicitly. This code was tested and both variants of textbox are working properly. And if you use the form you don't need any name at all, other contrary in the most cases you will get null if you use the name and form together.
#model UserManagementViewModel
<form asp-action="UpdateUserPermissions" method="post">
#Html.TextBoxFor(m => m.Code)
// or
<div class="form-group">
<label asp-for="Code" class="control-label"></label>
<input asp-for="Code" class="form-control" />
</div>
<div>
<button type="submit"> submit </button>
</div>
</form>
Related
I have 2 ASP.NET MVC action methods, I call the first method by passing and load some initial data, then I get some additional details from UI and call the second action method (Post action method from .cshtml). The data I received from the first call is missing in the post method. can anyone help me what am I doing wrong or missing here?
Action methods:
[Route("setprofile")]
public ActionResult SetProfile(string id)
{
ProfileData data = new ProfileData();
//do something
data.name= getData(id);
return this.View(data);
}
[Route("setprofile")]
[HttpPost]
public ActionResult SetProfile(ProfileData data)
{
// Here I'm not missing the data.name field value
}
View .cshtml file:
<div class="panel-body">
#using (Html.BeginForm("SetProfile", "Home", FormMethod.Post))
{
<div>
<h3> Name: #(this.Model.name)</h3>
</div>
<h3>
Comments:#Html.TextBoxFor(m => m.comments)
</h3>
}
I get the comments value but not getting the name field value from the model here.
Note: I need to display the value I received from the first action method as a label, not text box.
There are two things, Name is writen as text and in order to send back to server, you need to put it inside input element.
IF you dont want to show it #Html.HiddenFor(m => m.name) creates hidden input element.
Other than this, check ModelState for validation errors..
if (!ModelState.IsValid)
return BadRequest(ModelState);
.... your code here
if your model is not valid, the ProfileData returns result
You haven't added an input element for it to be sent back to the server when the form is submitted. If you don't want it to be visible, whilst still being posted back, add a hidden field for it:
#Html.HiddenFor(m => m.name)
Without that, all you're doing is rendering name to the markup but, once the form is submitted, it won't be sent back. Alternatively, you could render a textbox for the value whilst setting its readonly attribute. That would allow it to be visible, not changed, and still be sent back to the server.
#Html.TextBoxFor(m => m.name, new { #readonly = "readonly" })
Caution, before You read the rest
This question is not about POST method, redisplaying view with submited form or binding input values to controller method parameters. It's purely about rendering the View using html helper (HiddenFor or Hidden - both returns the same).
I created a simple hidden field using HiddenFor helper
#Html.HiddenFor(m => m.ProductCode)
and my problem is that value for this hidden field is rendered as null:
<input id="productCode" name="productCode" type="hidden" value/>
Even if I set it when instantiating a model and of course it's confirmed with debugging (it always has a value).
So instead it should look like this:
<input id="productCode" name="productCode" type="hidden" value="8888888"/>
Because I know there are some questions like this one (actually all of them refer to changing form values during form POST) I included list of things I tried already. My code is right below this section which I belive to be essential.
So far I tried:
ModelState.Clear() everywhere possible - cause as we know the value from ModelState is first place where it looks for the value. NO EFFECT which I expected, cause my ModelState is empty (my case is not about changing value during POST in controller as in many questions like that), so it should take value from my view model.
Not using HiddenFor helper, but pure html instead. WORKS, but its just workaround, not an answer to the problem.
Duplicating line with helper in view as follows:
#Html.HiddenFor(m => m.ProductCode)
#Html.HiddenFor(m => m.ProductCode)
PARTIALLY WORKS Produces first input as value/> and second as value="8888888"/> which indicates that there is probably something that hides initial property value. Anyway, I found nothing in ViewData at any point, nor in query string. Obviously I can't accept it this way, no explonation needed I guess.
Changing name of the property. Originally it was ProductCode. I changed it to productCode, ProdCode, ProductCodeasd, etc. and all of these WORKS. Again, it looks like there is something that hides/updates the value, but again - 100% sure there is no JS or anything else doing it. So still no answer found - just workaround again.
Explicitly setting the value for HiddenFor: #Html.HiddenFor(x => x.ProductCode, new {Value = #Model.ProductCode}). NO EFFECT, renders the same way.
Using #Html.Hidden instead of #Html.HiddenFor. NO EFFECT, with name set as ProductCode it renders the same way.
One more thing I found interesting. Reading html with Display page source in Chrome [ctrl+U] shows that value is valid value="8888888"/>, but in DevTools it's still value/> and of course submitting the form passes null to Controller method.
Model
public class Product
{
public string Description { get; set; }
public int Quantity { get; set; }
public string ProductCode { get; set; }
public string ImageUrl { get; set; }
public Product(string desc, string productCode, string imgUrl)
{
Description = desc;
ProductCode = productCode;
ImageUrl = imgUrl;
}
}
View
#model Product
#using (Html.BeginForm("UpdateCart", "Cart"))
{
<div class="row pad10">
<div class="col-sm-6 text-center">
<img src="#Model.ImageUrl" width="300" height="300" />
</div>
<div class="col-sm-6 text-justify">
<p>#Model.Description</p>
<div class="row padding-top-2">
<div class="col-sm-6">
<label>#CommonResources.Quantity: </label>
</div>
<div class="col-sm-4">
#Html.TextBoxFor(m => m.Quantity, new
{
#class = "form-control",
#data_val_required = CommonResources.FieldRequired,
#data_val_number = CommonResources.ValidationNumber
})
#Html.ValidationMessageFor(model => model.Quantity, "", new { #class = "text-danger" })
#Html.HiddenFor(m => m.ProductCode)
</div>
</div>
</div>
</div>
<div class="text-center col-xs-12 padTop20 padBottom20">
<input type="submit" value="Submit" class="whtBtn pad" />
</div>
}
Controller
The view is returned from controller with RedirectToAction as follows:
ValidateAndProceed -> ResolveNextStep (here redirection occurs) -> ShowProduct
public ActionResult ValidateAndProceed()
{
var order = Session.Current.Order;
var lang = LangService.GetSelectedLanguage();
var invoice = Session.Current.CurrentInvoice;
var localCurrency = Session.Current.LocalCurrencyInfo;
List<CheckoutValidationFieldError> errors = new List<CheckoutValidationFieldError>();
errors = ValidationService.ValidateAddress(order);
if (errors.Count > 0)
{
return RedirectToAction("InvalidAddress", "Address", new { serializedErrors = JsonConvert.SerializeObject(errors) });
}
return ResolveNextStep(order, invoice);
}
public ActionResult ResolveNextStep(IOrder order, IInvoice invoice)
{
if (OrderService.ShowProductView(order, invoice))
{
return RedirectToAction("ShowProduct");
}
return RedirectToAction("Summary");
}
public ActionResult ShowProduct()
{
Product model = ProductService.GetProduct(Session.Current.CurrentInvoice);
return View("~/Views/Product.cshtml", model );
}
Finally, what can cause such a weird behavior? I've already ran out of options. Maybe anyone had problem like mine before, would appreciate any clue on this case.
I debugged the whole process of rendering the view (got into .Net sources) checking every possible place that could make it fail and found nothing.
After #AndyMudrak and #Jeremy Lakeman comments I decided to try again to find JavaScript responsible for that behavior, but deeper than I did before. What I found was a really silly script where element Id is being concatenated from three strings what I didn't expect, cause it's really badly implemented. So finally - JavaScript is doing it and there is no bad behavior from framework etc.
Actually I am a bit disappointed (even if it's good to know this easy answer) cause it looked much more complicated than it really was and it took me hours to find out how simple it is :|
Thanks for comments, sorry for final simplicity.
Ok, I am a beginner and working on an ALREADY EXISTING ASP.Net MVC 5 web app.
Overview of WebApp:
This web app is more like work press and dynamically creates different sort of widgets and functionalities as per the client demand. So multiple URL's hits this web app and they all are treated as a separate web site. All the widgets have their own url's. these Url's are linked into different clients and all that fun stuff.(HUMOR me on this).
Issue: Issue might be totally unrelated to what I explained above. How to correctly use Remote Validation attribute in ASP.Net MVC 5. Below is code snippet and issue. I just have one Email Id field and I want to apply Remote validation to it.
Model
public class ForgotPasswordMV
{
[Display(Name = "Email"), Required]
[Remote("IsEmailUnique", "Settings", ErrorMessage = "Somethin Wrong")]
public string EmailId { get; set; }
}
Controller Function
Public JsonResult IsEmailUnique(string EmailId)
{
var respObj = SecurityHelper.EmailCount(EmailId); // if count > 1 returns false
return Json(respObj, JsonRequestBehavior.AllowGet);
}
View File
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
<h2>Forgot Password</h2>
#using (#Html.BeginForm("ForgotPassword", "Security"))
{
#Html.ValidationSummary(true, "Please fix below error")
#Html.AntiForgeryToken()
<div class="form-group">
#Html.LabelFor(m => m.EmailId)
#Html.TextBoxFor(m => m.EmailId, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.EmailId)
</div>
<button class="btn btn-primary">Continue</button>
}
So, for the FIRST TIME when user enters email id and if return type is false then ErrorMessage = "Somethin Wrong" gets displayed properly. But the problem starts from second time. Now everytime I type the letters in the email field to change it. On every keyboard click the IsEmailUnique function starts getting hit. I mean when I see the ERROR MESSAGE for the FIRST TIME after seeing the error message I now went to the UI text box and started giving some other email id but now for every letter I put the remote validation method is getting hit. Ideally it should happen when I am tap out of the text box or click on the sbumit button (the way it worked for the first time)
EDIT
INSPECT ELEMENT CODE FOR THE UI TEXTBOX
<div class="form-group">
<label for="EmailId">Email</label>
<input class="form-control input-validation-error" data-val="true" data-val-remote="Something went wrong" data-val-remote-additionalfields="*.EmailId" data-val-remote-url="/Settings/IsEmailUnique" data-val-required="The Email field is required." id="EmailId" name="EmailId" type="text" value="" aria-required="true" aria-invalid="true" aria-describedby="EmailId-error">
<span class="field-validation-error" data-valmsg-for="EmailId" data-valmsg-replace="true"><span id="EmailId-error" class="">Something went wrong</span></span>
</div>
Further More
During second attempt (after successfully seeing Remote validation error) when I try adding any letter in Email ID Field I am noticing that below chunk of code gets hit and then the validation method IsEmailUnique is getting hit.
public class SimRoute : Route
{
/// <summary>
/// Base c-tor, implementing the base class ctor
/// </summary>
public SimRoute(string url, IRouteHandler routeHandler) : base(url, routeHandler) { }
/// <summary>
/// This is where the "magic" happens - this is where the subdomain is retrieved & inserted into route values, so it can be referenced at a later time
///
/// The rest of the functionality is being retained by running the call to the base class
/// </summary>
public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData routeData = base.GetRouteData(httpContext);
if (routeData != null)
{
string[] temp = httpContext.Request.Url.Host.Split('.');
if (temp.Length > 0)
{
routeData.Values.Add("subdomain", temp.First());
}
}
return routeData;
}
}
CALL STACK
To be honest, I am not sure what this above code snippet does, but why this code is getting hit as soon as I type any letter in the EmailID text box, or this code is even remotely related to the issue. My original question is, why Remote validation is happening on every letter put in textbox. I have tried to put as many details as I can in the question. Please guide me.
I would advise you to try removing the onkeypress event from the field/form like explained in the following q&a
ASP.NET Remote Validation only on blur?
So what I have is a HTML-Form enabling the user to register for sportsevents. The user can register different profiles (e.g. his children) and every event can potentially have so called "Additional Attributes" like textboxes for T-Shirt-size etc.
#model Models.EventRegistrationModel
#{
Layout = null;
var playerCount = Model.PlayersToRegister.Count;
}
#using (Html.BeginForm("RegisterForEvent", "Event", FormMethod.Post)){
#Html.AntiForgeryToken()
<div class="form-group">
#for (int i = 0; i < playerCount; i++)
{
<div>
<p>#Model.PlayersToRegister[i].User.FullName</p>
</div>
<div
#Html.CheckBoxFor(model => Model.PlayersToRegister[i].PlayerShallGetRegistered)
</div>
//this is the "Additional Attributes"-section for each user-profile
#Html.Raw(Model.PlayersToRegister[i].Form.RenderHtml())
}
</div>
<input type="submit" value="Confirm Registration"/>
}
Since I do not create those events, I cannot know, what these "Additional Attributes" look like, which is why they are rendered dynamically using DynamicForm.
My problem is that I cannot access the user-input for those attributes in the controller. When I check the browser's console, I see the input being posted, but checking the dynamic form's value, it always says "null".
Here's my controller:
[HttpPost, ValidateAntiForgeryToken]
public ActionResult RegisterForEvent(EventRegistrationModel model)
{
for (int i = 0; i < playerList.Count; i++)
{
var form = Session["Form" + i] as Form;
model.PlayersToRegister[i].Form = form;
//var test = form
//var testtest = test.GetResponses(false);
}
return RedirectToAction("EventOverview");
}
As you can see, I tried to use the Form's "GetResponses"-Method, but it returned null.
public List<Response> GetResponses(bool completedOnly)
{
return InputFields.OrderBy(inputField => inputField.DisplayOrder).Select(field => new Response
{
Title = field.Title, Value = field.Response
}).Where(response => !completedOnly || !String.IsNullOrEmpty(response.Value)).ToList();
}
At the moment I am trying to get the values via Session, as this worked in an older version, where you were only able to register one profile at a time. The Session-variable gets assigned in the ActionResult returning the above View.
I've been trying various solutions from various threads over the past days (e.g. ModelState.Clear), but unfortunately nothing has been successful.
If you need more information, code or whatever, please let me know.
Since your form is dynamic you may want to use a dynamic model in the post method. Try something like this:
[HttpPost, ValidateAntiForgeryToken]
public ActionResult RegisterForEvent(FormCollection collection)
{
// stuff....
}
You'll have to do a bit of work parsing out the collection that comes in but it should have everything that was posted from the form. In general I don't recommend this as it can really spiral out of control. Having a well defined view model (as you did in the original posting) is much better in general. However, sometimes you really need something dynamic and this gets the job done.
I am using EF in an ASP.Net MVC application and also using WebApi to get an element like so:
[HttpGet]
public Student GetStudent(int id)
{
return db.Students.AsNoTracking().FirstOrDefault(n => n.Student_ID == id);
}
This all works great and I map it directly into ko for use. However when I bind the value of any DateTime properties of the element i.e. date of birth I get: 1955-04-17T11:13:56. I don't want to change the web api methods or the model for every date, is there a 3rd party library or a knockout function that can handle a datetime from asp.net and correct the value for inputs.
As Sridhar suggested in the comment. You can use moment.js to achieve this.
I've created a sample fiddle here - http://jsfiddle.net/sherin81/ordwenj6/
Knockout code
function viewModel()
{
var self = this;
self.dateInput = ko.observable("1955-04-17T11:13:56");
self.formattedDate = ko.computed(function(){
var m = moment(self.dateInput());
return m.format("DD-MM-YYYY");
});
}
ko.applyBindings(new viewModel());
HTML
<input data-bind="value : dateInput" />
<br/>
<span data-bind="text : formattedDate()" />
For demonstration, I've used the value from the input field for formatting. You can modify the code to use the value from the webapi and format it using moment.js.
Update
To do the same using ko custom binding handler, do the following
ko.bindingHandlers.dateFormattedByMoment = {
update: function (element, valueAccessor, allBindingsAccessor) {
$(element).val(moment(valueAccessor()).format("DD-MM-YYYY"));
}
};
HTML
<input id="customBinding" data-bind="dateFormattedByMoment : dateInput()" />
Working fiddle here - http://jsfiddle.net/sherin81/ujh2cg73/