Unobtrusive validation editorfor - asp.net-mvc-3

I've got a partial View, loaded from an Action, so the parent view contains:
#Html.Action("TourSearch")
The TourSearch View uses a editor as such:
#Html.EditorFor(model => model.ImpersonatedAgentModel, "ImpersonatedAgentView")
where ImpersonatedAgentModel is as such:
[Serializable]
public class ImpersonatedAgentModel
{
[Required(ErrorMessage = "Please provide a User ref")]
public string AgentImpersonatedUserName { get; set; }
[Required(ErrorMessage="Please provide a ABTA/AgencyCode")]
public string AgentImpersonatedBranchCode { get; set; }
[Required(ErrorMessage = "Please provide a User ref")]
public int? AgentImpersonatedBranchID { get; set; }
}
My editor is pretty straight forward:
#model Travel2.WebUI.Models.ImpersonatedAgentModel
<ul id="agencyDetails">
<li>
<label for="AgentImpersonatedBranchCode">ABTA/Agency Code: *</label>
#Html.TextBoxFor(model => model.AgentImpersonatedBranchCode, new {ID="txtBranchCode" })
#Html.ValidationMessageFor(model => model.AgentImpersonatedBranchCode, "*")
<input id="txtBranchId", type="hidden" value="#Model.AgentImpersonatedBranchID" />
<input id="txtUserName", type="hidden" value="#Model.AgentImpersonatedUserName" />
<input id="hidCurrentController" type="hidden" value='#ViewContext.Controller.ValueProvider.GetValue("controller").RawValue' />
</li>
<li>
<label for="AgentImpersonatedUserName">User ref: *</label>
<select id="ddlUser" disabled="disabled" ></select>
<input type="hidden" id="txtUserID" />
#Html.HiddenFor(model => model.AgentImpersonatedUserName, new {ID="AgentImpersonatedUserName" })
#Html.HiddenFor(model => model.AgentImpersonatedBranchID, new {ID="AgentImpersonatedBranchID"})
#Html.ValidationMessageFor(model => model.AgentImpersonatedUserName, "*")
</li>
</ul>
Now in Chrome, all works fine. But when I fill in the form in IE but not the data in the Editor, it passes validation incorrectly!
If I examine the markup, using IEs poor excuse for Firebug, I can see the validation attributes,
<input name="ImpersonatedAgentModel.AgentImpersonatedBranchCode" id="txtBranchCode" type="text" data-val="true" data-val-required="Please provide a ABTA/AgencyCode" jQuery172048066185567747205="94"/>
so why is IE ignoring them!! Stupid IE

Found the answer here:
http://www.tigraine.at/2011/08/26/jquery-validate-and-microsofts-unobtrusive-validation-dont-play-well-together/
We were referencing Jquery validation too and this seems to be causing this error

Related

Why has my unobtrusive ajax stopped updating my partial view in ASP.NET Core 3.1?

