RemoteAttribute is not passing parameter to action? - asp.net-mvc

I am trying to use the RemoteAttribute to validate a data element serverside using JSON.
My data field is:
[Display(Name = "My Number")]
[Required]
[Remote("IsValidMyNumber","Home",ErrorMessage="Bummer")]
public string MyNumber { get; set; }
My controller is:
public JsonResult IsValidMyNumber(string MyNumber)
{
var test = services.ValidateMyNumber(MyNumber);
return Json(test,JsonRequestBehavior.AllowGet);
}
My view is:
<div class="editor-field">
#Html.EditorFor(model => model.CheckInformation.MyNumber)
#Html.ValidationMessageFor(model => model.CheckInformation.MyNumber)
</div>
The HTML generated is:
<input class="text-box single-line" data-val="true" data-val-remote="Bummer"
data-val-remote-additionalfields="*.MyNumber" data-val-remote-url="/Home/IsValidMyNumber"
data-val-required="The Number field is required." id="CheckInformation_MyNumber"
name="CheckInformation.MyNumber" type="text" value="" />
When I debug and step inside my controller the "MyNumber" parameter is null even though I have text in the textbox that this represents.
I know that the name has to be the same in the textbox as in the parameter and I have validated that.
Any ideas?

Seems like the generated name of your input field is:
name="CheckInformation.MyNumber"
That's probably because your view model is a parent model of what you have shown in your question and you used something along the lines of:
#Html.TextBoxFor(x => x.CheckInformation.MyNumber)
So make sure you have specified this prefix or the default model binder will never be able to rehydrate the values:
public ActionResult IsValidMyNumber([Bind(Prefix = "CheckInformation")] string myNumber)
{
var test = services.ValidateMyNumber(myNumber);
return Json(test, JsonRequestBehavior.AllowGet);
}

You can also receive the value of myNumber to pass your view model in the action parameter, Like.
It works for me.
public ActionResult IsValidMyNumber(YourViewModel vm)
{
var test = services.ValidateMyNumber(vm.myNumber);
return Json(test, JsonRequestBehavior.AllowGet);
}

Related

Update a field in view based on the model in ASP.NET MVC

I need to do a calculator in ASP.NET MVC.
For the beginning I want to receive the value from the input field in controller and prefix it with the string "123". At the end I will process the expresion received and return the result.
I have the following model:
namespace CalculatorCloud.Models {
public class Calculator
{
public string nr { get; set; }
} }
In the view I am using the model:
#model CalculatorCloud.Models.Calculator
#{
ViewBag.Title = "Calculator";
}
#using (Html.BeginForm("Index", "Home"))
{
<div>
<div class="header">
#Html.TextBoxFor(m => m.nr, new { #id = "nr"})
<input type="button" id="C" name="C" value="C" />
<input type="button" id="back" name="back" value="<-" />
[...]
<div class="sum">
<input type="submit" value="=" />
</div>
</div>
}
The controller is like this:
namespace CalculatorCloud.Controllers
{
public class HomeController : Controller
{
Calculator model = new Calculator();
public ActionResult Index(string nr)
{
model.nr = "123" + nr;
return View(model);
}
}
}
I have the following problem: when pressing on submit button I am expecting to be displayed on the textbox the value from that was previously in the textbox, prefixed with the string "123".
But now it is kept the value from the textbox without the string "123".
Can someone help me with this?
Thank you! :)
If you want to modify the value of a model property in a postback action you will need to remove it from the ModelState:
public ActionResult Index(string nr)
{
ModelState.Remove("nr");
model.nr = "123" + nr;
return View(model);
}
The reason for this is that Html helpers such as TextBoxFor will first look at the value present in the ModelState and then in your view model property when rendering the value. This is by design.

List Binding with model data

So I have a form that I am trying to submit and I can get either the list or the model to bind, but not both at the same time. I suspect it has to do with the model binder.
HTML
#using (Html.BeginForm("Index", "Home", FormMethod.Post)){
#Html.AntiForgeryToken()
<div class="TransferHeader">
<div>
#Html.LabelFor(model => model.tranRequestedBy)
#Html.EditorFor(model => model.tranRequestedBy, new { #Name = "h.tranRequestedBy" })
</div>
<div>
#Html.LabelFor(model => model.tranNotes)
#Html.EditorFor(model => model.tranNotes, new { #Name = "h.tranNotes" })
</div>
<input name="h.TransfersDetail.Index" id="detIndex" type="hidden" value="c3a3f7dd-41bb-4b95-b2a6-ab5125868adb">
<input name="h.TransfersDetail[c3a3f7dd-41bb-4b95-b2a6-ab5125868adb].detToolCode" id="detToolCode" type="hidden" value="1234">
</div>
}
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(TransfersHeader h)
{
return View();
}
Model Class:
public virtual ICollection<TransfersDetail> TransfersDetail { get; set; }
public string tranRequestedBy { get; set; }
public string tranNotes { get; set; }
The two bottom inputs were generated from an AJAX call to an add method, what happens is if they are not present the two HTML helper editors will come in the model, but if they do exist only the transfer detail list will appear.
Is there anything I could do to make sure all of the data comes into the model?
Its not clear how you are generating those inputs, but the name attributes are incorrect. You model does not contain a collection property named h, but it does contain one named TransfersDetail, so your inputs need to be
<input name="TransfersDetail.Index" type="hidden" value="c3a3f7dd-41bb-4b95-b2a6-ab5125868adb">
<input name="TransfersDetail[c3a3f7dd-41bb-4b95-b2a6-ab5125868adb].detToolCode" type="hidden" value="1234">
Its also not clear why your adding an id attribute (if you referencing collection items in jQuery, you would be better off using class names and relative selectors), but the id your using does not have an indexer suggesting that your going to be generating duplicate id attributes which is invalid html (and jQuery selectors would not work in any case)

