I have the following code:
#Html.EditorFor(model => model.AssociatedCard, new { htmlAttributes = new { #class = "form-control", #id="addCardBox", #onkeydown="test()"} })
Notice the #onkeydown="test()" attribute. This works fine, on keydown, a JS function is called.
But I want to send the key that was pressed, to the server, and save it to the database.
How can I send this key to the server, so that I can handle it in the C# code?
In asp.net, we can do something like this (which will allow me to bind to the TextBox):
<asp:TextBox id="txt" Runat="Server"></asp:TextBox>
At server side, I can then reach my textbox with the id=txt. Can I do something similar, without having to use ajax/JQuery?
Provided EditorFor is producing a textbox you can do the following as it will capture the input.
You appear to be on the right track given that you've created a strongly typed view, you just need to wrap your inputs in a form to post them to the server.
#using (Html.BeginForm("PostAction", "Home"))
{
//Put your foreach loop in here
}
Then in your controller
public class HomeController : Controller
{
[HttpPost]
public ActionResult PostAction(ViewModelType model)
{
//model.AssociatedCard should contain your value
}
}
Related
Our application (MVC5) has some very complex validation that needs to be done server side (compound capacity checks, workflow validation, and more). The problem we are running into is that once server side validation fails and returns to the same view, the client side never submits values for any fields again (0 for int, empty strings, etc.)
Our general pattern is as follows:
public ActionResult PerformSomeAction()
{
var model = GetActionTemplate();
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult PerformSomeAction([Bind(Include = ActionTemplate.FIELDS)] ItemTemplate template)
{
string errorMessage;
if (ModelState.IsValid)
{
bool isValid = ValidateAndPerformAction(template, out errorMessage)
if(isValid)
return RedirectToAction("Action", "Controller");
}
// Reset non-bound fields from new template
var model = GetActionTemplate();
template.FieldValue = model.FieldValue
return View(template);
}
Our views don't have anything special in them, other than the fact that some of our editors are built with the Telerik Kendo library. However, the symptoms are seen for all controls, not just Kendo based ones.
A basic View layout for editing a field is as follows:
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-9">
#Html.Kendo().TextBoxFor(model => model.Name).HtmlAttributes(new
{
title = ModelMetadata.FromLambdaExpression(model => model.Name, ViewData).Description
})
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
Does anyone have any suggestions on what we are doing wrong?
Note: While it could be argued that this validation could be triggered through AJAX or other service calls, we would prefer to do it with the post implementation that we are using.
Update:
After further research, it appears this has to do with Kendo and not MVC. If I switch my View to be the following:
#Html.EditorFor(model => model.Volume)
Instead of:
#Html.Kendo().NumericTextBoxFor(model => model.Volume).HtmlAttributes(new
{
#class = "",
title = ModelMetadata.FromLambdaExpression(model => model.PreBoilVolume, ViewData).Description
}).Value(Model.Volume)
Everything appears to work fine. So somewhere in that Kendo statement it fails to rebind when returning from the post. It doesn't matter if I set the value again manually, it will never send it back in.
I guess it is time to get rid of some Kendo statements and go back to a more basic UI.
What you "want" to do is a very basic scenario for MVC with failed validation.
The first one that always catches people is with drop down lists. The posted model, does not contain the list of items, so has to be re-populated after failed validation, and then passed back into the view on return.
When I can't solve things like this I start commenting stuff out and work forwards. So in this case strip your model back to one non-ID property and see if it will work. Then you can try and track down the culprit.
It looks like the selectedvalues are not set on the model. Try change these lines of code:
var model = GetActionTemplate();
template.FieldValue = model.FieldValue
return View(template);
to something like this:
var model = GetActionTemplate();
model.Selectedvalues = template.FieldValue ?
return View(model);
So I am working on a MVC which is basically three steps.
Create a view for each step i.e.
StepOne
StepTwo
StepThree
On step one and two I ask the users to enter some details.
All the values for the multiple step I store in one Model.
And getting from StepOne to StepTwo is fine. Certain values in my model are being set and maintained.
But on StepTwo when I do my second httppost and pass the model, it seems to just create a new instance of the model and values from stepone are not maintained.
<% using (Html.BeginForm("StepTwo", "Home", FormMethod.Post, new { id = "restrictionForm" })) { %>
<%: Html.AntiForgeryToken() %>
<div id="wrapping" class="clearfix">
<h3>Postcode Restriction Type : </h3>
<%= Html.DropDownListFor(x => x.SelectedRestriction, Model.RestrictionTypes,"Select Restriction...", new { #class = "selmenu required" }) %>
<h3>Restriction Description : </h3>
<%= Html.TextBoxFor(m => m.RestrictionDescription, new { #class = "txtblock required" }) %>
</div>
<section id="buttons">
<input type="submit" value="Submit" id="submitBtn" />
</section>
And in my controller
On Page Load my Model is still intact and still maintains values from previous step.
[Authorize]
public ActionResult StepTwo(PostcodesModel model)
{
var summaryMessage = "";
model.SummaryMessage = summaryMessage;
model.RestrictionTypes = _Provider.GetRestrictionTypes();
return View(model);
}
But at the Httppost, the model has lost values and seems to have created new instance of model.
[Authorize]
[HttpPost]
[ActionName("StepTwo")]
[ValidateAntiForgeryToken]
public ActionResult StepTwoPost(PostcodesModel model)
{
return View(model);
}
Any idea how I can maintain model between Http Posts ?
It seems from your question that you believe models persist across requests. This is not true.
You either pass information to the view via your model from the controller, or submit values from your view to your controller and MVC handles this by binding html form inputs to your View Model.
If you want to persist your View Model across each step you need to take the values accepted and copy them into a new model (or directly inject it) when calling your new view.
Something like this (I just typed this up off my head so its not clean but should give you an idea):
[HttpGet]
public ActionResult StepOne()
{
var model = new MyNewModel();
return View(model);
}
/* NOTE THE MODEL PASSED BACK HERE IS NOT THE EXACT SAME OBJECT
AS THE ONE CREATED IN THE GET ACTION ABOVE, MODEL BINDING HAS OCCURRED
TO READ YOUR FORM INPUTS AND MATCH THEM TO A NEW MODEL WHICH IS EXPECTED */
[HttpPost]
public ActionResult StepOne(MyNewModel model)
{
if (ModelState.IsValid)
{
// Do something here
// pass model to new view
TempData["model"] = model;
return RedirectToAction("StepTwo");
}
return View(model);
}
[HttpGet]
public ActionResult StepTwo()
{
MyNewModel model;
if (TempData["model"] != null)
{
model = (MyNewModel) TempData["model"];
// make some changes if necessary
model.MyProperty = 2;
return View(model);
}
return RedirectToAction("StepOne");
}
I think you can also keep your model in Session ( per application ) or in a ViewState ( per page ).
Every time you make a post you upgrade the session. It's also optimal because on the client side you receive only a session identifier.
Some differences between Session and Viewstate:
Session is per application, while ViewState is per page
Session sends to the client side only a session identifier, while ViewState sends an ecrypted text
For love nor money I can't understand the magic mapping that ASP.NET MVC performs when I post values between pages.
I have a "wizard" consisting of 3 controller actions: NewRequest, NewRequestValidation and PostNewRequest. NewRequest collects some form input and passes it to NewRequestValidation where the user confirms their request and either submits it to PostNewRequest or rejects it and returns to NewRequest.
NewRequest uses:
#model SCL.Intranet.Controllers.HR.LeaveRequests.NewLeaveRequestViewModel
NewRequestValidation expects the NewLeaveRequestViewModel as a parameter on the method signature:
public ActionResult NewRequestValidation(NewLeaveRequestViewModel lrvm)
It then does some validation and passes a NewLeaveRequestAnalysisViewModel down to the corresponding View.
The NewRequestValidation view uses:
#model SCL.Intranet.Controllers.HR.LeaveRequests.NewLeaveRequestAnalysisViewModel
I am trying to post some parts of the NewLeaveRequestAnalysisViewModel, specifically the "NewLeaveRequestViewModel" part into PostNewRequest.
My form definition, including some hidden fields to transport the appropriate values, looks like:
#using (Html.BeginForm("PostNewRequest", "LeaveRequests", new { area = "HR" }, FormMethod.Post, new { #class = "form-horizontal" }))
{
#Html.HiddenFor(m => m.NewLeaveRequest)
#Html.HiddenFor(m => m.NewLeaveRequest.From.Date)
#Html.HiddenFor(m => m.NewLeaveRequest.From.Half)
#Html.HiddenFor(m => m.NewLeaveRequest.Until.Date)
#Html.HiddenFor(m => m.NewLeaveRequest.Until.Half)
#Html.HiddenFor(m => m.NewLeaveRequest.Type.LeaveRequestTypeId)
// Some presentation stuff here.
}
Finally, PostNewRequest expects a NewLeaveRequestViewModel:
public ActionResult PostNewRequest(NewLeaveRequestViewModel lrvm)
I understand that I need to pass a NewLeaveRequestViewModel in, and that my current code doesn't work because I'm actually just passing in a bunch of fields that happen to have their values from a previous instance of NewLeaveRequestViewModel.
The question is: How do I set up a new NewLeaveRequestViewModel in NewRequestValidation that will match the signature on PostNewRequest?
I fixed this by using TempData as suggested (I think!) by meda.
In the "NewRequestValidation" action, I commit the NewLeaveRequestViewModel to TempData:
TempData.Add("PendingLeaveRequest", lrvm);
Then, in PostNewRequest, I get it back out of TempData again:
if (TempData.ContainsKey("PendingLeaveRequest"))
{
var lrvm = TempData["PendingLeaveRequest"] as NewLeaveRequestViewModel;
// Do something with lrvm
}
I have a model that I am using in my view that is full of data. This data is then edited in the view. I need to figure out a way to resubmit this data back over to the controller.
Here is what I have so far.
VIEW:
#using (Html.BeginForm("DownloadCSV", "Respondents", FormMethod.Post))
{
#Html.HiddenFor(m => m.FilterSet)
<div class="btn btn-default pull-right" id="dispoCSV" onclick="$('#csvFormSubmit').click()">
<i class="icon-file-alt"></i> Disposition Report
</div>
<input id="csvFormSubmit" type="submit" style="display:none;" />
}
CONTROLLER:
[HttpPost]
public ActionResult DownloadCSV(RespondentsFilterSet model)
{
string csv = "Charlie, Test";
return File(new System.Text.UTF8Encoding().GetBytes(csv), "text/csv", "DispositionReport.csv");
}
MODEL:
public class RespondentsFilterSet : ColdListFilterSet
{
public List<int> OwningRecruiters { get; set; }
public List<int> RecruitingGroups { get; set; }
public override bool HasAtLeastOneFilter()
{
return base.HasAtLeastOneFilter() || OwningRecruiters.IsNotNullOrEmpty() || RecruitingGroups.IsNotNullOrEmpty();
}
public override ExpressionBase ToExpression()
{
var expr = base.ToExpression();
var expressions = expr == null ? new List<ExpressionBase>() : new List<ExpressionBase> { expr };
if (OwningRecruiters.IsNotNullOrEmpty())
{
expressions.Add(new InExpression<int> { Field = Create.Name<Respondent>(r => r.RecruitedBy), Values = OwningRecruiters });
}
if (RecruitingGroups.IsNotNullOrEmpty())
{
expressions.Add(new InExpression<int> { Field = Create.Name<Respondent>(r => r.RecruitingGroupId), Values = RecruitingGroups });
}
return expressions.Count == 0 ? null : BuildAndExpressionFromList(expressions);
}
}
I realize that my controller is not not finalized. I just have displaying some static csv. But I can't figure out why my model from my view is always null when returned to the controller.
Just look at your form. There's not a single input element (except the submit button). You cannot expect to get anything back on the server in this case.
Please read about HTML and how forms work in HTML. In HTML forms you have input fields. Things like text fields, hidden fields, checkboxes, radio buttons, ... - fields that the user interacts with get submitted to the server.
The fact that you have made your HttpPost controller action take some model as parameter doesn't mean at all that this parameter will be initialized. In ASP.NET MVC you have a default model binder. This model binder looks at what gets sent to the server as values when the form is submitted and uses the names of the fields to bind to the corresponding properties. Without input fields in the form, nothing gets sent to the server. Just use the debugging tools built into your web browser to inspect what exactly gets sent to the server.
Contrary to classic ASP.NET WebForms, ASP.NET MVC is stateless. There's no ViewState to remember your model.
So all this rambling is to say that you should read more about HTML forms first and understand the stateless nature of the web before getting into ASP.NET MVC. As far as your particular problem is concerned, well, assuming the user is not supposed to modify any values of the view model in your view throughout some input fields, you could simply include a hidden field containing the id of your model in the form. This id will then be sent to your POST controller action as parameter and you could use it to retrieve your original model from wherever it is stored (I guess a database or something).
I'm using ASP .NET MVC 3 with Data Annotations and the jQuery validate plugin.
Is there a way to mark that a certain field (or certain data annotation) should only be validated server-side?
I have a phone number field with a masking plugin on it, and the regular expression validator goes crazy on the user's end. The regex is only a fail-safe (in case someone decides to hack the javascript validation), so I don't need it to run on the client side. But I'd still like the other validation to run client side.
I'm not sure if this solution works on MVC3. It surely works on MVC4:
You can simply disable client side validation in the Razor view prior to render the field and re-enable client side validation after the field has been rendered.
Example:
<div class="editor-field">
#{ Html.EnableClientValidation(false); }
#Html.TextBoxFor(m => m.BatchId, new { #class = "k-textbox" })
#{ Html.EnableClientValidation(true); }
</div>
Here we disable client side validation for the BatchId field.
Also I have developed a little helper for this:
public static class YnnovaHtmlHelper
{
public static ClientSideValidationDisabler BeginDisableClientSideValidation(this HtmlHelper html)
{
return new ClientSideValidationDisabler(html);
}
}
public class ClientSideValidationDisabler : IDisposable
{
private HtmlHelper _html;
public ClientSideValidationDisabler(HtmlHelper html)
{
_html = html;
_html.EnableClientValidation(false);
}
public void Dispose()
{
_html.EnableClientValidation(true);
_html = null;
}
}
You will use it as follow:
<div class="editor-field">
#using (Html.BeginDisableClientSideValidation()) {
#Html.TextBoxFor(m => m.BatchId, new { #class = "k-textbox" })
}
</div>
If anyone has better solutions please let me know!
Hope this help.
You can switch off client-side unobtrusive validation for a single field by adding a data-val='false' attribute:
#Html.TextBoxFor(m => m.BatchId, new { data_val = "false" })
This will override the data-val='true' attribute that MVC adds due to any System.ComponentModel.DataAnnotations attributes. The HTML element will still be decorated with other validation attributes (e.g. data-val-required) but they won't have any effect.
(Note the underscore in data_val above. MVC automatically converts underscores to hyphens in anonymous type properties, so data_val becomes data-val when rendering the HTML)
MVC5 use jquery.validate
http://jqueryvalidation.org/rules/
If you want to remove validations in MVC5 client-Side you need to do the following:
Remove all validations on 'myinput'
$("#myinput").rules("remove");
Specific validations
$("#myinput").rules("remove", "min max" );
Listing the validations can help
$("#myinput").rules();
Then you will need to correct your Code Behind to validate manually your model or differently because ModelState.IsValid will be false. Using ModelState.Clear() and TryValidateModel can then be handy.
Edit:
Disabling the control also remove the validations.
$("#myinput").attr('disabled', disabledValue);
Assuming you use default unobtrusive validation, You could use some javascript to remove rules on client side. Take a look at Plugins/Validation/rules
To achieve this goal in the given scenario, we need to make two tweaks.
Client Side
To disable client side validation, we need to disable it by force.
#Html.EditorFor(model => model.Password, new { htmlAttributes = new { #data_val = "false" , #class = "form-control"} })
Notice the #data_val= “false”. It will disable the validation on this field.
Server Side (In Action)
When the model is validated on the post action, ModelState.IsValid will always return false because password is not provided. Here we have to provide the current password to the model and Re-validate the model.
var userObj = db.Users_Info.Where(a => a.Id == users_Info.Id).FirstOrDefault();
if (String.IsNullOrEmpty(users_Info.Password))
{
users_Info.Password = userObj.Password;
}
ModelState.Clear();
TryValidateModel(users_Info);
Let me explain, first we retrieve current information saved in the database which we are using later to assign to current model if password is not provided. The last two lines actually reset the ModelState to return updated result on ModelState.IsValid.
I ran into trouble with data_val="true". I had a sequence of radio buttons tied to a single property in my model. The data validation only worked when I applied data_val="true" to the first #Html.RadioButtonFor call.
In debugging this, I discovered you can also disable or alter individual rules on the client side by using data_rule_??. The rules can be found in the jquery validation documentation.
for example;
#Html.RadioButtonFor(m => m.Answer, "Yes", new { data_rule_Required = "false" });
#Html.TextBoxFor(m => m.Answer, new { data_rule_minlength = "10" }
If you want to remove validations in MVC5 client-Side you need to do the following:
$("#Email").rules("remove", {
"required",
"minlength",
"email"
}
});