I know this kind of issue has been solved many times already, however I am unable to get mine fixed based on solutions provided.
I am building a simple library application. There is a feature to add a copy of a book, which uses jQuery to invoke controller actions and return partial views which are then added dynamically to the DOM.
The last dynamically element added is a form with additional details of a created copy. The ajax call is being triggered when a value of a DropDownList (#AuthorBooksDropDown) (also added dynamically) changes.
$('#authorBooksPlaceHolder').on('change', '#AuthorBooksDropDown', function () {
var bookId = $(this).val();
$.get('/Books/AddCopy_RenderDetails/' + bookId, function (data) {
$('#bookDetailsPlaceHolder').html(data);
$('#bookDetailsPlaceHolder').slideDown();
});
$.validator.unobtrusive.parse('#addCopyForm');
});
The call invoked the AddCopy_RenderDetails action get an entity from a DB based on book id, and creates a new copy with certain fields populated.
Controller action:
public PartialViewResult AddCopy_RenderDetails(int id)
{
var book = db.LibraryBooks.Find(id);
var newCopy = new Book()
{
Author = book.Author,
Title = book.Title,
Publisher = book.Publisher,
CollectionId = book.CollectionId,
Collection = book.Collection
};
return PartialView("_AddCopy_Details", newCopy);
}
The view displays remaining fields which need to be populated.
#model CityLibrary.Models.Library.Book
<div class="vertical-separator"></div>
<hr />
#using (Ajax.BeginForm("AddCopy", "Books", new AjaxOptions
{
UpdateTargetId = "bookDetailsPlaceHolder"
}, new { #id = "addCopyForm" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.Author)
#Html.HiddenFor(model => model.Title)
#Html.HiddenFor(model => model.CollectionId)
#Html.HiddenFor(model => model.Collection.Name)
#Html.HiddenFor(model => model.Publisher)
<div class="form-group">
#Html.LabelFor(model => model.Collection.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Collection.Name, new { htmlAttributes = new { #class = "form-control", #disabled = "" } })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ISBN, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ISBN, new { htmlAttributes = new { #class = "form-control", } })
#Html.ValidationMessageFor(model => model.ISBN, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Publisher, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Publisher, new { htmlAttributes = new { #class = "form-control", #disabled = "disabled" } })
#Html.ValidationMessageFor(model => model.Publisher, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.YearPrinted, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.YearPrinted, new { htmlAttributes = new { #class = "form-control", #Value = "" } })
#Html.ValidationMessageFor(model => model.YearPrinted, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-success btn-block" />
</div>
</div>
}
Even though I have $.validator.unobtrusive.parse('#addCopyForm'); invoked when the form is rendered (checked in chrome dev tools), validation still happens on the server side upon pressing a submit button as a POST action is being triggered every time. Not to mention that validation errors do not display upon TABing to next field.
Validation attributes are there in form's inputs:
I also have remote validation which checks whether a entered ISBN is already in the database. Obviously this works on the client side, which in my case simply does not.
Thank you for your time and help.
EDIT:
Well, I've added the following to the end of the view:
<script>
$.validator.unobtrusive.parse('#addCopyForm');
</script>
And it works. I have no idea why triggering it on a function does nothing.
Ajax is async, and your $.validator.unobtrusive.parse('#addCopyForm'); line of code is being called before the html has been added to the DOM. Move it to inside the success callback
$.get('/Books/AddCopy_RenderDetails/' + bookId, function (data) {
$('#bookDetailsPlaceHolder').html(data);
$('#bookDetailsPlaceHolder').slideDown();
$.validator.unobtrusive.parse('#addCopyForm');
});
Try this peace of code
$("form").on("submit", function (e) {
e.preventDefault();
$.validator.unobtrusive.parse($('#addCopyForm')); // here you need define your form id
if ($(this).valid()) // use to validate the form
{
//do ajax call
$.ajax({
type: "Post",
url: "/Books/AddCopy_RenderDetails/" + bookId,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
}
});
}
});
Related
I am developing a project in MVC 5. There is are some form input field where I need to put custom client side validation using jquery/javascript. The expected behaviour is for example when someone tries to type alphabets or special characters in the phone input, there should be a validation error displayed below the field or atleast an alert box should get triggered containing the error. I have added the custom validation file in my scripts folder. I can see some basic logs I wrote on the console of the page. I am facing challenges on the js function where we can capture the id of the field and provide custom logic for it. Here is my code. Please suggest what can be done.
#model StudentMVCApp.Models.registration
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm(new
{
#id = "registerFormId",
#class = "form-horizontal",
role = "form"
}))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Register a new student</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.firstname, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.firstname, new { htmlAttributes = new { #class = "form-control", data_val = "true", data_val_required = "Please dont provide empty name!" } })
#Html.ValidationMessageFor(model => model.firstname, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.lastname, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.lastname, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.lastname, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.phone, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.phone, new { htmlAttributes = new { #class = "form-control",#id="phoneid" } })
#Html.ValidationMessageFor(model => model.phone, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.email, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/Unobtrusive")
#Scripts.Render("~/CustomValidation")
}
Here is my Custom JavaScript
console.log("I am working JS");
(function ($) {
console.log("This function is captured");
var phone = $("#phoneid").val();
console.log(phone);
if (phone != null)
console.log(phone);
else if (phone == 100)
alert($(this).val());
else if (phone == "abc")
alert(phone);
else if (phone == abc)
alert(phone)
})(jQuery);
I tried different tutorials and experimented with some js functions. But couldn't get it working using id of the field.
I would recommend using jquery.validation.unobstrusive package scripts on your view:
<script src="~/Scripts/Jquery/jquery-1.9.1.js"></script>
<script src="~/Scripts/Jquery/jquery.validate.js"></script>
<script src="~/Scripts/Jquery/jquery.validate.unobtrusive.js"></script>
and adding your own custom validation on a form element by writing jquery such as:
$("#myinput").rules("add", {
required: true,
minlength: 2,
messages: {
required: "Required input",
minlength: jQuery.validator.format("Field needs to be more than{0}")
}
});
More info can be found at https://jqueryvalidation.org/rules/
You need to attach your code above to an event, input or submit for example.
Now it only runs once, when the page loads
something like
$(function() {
console.log("This function is captured");
const $phone = $('#phoneid');
$phone.on('input',function() {
const val = $(this).val(); // get the value - it is a string
console.log(val);
if (val == 100) console.log('100'); // note the == coerces the numeric string to number
else if (phone == 'abc') console.log('abc');
});
});
This question already has answers here:
How to append whole set of model to formdata and obtain it in MVC
(4 answers)
Closed 4 years ago.
I use a code like this to receive customer information.
Along with my customer information, I must upload a file as well.
CSHTML File
#using (Html.BeginForm("AddPersonInfo", "Management", FormMethod.Post, new { enctype = "multipart/form-data", id = "frmAddPerson" })) { #Html.AntiForgeryToken()
<div id="divControls" class="form inline">
<div class="form-group">
#Html.LabelFor(model => model.FName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-12">
#Html.EditorFor(model => model.FName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LName, htmlAttributes: new { #class = "control-label col-md-2"})
<div class="col-md-12">
#Html.EditorFor(model => model.LName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FileName, htmlAttributes: new { #class = "control-label col-md-2", style = "float: right" })
<div class="col-md-12">
<input id="postedFile" type="file" data-buttonText="Select File">
#Html.ValidationMessageFor(model => model.FileName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<input style="float: right" id="btnSave" type="button" value="Save" class="btn btn-info" />
</div>
</div>
</div>
}
Java Script File
<script>
$(document).ready(function () {
$("#btnSave").click(function (e) {
e.preventDefault();
if ($("#frmAddPerson").valid()) {
var formData = new FormData();
var file = document.getElementById("postedFile").files[0];
formData.append("postedFile", file);
$.ajax({
url: "/Management/AddPersonInfo",
data: formData,
type: "Post",
dataType: "Json",
contentType: false,
processData: false,
success: function (result) {
alert("success")
},
error: function (result) {
alert("error");
}
});
}
});
});
</script>
Controller File
public ActionResult AddPersonInfo(PersonViewModel model, HttpPostedFileBase postedFile)
{
try
{
//
}
catch
{
//
}
}
In the AddPersonInfo in Controller, the file information (postedFile) will be received correctly But the form information (model) will be received null
In the ajax request, you are only adding the postedFile to your FormData()
var formData = new FormData();
var file = document.getElementById("postedFile").files[0];
formData.append("postedFile", file);
Try adding the model by hand,
var formData = new FormData();
var file = document.getElementById("postedFile").files[0];
formData.append("postedFile", file);
var model: {
'FName' : 'John',
'LName' : 'Smith'
}
formData.append('model', model);
I am trying to Post a Partial View's data to the server to save the input fields result to database and then return back to the same (partial view which is open as a modal dialog) or return to the parent view which called the modal dialog.
But for some reason, I am not getting the Action method's parameter (i.e. weldMaster) in the Controller which is being sent from the ajax method call.
Here is my controller method.
// GET: WeldMasters/Details/5
public ActionResult Details(int? ids, bool isPartials = false)
{
if (ids == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
WeldMaster weldMaster = db.WeldMasters.Find(ids);
ViewBag.Readonly = false;
if (weldMaster == null)
{
return HttpNotFound();
}
if(isPartials)
return PartialView(weldMaster);
else
return View(weldMaster);
}
// POST: WeldMasters/Details/5
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult Details**(WeldMaster weldMaster)**
//{"The parameter conversion from type 'System.String' to type 'IMCC_PAS.Entities.WeldMaster' failed because no type converter can convert between these types."}
{
var success = 0;
if (ModelState.IsValid)
{
success = 1;
db.Entry(weldMaster).State = EntityState.Modified;
db.SaveChanges();
return Json(new { success });
}
return Json(new { success });
}
Here is the ajax code
<script type="text/javascript">
$(document).ready(function () {
$(".saveBtn").click(function (e) {
debugger;
var token = $('input[name="__RequestVerificationToken"]').val();
// If I dont pass the token in the `data` parameter of ajax, then action method of controller is not called at all.
e.preventDefault();
$.ajax({
type: "POST",
url: '#Url.Action("Details", "WeldMasters")',
data: { __RequestVerificationToken: token, weldMaster: $('#__AjaxAntiForgeryForm').serialize() },
success: function (result) {
alert(result);
},
failure: function (response) {
alert(result.responseText);
},
error: function (response) {
alert(result.responseText);
}
});
});
});
</script>
Here is the modal for the partial view
#model IMCC_PAS.Entities.WeldMaster
and here is the form
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "__AjaxAntiForgeryForm" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.id)
<div class="row">
<div class="col-md-3 col-sm-12">
#Html.LabelFor(model => model.weld_no, htmlAttributes: new { #class = "control-label" })
</div>
<div class="col-md-3 col-sm-12">
#Html.LabelFor(model => model.rep_no, htmlAttributes: new { #class = "control-label" })
</div>
<div class="col-md-3 col-sm-12">
#Html.LabelFor(model => model.length, htmlAttributes: new { #class = "control-label" })
</div>
<div class="col-md-3 col-sm-12">
#Html.LabelFor(model => model.normal_size, htmlAttributes: new { #class = "control-label" })
</div>
</div>
<div class="row">
<div class="col-md-3 col-sm-12">
#Html.EditorFor(model => model.weld_no, ViewBag.Readonly ? (object)new { htmlAttributes = new { #readonly = "readonly", #class = "form-control" } } : new { htmlAttributes = new { #class = "form-control" } })
</div>
<div class="col-md-3 col-sm-12">
#Html.EditorFor(model => model.rep_no, ViewBag.Readonly ? (object)new { htmlAttributes = new { #readonly = "readonly", #class = "form-control" } } : new { htmlAttributes = new { #class = "form-control" } })
</div>
<div class="col-md-3 col-sm-12">
#Html.EditorFor(model => model.length, ViewBag.Readonly ? (object)new { htmlAttributes = new { #readonly = "readonly", #class = "form-control" } } : new { htmlAttributes = new { #class = "form-control" } })
</div>
<div class="col-md-3 col-sm-12">
#Html.EditorFor(model => model.normal_size, ViewBag.Readonly ? (object)new { htmlAttributes = new { #readonly = "readonly", #class = "form-control" } } : new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group modal-footer">
<div class="col-md-12">
<a class="saveBtn" href="javascript:void(0);">Edit</a>
</div>
</div>
</div>
}
EDIT: Here is the parent view which has the selection parameters for showing the weld details. And then Weld details are shown in a div (for readonly purpose) and can be edited in a popup modal dialog on a button click.
<script>
$(document).ready(function () {
... more code for other dropdowns here
//When Wled No is changed, reload Weld Details in its respective Partial view
$("#weldddl").change(function () {
var parameter = { ids: $("#weldddl").val(), isPartials: true };
$.ajax({
url: '/WeldMasters/Details/',
type: 'GET',
data: parameter,
success: function (result) {
var div = $('#weldDetails');
div.html('');
div.html(result);
}
});
});
$(".editWeldBtn").click(function () {
//debugger;
$('#MyModal').empty();
$('#MyModal').append(GetModalDialog());
var link = '#Url.Action("Details", "WeldMasters")';
data = { ids: $("#weldddl").val(), isPartials: true };
LaunchModalDlg(link, data, "View Weld Master Information", "75%");
});
});
</script>
<h4>Index</h4>
<script src="~/Scripts/MyScripts.js"></script>
<p>
#Html.ActionLink("Create New", "Create") |
<a class="editWeldBtn" href="javascript:void(0);">Edit</a>
</p>
<div class="row">
<div class="col-sm-3">
#Html.LabelFor(model => model.Discipline, htmlAttributes: new { #class = "control-label col-md-12" })
<div class="col-md-12">
#Html.DropDownListFor(model => model.Discipline, (SelectList)ViewBag.disciplines, htmlAttributes: new { #class = "form-control", #id = "disciplineddl" })
#Html.ValidationMessageFor(model => model.Discipline, "", new { #class = "text-danger" })
</div>
#Html.LabelFor(model => model.DrawingNo, htmlAttributes: new { #class = "control-label col-md-12" })
<div class="col-md-12">
#Html.DropDownListFor(model => model.DrawingNo, (SelectList)ViewBag.drawings, htmlAttributes: new { #class = "form-control", #id = "drawingddl" })
#Html.ValidationMessageFor(model => model.DrawingNo, "", new { #class = "text-danger" })
</div>
#Html.LabelFor(model => model.ComponentNo, htmlAttributes: new { #class = "control-label col-md-12" })
<div class="col-md-12">
#Html.DropDownListFor(model => model.ComponentNo, (SelectList)ViewBag.components, htmlAttributes: new { #class = "form-control", #id = "componentddl" })
#Html.ValidationMessageFor(model => model.ComponentNo, "", new { #class = "text-danger" })
</div>
#Html.LabelFor(model => model.WeldNo, htmlAttributes: new { #class = "control-label col-md-12" })
<div class="col-md-12">
#Html.DropDownListFor(model => model.WeldNo, (SelectList)ViewBag.components, htmlAttributes: new { #class = "form-control", #id = "weldddl" })
#Html.ValidationMessageFor(model => model.WeldNo, "", new { #class = "text-danger" })
</div>
</div>
<div class="col-sm-9" id="weldDetails"> <!--This is for showing the partial view for showing weld details-->
</div>
<div id="MyModal"></div> <!--This is for pop up modal dialog-->
</div>
If you are serializing the full form then you don't need to send explicitly anti forgery token.
use this:
data: $('#__AjaxAntiForgeryForm').serialize(),
I have a form in my webpage that asks for first and last name. When the user submits this form I would like the data to be passed to a function in my controller, where it is used to find the corresponding user in our Active Directory. I want to then return the matching Email address to a Javascript function, when then displays that information on my page. Is this the "correct" way to go about doing this, and if yes, how would I structure a function in my controller to accept form input, and return data to my client side javascript?
What I have so far in my controller:
public SearchResult[] searchAD(String fname, String lname)
{
Func<System.DirectoryServices.ActiveDirectory.Domain> domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain;
System.DirectoryServices.DirectoryEntry root = new DirectoryEntry("LDAP://"+domain);
DirectorySearcher searcher = new DirectorySearcher();
searcher.SearchRoot = root;
searcher.SearchScope = SearchScope.Subtree;
searcher.Filter = string.Format("(&(objectCategory=person)(objectClass=user)(givenName={0})(sn={1}))", fname, lname);
SearchResult[] results = new SearchResult['a'];
searcher.FindAll().CopyTo(results, 0);
return results;
}
And my form:
#using (Html.BeginForm("searchAD", "AD", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.firstName, "First Name", htmlAttributes: new { #class = "control-label" })
<div>
#Html.EditorFor(model => model.firstName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.firstName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.lastName, "Last Name", htmlAttributes: new { #class = "control-label" })
<div>
#Html.EditorFor(model => model.lastName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.lastName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div>
<input type="submit" value="Search" class="btn" />
</div>
</div>
}
You can Store value in TempData and get it on view
public ActionResult searchAD(String fname, String lname)
{
Func<System.DirectoryServices.ActiveDirectory.Domain> domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain;
System.DirectoryServices.DirectoryEntry root = new DirectoryEntry("LDAP://"+domain);
DirectorySearcher searcher = new DirectorySearcher();
searcher.SearchRoot = root;
searcher.SearchScope = SearchScope.Subtree;
searcher.Filter = string.Format("(&(objectCategory=person)(objectClass=user)(givenName={0})(sn={1}))", fname, lname);
SearchResult[] results = new SearchResult['a'];
searcher.FindAll().CopyTo(results, 0);
TempData["Result"]=results;
return View("ViewName");
}
In View (Jquery)
$(function(){
if ('#TempData["Result"]' != '')
{
// call your function here and pass TempData Value
}
});
Above is not perfect solution for your posted issue because tempdata or viewbag are not meant for that.If you need to post data again back from control to view then its always make use of #Ajax.BeginForm
#using (Ajax.BeginForm("ActionName", "Controller", new AjaxOptions { OnComplete = "Sucess" }))
in jquery you have to write Success function like below :
function Sucess(arg) {
//arg.data
}
in arg.data you will get that object what you are passing from controller.More ever #Html.BeginForm refreshing your whole page not specific content but with With Ajax.begin form you can manage to reload only those content which is under ajax.begin form.
Using MVC/Json/Jquery.
Using form to create a new "group".
Form is on ~Group/Manage, posting form to ~Group/Create
Whilst working on this, returning Json result was working fine, handling in Jquery, no URL redirection.
Now, everytime I run it, it redirects me to ~Group/Create and displays the Json result.
Controller Group/Create
[HttpPost]
public ActionResult Create([Bind(Include="name,description")] GroupModel groupmodel)
{
...
return Json(new { success = true, message = groupmodel.name }, JsonRequestBehavior.AllowGet);
}
Form
<form id="frm_createGroup" action="/Groups/Create" method="post">
<h2>Create Group</h2>
<div class="form-group">
#Html.LabelFor(model => model.name, new { #for = "name" })
#Html.TextBoxFor(model => model.name, new { #class = "form-control", #placeholder = "Group Name" })
#Html.ValidationMessageFor(model => model.name)
</div>
<div class="form-group">
#Html.LabelFor(model => model.description, new { #for = "description" })
#Html.TextBoxFor(model => model.description, new { #class = "form-control", #placeholder = "Group Description" })
#Html.ValidationMessageFor(model => model.description)
</div>
<span id="createGroupMessage"></span>
<button type="submit" class="btn btn-primary pull-right">Create</button>
</form>
Jquery to handle form
$(document).ready(function (){
$('#navGroups').makeActiveMenuItem();
var options = {
success: groupCreateSubmitted
,error: groupCreateError
}
$('#frm_createGroup').ajaxForm(options);
});
function groupCreateSubmitted(responseText, statusText, xhr, $form) {
if (responseText.success)
{
$('#createGroupMessage').html = "Group Created";
}
else
{
$('#createGroupMessage').html = responseText.message;
}
}
To be clear, I don't want URL redirection, I just want the Jquery to catch the return (it was before, have no idea why its changed...)
Thanks!
removed
,error: groupCreateError
working now...form bind was failing.