How To Pass Value Entered In A Text Box To An Action Method

I was building a Movies application using MVC. CRUD was automatically created for me by Visual Studio. Now, I am trying to build a Search functionality for the user. Here is the code I wrote:
#using (Html.BeginForm("SearchIndex", "Movies", new {searchString = ??? }))
{
<fieldset>
<legend>Search</legend>
<label>Title</label>
<input type ="text" id="srchTitle" />
<br /><br />
<input type ="submit" value="Search" />
</fieldset>
}
I have built the SearchIndex method and the associated view. I just can't find how to pass the value entered in the text box to the SearchIndex action method.
Please help.
In your Model:
public class Search
{
public String SearchText { get; set; }
}
Make your View strongly typed and use
#Html.EditorFor(model => model.SearchText)
In your Controller:
[HttpPost]
public ActionResult SearchIndex(Search model)
{
String text = model.SearchText;
}
Hope this helps.
You need to give your input field a name:
<input type="text" id="srchTitle" name="movieToFind" />
Then in your Controller make sure it has a string parameter:
in MoviesController:
[System.Web.Mvc.HttpPost]
public ActionResult SearchIndex(string movieToFind)
{
//Controller Action things.
}
Note: Form fields names must match the parameters expected in the controller. Or map to model properties if a 'Model' is expected.

Get dynamic radio buttons values

I'm doing survey system on asp.net mvc 4. How can i get all radio buttons data in Controller ?
Example
$(document).ready(function(){
var AnswerCounter = 0;
$("#answer-add").click(function(){
var answertext = $("#answer-text").val();
var answerContent = $('<p>', {
class:'external',
});
var inputAnswer = $('<input>',{
id:'answer-'+AnswerCounter,
name:'rb',
class:'answer-radio'
}).attr("type","radio").appendTo(answerContent);
var labelAnswer = $('<label>').attr('for','answer-'+AnswerCounter).text(answertext).appendTo(answerContent);
$('#answers').append(answerContent);
AnswerCounter++;
});
});
You can see in the example buttons created by jquery. so i can use model binding.
Thanks.
The HTML typically geneated for MVC3 radiobuttons look like this
<input name="FeedingTime" id="FeedingTime" type="radio" value="Morning"/>
<input name="FeedingTime" id="FeedingTime" type="radio" value="Afternoon" checked="checked"/>
and when the radio button group posts, it will bind to the variable that matches the name.
[HttpPost]
public ActionResult Index (string FeedingTime){
//feeding time here is "Afternoon"
}
so to make your code bind correctly,
set the value of the input 'value' attribute in your script, and
have a variable in the ActionResult that matches the 'name' attribute in the html input element
EDIT: This answer is no longer relevant to the question being asked.
to get the value from radio buttons you can bind to them like you would a string variable; suppose in your view you have
#Html.LabelFor(m => m.FeedingTime,"Morning")
#Html.RadioButtonFor(m => m.FeedingTime, "Morning")<br />
#Html.LabelFor(m => m.FeedingTime,"Afternoon")
#Html.RadioButtonFor(m => m.FeedingTime, "Afternoon")<br />
#Html.LabelFor(m => m.FeedingTime,"Night")
#Html.RadioButtonFor(m => m.FeedingTime, "Night")
when you post back, you would capture this in a string variable
public ActionResult(..., string FeedingTime, ...){
}
normally this variable is embedded in a viewmodel such as
public class AnimalViewModel
{
public string Name { get; set; }
public string Type { get; set; }
public string FavoriteColor { get; set; }
public string FeedingTime { get; set; }
}
so that when you post
[HttpPost]
public ActionResult Index(AnimalViewModel viewModel){
...
}
it binds the viewmodel to the data automagically.
To answer your question more directly. I dont think theres a way to detect all radiobutton inputs on a page, you can only detect the results from that are posted from the radio button. There is no indication that the string posted was once the answer to a radiobutton. So, if you want to know the values of all radiobuttons on the page, you will simply have to check the values of the strings which are posted.

MVC Model Binding to a collection where collection does not begin with a 0 index