I'm using unobtrusive ajax inside a kendo window to update a form once it's been filled in. The form should update the Id's of the DOM elements dynamically and also show the relative data inside the form as returned from the controller.
Let's start, here is the window element that houses the partial view which is a form:
#model Requirement
#{
int localId = Model.Id;
}
<div class="window-content">
<form method="post" data-ajax-url="/Home/Process_Requirement" data-ajax="true" data-ajax-method="post" data-ajax-loading="#spinner" data-ajax-update="#update-form-requirement" data-ajax-success="form_requirement_success">
<div id="update-form-requirement">
<partial name="_Form_Requirement" />
</div><!--/update-panel-->
<div class="window-footer">
<div class="container">
<div class="row no-gutters">
<div class="col">
<button type="submit" class="input-submit float-right">Save</button>
</div>
</div>
</div>
</div>
</form>
</div>
_Form_Requirement
#model Requirement
#{
int localId = Model.Id;
}
<div class="tab-content">
<div class="tab-pane fade show active" id="form-requirement-basic-#localId" role="tabpanel" aria-labelledby="requirement-tab">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="text" id="Id_#localId" name="Id" asp-for="Id" readonly />
<input type="text" id="CreatedUser_#localId" asp-for="CreatedUser" readonly />
<div class="container">
<div class="row">
<div class="col">
<div class="form-group">
<label>Vessel Type</label>
<kendo-dropdownlist name="VesselType"
for="VesselType"
id="VesselType_#localId"
datatextfield="TypeName"
datavaluefield="Id"
min-length="3"
style="width: 100%"
value-primitive="true"
option-label="Select vessel type"
footer-template="<button class='dropdown-button k-icon k-i-plus-outline' data-object-title='Add vessel type' data-object-function='_Window_Vessel_Type' onclick='open_data_window(this, event)'></button>"
filter="FilterType.Contains">
<datasource type="DataSourceTagHelperType.Ajax" page-size="80">
<transport>
<read url="/Vessel/ReadVesselTypes" />
</transport>
</datasource>
<popup-animation>
<open duration="300" effects="fadeIn" />
<close duration="300" effects="fadeOut" />
</popup-animation>
</kendo-dropdownlist>
</div>
</div>
</div>
</div>
</div>
</div>
When the form is submitted the controller action does the following:
/Home/Process_Requirement
public IActionResult Process_Requirement(Requirement model)
{
//Define the partial view we want
string modal = "_Form_Requirement";
//If the Id is 0 then create a new requirement
if (model.Id == 0)
{
if (ModelState.IsValid)
{
try {
_requirementService.InsertRequirement(model);
var getData = _requirementService.GetRequirement(model.Id);
return PartialView(modal, getData);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
else
{
try
{
_requirementService.UpdateRequirement(model);
return PartialView(modal, model);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
return PartialView(modal);
}
So, when you first open a window to create a new requirement the localId is 0 because you're not editing one, that's expected behavior.
When you then fill in all the details and submit the form, the data is posted to the database and saved correctly but the model doesn't seem to update with the new data, for example the input for the Id remains as 0:
<input type="text" id="Id_#localId" name="Id" asp-for="Id" readonly />
yet the id for the input updates to the newly created id number. Why is this happening and can I do anything about it?
you have a bug. fix the last return
return PartialView(modal,model);
Is the Id primary key? If so, it is not recommend to change the primary key, you should add a new column to be the key.
the data is posted to the database and saved correctly but the model doesn't seem to update with the new data
This could be caused by the concurrency issues. I can't see your update code now, but you can
try to change your update code like this:
public async Task<IActionResult> Process_Requirement(Requirement model)
{
string modal = "_Form_Requirement";
if (model.Id == 0)
{
//....
}
else //to update
{
await TryUpdateModelAsync<Requirement>(_context.Requirements.FirstOrDefault(x => x.RId ==model.RId),"",c => c.Id, c => c.CreatedUser);
_context.SaveChanges();
return PartialView(modal, model);
}
return PartialView(modal);
}
My Model:
public class Requirement
{
[Key]
public int RId { get; set; } //new column
public int Id { get; set; }
public string CreatedUser { get; set; }
}
And in partial, pass the key with a hidden field:
<input type="text" asp-for="RId" hidden/>
<input type="text" id="Id_#localId" name="Id" asp-for="Id" />
<input type="text" id="CreatedUser_#localId" asp-for="CreatedUser" />
Result:

Using remote attribute in .net core razor pages. The Parameter does not get value in Action Method of Controller

I am using Remote Attribute validation
The method is invoked successfully on textchange event. However, the parameters in the action method of the controller does not get the value of the field.
Here is the Action Method in the HomeController.cs. When invoked the Name parameter remains null. I will be pleased if someone solve this problem
[AcceptVerbs("Get", "Post")]
public async Task<ActionResult> IsExist(string Name)
{
List<Keywords> keywords = new List<Keywords>();
HttpClient client = _api.Initial();
HttpResponseMessage res = await client.GetAsync("api/Keywords");
if (res.IsSuccessStatusCode)
{
var result = res.Content.ReadAsStringAsync().Result;
keywords = JsonConvert.DeserializeObject<List<Keywords>>(result);
}
if (keywords.FirstOrDefault(x => x.Name == Name) == null)
{
return Json(false);
}
else
{
return Json(true);
}}
Here is the Model
public partial class Keywords
{
public int Id { get; set; }
[Display(Name = "Name of Keyword")]
[Required]
[Remote(action: "IsExist",controller: "Home", ErrorMessage = "Keyword already present!")]
public string Name { get; set; }}
Here is the razor page in which I want to implement validation
#page
#model Consumer.Pages.Keyword.CreateModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Keywords</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Keywords.Name" class="control-label"></label>
<input asp-for="Keywords.Name" class="form-control" />
<span asp-validation-for="Keywords.Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Keywords.Department" class="control-label"></label>
<select asp-for="Keywords.DepartmentId" class="form-control" asp-items="ViewBag.Department"></select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I found the solution. It is to
Remove partial in the model class definition.
Replace in the Razor Page
<input asp-for="Keywords.Name" class="form-control" />
with
<input asp-for="Keywords.Name" name="Name" class="form-control" />
The [Remote] attribute is all but useless. There's a long-standing problem from the ASP.NET MVC days that migrated its way into ASP.NET Core unabated, as well. Namely, the action that handles the remote must take a param that matches the input name of what what's being validated. In other words, your action takes a param, name, but what's being sent to it is Keywords.Name, which cannot bind to name. You'd have to do something like:
public async Task<ActionResult> IsExist(Keywords keywords)
And then, then the value will be available via keywords.Name. This obviously makes the usage highly dependent on the particular implementation, so if there's a situation with a different input name like Foo.Keywords.Name, then you'd need an entirely different action, to match that binding, and basically duplicate the logic (though you could factor out the majority the logic into some common method all the actions could utilize).
Long and short, you're probably better off just handling this yourself manually. Just bind to the input event on the name input, and then call your method via AJAX. Then you can explicitly control the key that's sent in the request body, such that it will always match your action. Just note that you'll also want to debounce sending that AJAX request so that it only happens every few keystrokes or so. Otherwise, you're going to hammer your action.

Model Binding collections in ASP.NET MVC with hidden controls

Hihi,
Having trouble model binding with this class (the following concrete, not the abstract).
Ignore the basic properties, its the Lists that I'm interested in binding.
public abstract class MessageModel
{
public string Tag { get; set; }
public string Message { get; set; }
public int Id { get; set; }
public const int DefaultIdValue = Int32.MinValue;
public List<LinkModel> Linked { get; set; }
public List<LinkModel> NotLinked { get; set; }
protected MessageModel()
{
Id = DefaultIdValue;
Linked = new List<LinkModel>();
NotLinked = new List<LinkModel>();
}
protected MessageModel(string tag, string message):this()
{
Tag = tag;
Message = message;
}
}
public class TextModel:MessageModel
{
public int TextId { get; set; }
public TextModel()
{
TextId = DefaultIdValue;
}
}
This is the submission I get on the server side on submit (formatted for sanity):
Tag=
&Message=
&NotLinked.index=35fda83a053645e6809bbb8b0ea00103
&NotLinked.index=14c2e286e28b4c9d8f889fb3eb437e5f
&NotLinked.%5b35fda83a053645e6809bbb8b0ea00103%5d.RecipientId=1
&NotLinked.%5b35fda83a053645e6809bbb8b0ea00103%5d.RecipientName=Bob+Biggins
&NotLinked.%5b14c2e286e28b4c9d8f889fb3eb437e5f%5d.RecipientId=2
&NotLinked.%5b14c2e286e28b4c9d8f889fb3eb437e5f%5d.RecipientName=Billy+Oswold
&Submit=Submit
When the function is called that accepts that model the NotLinked collection is set to null. D:
The (relevant) output html looks like this (im trying to "faux" bind to:
<ol> and <li>
with jQuery doing the work of moving stuff around)
<div id="NotLinkedContainer">
<ol id="NotLinked" name="NotLinked" style="width: 500px;height: 200px">
<li value="1">Bob Biggins
<input id="NotLinked_index" name="NotLinked.index" type="hidden" value="a0ab331bee2a461084b686e13a87090b" />
<input id="NotLinked__a0ab331bee2a461084b686e13a87090b__RecipientId" name="NotLinked.[a0ab331bee2a461084b686e13a87090b].RecipientId" type="hidden" value="1" />
<input id="NotLinked__a0ab331bee2a461084b686e13a87090b__RecipientName" name="NotLinked.[a0ab331bee2a461084b686e13a87090b].RecipientName" type="hidden" value="Bob Biggins" />
</li>
<li value="2">Billy Oswold
<input id="NotLinked_index" name="NotLinked.index" type="hidden" value="d7d294d3174c4bd98d583e92010359e7" />
<input id="NotLinked__d7d294d3174c4bd98d583e92010359e7__RecipientId" name="NotLinked.[d7d294d3174c4bd98d583e92010359e7].RecipientId" type="hidden" value="2" />
<input id="NotLinked__d7d294d3174c4bd98d583e92010359e7__RecipientName" name="NotLinked.[d7d294d3174c4bd98d583e92010359e7].RecipientName" type="hidden" value="Billy Oswold" />
</li>
</ol>
</div>
Any ideas? Haven't done this sort of complex binding before so I'm at a loss at the simple mistake I've probably made.
Try removing the dot notation before the brackets and since this isn't a dictionary, but a list, you need to use indices, not keys. The correct syntax for a list in a razor view should look more like this:
<div id="NotLinkedContainer">
<ol id="NotLinked" name="NotLinked" style="width: 500px;height: 200px">
<li value="1">Bob Biggins
<input id="NotLinked_index" name="NotLinked.index" type="hidden" value="a0ab331bee2a461084b686e13a87090b" />
<input id="NotLinked__a0ab331bee2a461084b686e13a87090b__RecipientId" name="NotLinked[0].RecipientId" type="hidden" value="1" />
<input id="NotLinked__a0ab331bee2a461084b686e13a87090b__RecipientName" name="NotLinked[0].RecipientName" type="hidden" value="Bob Biggins" />
</li>
<li value="2">Billy Oswold
<input id="NotLinked_index" name="NotLinked.index" type="hidden" value="d7d294d3174c4bd98d583e92010359e7" />
<input id="NotLinked__d7d294d3174c4bd98d583e92010359e7__RecipientId" name="NotLinked[1].RecipientId" type="hidden" value="2" />
<input id="NotLinked__d7d294d3174c4bd98d583e92010359e7__RecipientName" name="NotLinked[1].RecipientName" type="hidden" value="Billy Oswold" />
</li>
</ol>
</div>
Here is an article that covers what you are attempting to do:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

Asp.Net MVC 3 not binding collections to models on posting

i have a object with many properties of which one is an array of some other complex object
something like this
class obj1
{
public string prop1 {get; set;}
public string prop2 {get; set;}
public obj2[] array {get; set;}
}
class obj2
{
SomeEnum Type{get; set;}
string Content{get; set;}
}
I have created an editor template for the array of obj2 lets name it obj2ArrayTemplate
which is like this
#for (int i = 0; i < Model.Length; i++)
{
#Html.EditorFor(model=>model[i],"obj2Template")
}
and an editor template for obj2 lets name It obj2Template
which is like this
<div class="editor-label">
#Html.DisplayFor(model=>model.Type,"SomeEnum",ViewData)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Content)
#Html.ValidationMessageFor(model => model.Content)
</div>
now because property Type if of type SomeEnum which is an enum and hence cannot be rendered directly by the Asp.Net MVC
I have created a Template for this too
which is something like this
<input type="text" value="#Model.ToString()" id="#ViewData.TemplateInfo.HtmlFieldPrefix" name="#ViewData.TemplateInfo.HtmlFieldPrefix"/>
the view is being rendered correctly and the HTML of the combined rendered view is
<div class="editor-field">
<input class="text-box single-line" id="array__0__Content" name="array.[0].Content" type="text" value="FBID" />
</div>
<div class="editor-label">
<input type="text" value="Blog" id="array.[1].Type" name="array.[1].Type"/>
</div>
<div class="editor-field">
<input class="text-box single-line" id="array__1__Content" name="array.[1].Content" type="text" value="SOme random blog" />
</div>
<div class="editor-label">
<input type="text" value="Twitter" id="array.[2].Type" name="array.[2].Type"/>
</div>
<div class="editor-field">
<input class="text-box single-line" id="array__2__Content" name="array.[2].Content" type="text" value="Twitter Profile" />
</div>
when iam posting back the form containing this html
chrome is showing me this posted data
prop1:val1
prop2:val2
array.[0].Type:Facebook
array.[0].Content:FBID
array.[1].Type:Blog
array.[1].Content:SOme random blog
array.[2].Type:Twitter
array.[2].Content:Twitter Profile
but still array field which is an array of obj2 in the model of type obj1 is null
what am I doing wrong?
Could crack that one :)
inspecting the request that was being posted from the server I found out that there is one .(dot) extra in the request so the array is not being populated
so instead of
array.[0].Type:Facebook
array.[0].Content:FBID
array.[1].Type:Blog
array.[1].Content:SOme random blog
array.[2].Type:Twitter
array.[2].Content:Twitter Profile
this should be posted back
array[0].Type:Facebook
array[0].Content:FBID
array[1].Type:Blog
array[1].Content:SOme random blog
array[2].Type:Twitter
array[2].Content:Twitter
and I found no apparent reason of why Asp.Net MVC framework is putting in that extra dot. when im kind of doing everything the MVC way
so I changed my code to include a little hack.
I added this like to the Editor template of obj 2
#{
ViewData.TemplateInfo.HtmlFieldPrefix = ViewData.TemplateInfo.HtmlFieldPrefix.Replace(".", "");
}
so now everything is running fine and smooth.

Posting collections using partial views and microsoft ajax in ASP.Net MVC2

I am using the partial view with the ajax.beginform. In that partial view page, i have the following markup
EDIT
<%
using (Ajax.BeginForm("ManageDataSources", "DataSources", saveAjaxOptions))
{
%>....
<td>
<%: Html.Hidden("DataSource_Id", dataSource.Id, new { #class = "DataSource_Id" })%>
<%: Html.TextBox("DataSource_Name", dataSource.Name, new { #class = "DataSource_Name" })%>
</td>
<tr class="queryParameters" style="display: block">
<td colspan="2" align="center">
<input id="Text1" name="parametername" type="text" />
<input id="Text2" name="parametervalue" type="text" />
<input id="Text3" name="parametername" type="text" />
<input id="Text4" name="parametervalue" type="text" />
<input id="Text5" name="parametername" type="text" />
<input id="Text6" name="parametervalue" type="text" />
<input id="Text7" name="parametername" type="text" />
<input id="Text8" name="parametervalue" type="text" />
<input id="Text9" name="parametername" type="text" />
<input id="Text10" name="parametervalue" type="text" />
</td>
</tr>
and in the the controller, i have this model for the representation of the data
public class DataSourceViewModel
{
public string DataSource_Id { get; set; }
public string DataSource_Name { get; set; }
public List<SCParams> parameters { get; set; }
}
public class SCParams
{
public string parametername { get; set; }
public string parametervalue { get; set; }
}
EDIT
public ActionResult ManageDataSources(DataSourceViewModel dsvm)
{
return PartialView("ManageDataSources");
}
when i post the data these parametername and parameter values are not at all bound to the list of objects. How do i do this. i am using microsoft ajax and want to do this without using other plugings. Kindly suggest the right way.
EDIT
This is the data in the header taken from chrome
DataSource_Id:
DataSource_Name:Name
parametername:a
parametervalue:1
parametername:q
parametervalue:2
parametername:z
parametervalue:3
parametername:s
parametervalue:4
parametername:w
parametervalue:5
x:15
y:12
What I understand you have master detail structure and you want to receive it controller. if that is the case. then there are two possibilities either your detail portion has variable length detail portion or fixed length detail portion. You may follow the post here for variable length as well as fixed length. For Fixed length you may also follow here.
You will receive the model in following signature
public ActionResult ManageDataSources(DataSourceViewModel dsvm)
moreover you may also check formcollection parameter for actionresult
[HttpPost]
public ActionResult MyAction(FormCollection collection)

Resources