I'm trying to perform remote validation on a property of an item within a collection. The validation works OK on the first item of the collection. The http request to the validation method looks like:
/Validation/IsImeiAvailable?ImeiGadgets[0].ImeiNumber=123456789012345
However on the 2nd item where the url looks like below, the validation doesn't work
/Validation/IsImeiAvailable?ImeiGadgets[1].ImeiNumber=123456789012345
Now I'm pretty sure the reason for this, is that binding wont work on a collection that doesn't begin with a zero index.
My validation method has a signature as below:
public JsonResult IsImeiAvailable([Bind(Prefix = "ImeiGadgets")] Models.ViewModels.ImeiGadget[] imeiGadget)
Because I'm passing an item within a collection I have to bind like this yet what I'm really passing is just a single value.
Is there anyway I can deal with this other than just binding it as a plain old query string.
Thanks
Edit: This is the quick fix to get the Imei variable but I'd rather use the model binding:
string imeiNumber = Request.Url.AbsoluteUri.Substring(Request.Url.AbsoluteUri.IndexOf("=")+1);
Edit: Here is my ImeiGadget class:
public class ImeiGadget
{
public int Id { get; set; }
[Remote("IsImeiAvailable", "Validation")]
[Required(ErrorMessage = "Please provide the IMEI Number for your Phone")]
[RegularExpression(#"(\D*\d){15,17}", ErrorMessage = "An IMEI number must contain between 15 & 17 digits")]
public string ImeiNumber { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
You could write a custom model binder:
public class ImeiNumberModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var modelName = bindingContext.ModelName;
var request = controllerContext.HttpContext.Request;
var paramName = request
.Params
.Keys
.Cast<string>()
.FirstOrDefault(
x => x.EndsWith(modelName, StringComparison.OrdinalIgnoreCase)
);
if (!string.IsNullOrEmpty(paramName))
{
return bindingContext
.ValueProvider
.GetValue(request[paramName])
.AttemptedValue;
}
return null;
}
}
and then apply it to the controller action:
public ActionResult IsImeiAvailable(
[ModelBinder(typeof(ImeiNumberModelBinder))] string imeiNumber
)
{
return Json(!string.IsNullOrEmpty(imeiNumber), JsonRequestBehavior.AllowGet);
}
Now the ImeiGadgets[xxx] part will be ignored from the query string.
If you are posting the whole collection, but have a nonsequential index, you could consider binding to a dictionary instead
http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
If you're only posting a single item or using a GET link, then you should amend to
/Validation/IsImeiAvailable?ImeiNumber=123456789012345
and
public JsonResult IsImeiAvailable(string imeiNumber)
If you are sending up a single value to the server for validation, then your Action Method should only accept a scalar (single-value) parameter, not a collection. Your URL would then look like this (assuming default routing table for {controller}/{action}/{id}:
/Validation/IsImeiAvailable?ImeiNumber=123456789012345
the corresponding action method signature could look like this:
/* note that the param name has to matchthe prop name being validated */
public ActionResult IsImeiAvailable(int ImeiNumber)
EDIT:
which you could then use to lookup whether that particular ID is available.
if you want to change the name of the parameter, you can modify the routing table, but that's a different topic.
The long story short of it is that if you wanted to do validate a collection of ImeiGadget, you'd GET or POST that full collection. For a single value, it doesn't make much sense to send up or to expect an entire collection.
UPDATE:
Based on new info, I would look at where the remote validation attribute is being placed. It sounds like it might be placed on something like an IEnumerable<IMEiGadgets>, like this:
[Remote("IsImeiAvailable", "Validation", "'ImeiNumber' is invalid"]
public IEnumerable<ImeiGadget> ImeiGadgets { get; set;}
Would it be possible to move that attribute and modify it to be on the ImeiGadget class instead, to be something like this?
[Remote("IsImeiAvailable", "Validation", "'ImeiNumber is invalid"]
public int ImeiNumber { get; set;}
In theory, you shouldn't have to change anything on your HTML templates or scripts to get this working if you also make the change suggested in my answer above. In theory.
Unless you need this binding feature in many places and you control the IsImeiAvailable validation method then I think creating a custom model binder is an over-head.
Why don't you try a simple solution like this,
// need little optimization?
public JsonResult IsImeiAvailable(string imeiNumber)
{
var qParam = Request.QueryString.Keys
.Cast<string>().FirstOrDefault(a => a.EndsWith("ImeiNumber"));
return Json(!string.IsNullOrEmpty(imeiNumber ?? Request.QueryString[qParam]), JsonRequestBehavior.AllowGet);
}
You can add an extra hidden field with .Index suffix to allow arbitrary indices.
View:
<form method="post" action="/Home/Create">
<input type="hidden" name="products.Index" value="cold" />
<input type="text" name="products[cold].Name" value="Beer" />
<input type="text" name="products[cold].Price" value="7.32" />
<input type="hidden" name="products.Index" value="123" />
<input type="text" name="products[123].Name" value="Chips" />
<input type="text" name="products[123].Price" value="2.23" />
<input type="hidden" name="products.Index" value="caliente" />
<input type="text" name="products[caliente].Name" value="Salsa" />
<input type="text" name="products[caliente].Price" value="1.23" />
<input type="submit" />
</form>
Model:
public class Product{
public string Name{get; set;}
public decimal Price{get; set;}
}
Controller:
public ActionResult Create(Product[] Products)
{
//do something..
}
For more information refer to : You've Been Haacked: Model Bindig To A List

Resources