Ajax Results Div Hidden Until Result - ajax

I have an Ajax form which is working properly:
<div class="row">
<div class="col-sm-8 col-sm-offset-2 alert alert-info" id="result"></div>
<div class="col-sm-8 col-sm-offset-2">
<h4>Contact Us</h4>
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "result" }))
{
#Html.LabelFor(a=>a.Email)
#Html.EditorFor(a => a.Email)
#Html.ValidationMessageFor(a => a.Email)
#Html.LabelFor(a=>a.Name)
#Html.EditorFor(a => a.Name)
#Html.ValidationMessageFor(a => a.Name)
#Html.LabelFor(a=>a.Phone)
#Html.EditorFor(a => a.Phone)
#Html.ValidationMessageFor(a => a.Phone)
#Html.LabelFor(a=>a.Message)
#Html.EditorFor(a => a.Message)
#Html.ValidationMessageFor(a => a.Message)
<input type="submit" class="btn btn-primary" value="Send Message" />
}
</div>
However, the results div, which I'd like to be styled as an alert, is always displaying by default. How can I make it display only when it has inner text? Is there something like #if(AjaxResult != null) { <div>...</div> }?
I suspect this can be solved within the View only, but I'm including my Controller code below for completeness:
[HttpPost]
public ActionResult Index(ContactUsViewModel model)
{
var fromAddress = new MailAddress("X", "X");
var toAddress = new MailAddress("X", "X");
string fromPassword = "X";
string subject = "GN Query";
string body = model.Message;
var smtp = new SmtpClient
{
Host = "smtp.gmail.com",
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
};
using (var message = new MailMessage(fromAddress, toAddress)
{
Subject = subject,
Body = body
})
{
smtp.Send(message);
}
return Content("Your message has been received and we will respond within 24-48 hours.", "text/html");
}

You can create a partial view call _alert with your html:
<div class="col-sm-8 col-sm-offset-2 alert alert-info" id="result">
Your message has been received and we will respond within 24-48 hours.
</div>
and then in your view you can set a container div in which it will be rendered when ajax call completes:
<div id="messageContainer">
</div>
now your action should return that partial view and you can have a boolean in your view to decide either render or not:
#model System.Boolean
#if(Model)
{
<div class="col-sm-8 col-sm-offset-2 alert alert-info" id="result">
Your message has been received and we will respond within 24-48 hours.
</div>
}
your controller action will decide either the operation was successful and show the message or not:
bool isSuccess = true; // your logic to set this flag
............
............
{
smtp.Send(message);
}
return PartialView("_alert.cshtml",isSuccess );
and in main view you will have :
<div id="messageContainer">
</div>
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "messageContainer" }))
Hope it helps.

Related

Ajax form not submitting to controller action

Ajax form is not submitting to controller action. Here is the code
#using (Ajax.BeginForm("searchCustomers", "Transaction", new { phoneNumber = Model.CustomerMobile }, new AjaxOptions
{
UpdateTargetId = "custList",
InsertionMode = InsertionMode.Replace
}))
{
<div class="col-md-6">
<div class="form-group">
<label>Customer Mobile No:</label>
#Html.TextBoxFor(x => x.CustomerMobile, new { #class = "form-control", id = "custMobile" })
</div>
#*<div class="form-group">
<label>Customer Name</label>
#Html.TextBoxFor(x => x.CustomerName, new { #class = "form-control", id = "custName" })
</div>*#
<input type="submit" class="btn btn-default" value="Get Customer Details" >
</div>
}
Here is the controller action
public ActionResult searchCustomers(string phoneNumber)
{
if (phoneNumber==null)
{
return PartialView(new List<Models.Customer>());
}
var c = Database.Session.Query<Models.Customer>()
.Where(x => x.MobileNumber.Like(phoneNumber) )
.ToList();
return PartialView(c);
}
but the ajax form is not submitting. I've added the JavaScript files as bundles. I've another #Html.Action("searchCustomers", new { phoneNumber = Model.CustomerMobile }) this one calls the controller action.
Everything is fine in your code. There are two javascript files that are needed for Ajax.Beginform to work.
jquery-{Vaersion}.js
jquery.unobtrusive-ajax.js
Check whether you have included those files to your view or not. Or if your view has any LayOut if those javascript files are included in your LayOut or not.

AJAX Model Validation with Partial View

I have a partial view, which is a login that functions as a popup. All I want to do is have my model do the validation (server side) and return any errors via AJAX. The code below returns the partial view only with the errors. I want my action result to not return a a view, but only the errors. In old ASP.NET, this would be a Partial Post back. I am not sure how to accomplish this in MVC.
Here is the Model
public class LoginModel
{
[Required]
public String Email { get; set; }
[Required]
[DataType(DataType.Password)]
public String Password { get; set; }
}
Here is the Partial View
#model MySite.Models.LoginModel
#using (Ajax.BeginForm("Authenticate", "Account", null, new AjaxOptions { OnFailure = "error" }, new { id = "LoginForm" }))
{
<div class="modal-body" id="LoginPopupDialogMessage">
The page you have requested requires you to login. Please enter your credentials and choose your country:
<br />
<br />
<div class="row">
<div class="form-group col-lg-offset-2 col-lg-8">
<label>Email Address</label>
#Html.TextBoxFor(u => u.Email, new { #class = "form-control input-lg input-sm", id = "Email", name = "Email" })
#Html.ValidationMessageFor(u => u.Email)
</div>
</div>
<div class="row">
<div class="form-group col-lg-offset-2 col-lg-8 ">
<label>Password</label>
#Html.PasswordFor(u => u.Password, new { #class = "form-control input-lg input-sm", name = "Password" })
#Html.ValidationMessageFor(u => u.Password)
</div>
</div>
<div style="text-align: center; padding-top: 20px;" class="ImageGroup">
<button name="companyCode" value="LB_US" class="btn-link" type="submit">
<img src="../../WebContent/Images/icon-flag-usa.png" />
</button>
<button name="companyCode" value="LB_CA" class="btn-link" type="submit">
<img src="../../WebContent/Images/icon-flag-canada.png" />
</button>
<button name="companyCode" value="LB_EU" class="btn-link" type="submit">
<img src="../../WebContent/Images/icon-flag-europe.png" />
</button>
</div>
</div>
}
I call the parial view from _layout.cshtml.
<div class="modal fade" id="LoginPopupDialog" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header" style="background: #e7e3e7; color:#000;">
<button type="button" class="close" data-dismiss="modal" aria-label="Close" style="color:#000;">
<span aria-hidden="true">×</span>
</button>
<div class="modal-title" id="LoginPopupDialogHeader">Please Login</div>
</div>
#Html.Action("Login", "Account")
</div>
</div>
</div>
My Controller Action:
[HttpPost]
[Route("account/authenticate")]
public ActionResult Authenticate(String companyCode, LoginModel model)
{
if (!ModelState.IsValid)
{
// ??
}
return PartialView("Login", model);
}
Since your code is doing an ajax form submission for the login, you should try to return a JSON response from the server. If model validation fails, you may read the validation errors from the model state dictionary and store that in a collection of strings (error messages) and return that as part of the json response. If model validation passes, you can continue executing your code to verify the login credentials and if those looks good, send back a json response with the next url for the user (to which we can redirect the user).
[HttpPost]
public ActionResult Authenticate(String companyCode, LoginModel model)
{
if (!ModelState.IsValid)
{
var errors = ViewData.ModelState.Values
.SelectMany(x => x.Errors.Select(c => c.ErrorMessage));
return Json(new { Status = "Error", Errors = errors });
}
//to do :Verify login, if good, return the below respose
var url=new UrlHelper(Request.RequestContext);
var newUrl = url.Action("About");
return Json(new { Status="Success", Url = newUrl});
}
Now in your view, you may specify a OnSuccess handler as part of the AjaxOptions. This will be a javascript object to which the json response from the server will come. We basicallly need to check the Status property value and do the appropriate things.
new AjaxOptions { OnFailure = "error" , OnSuccess="loginDone"}
The below implementation of loginDone simply alerts the error messages. You can update it to show it as part of the DOM.
function loginDone(d) {
if (d.Status === "Success") {
window.location.href = d.Url;
} else {
$.each(d.Errors,function(a, b) {
alert(b);
});
}
}
You may also consider enabling the unobtrusive client side validation which does the client side validation before trying to make a call to server. This will also show the error messages in the validation error spans (same as the normal mvc model validation does)

Ajax.BeginForm only displaying partial View instead of parent & partial view on return

I have a parent View & a child View.
When posing with the Ajax.BeginForm, I'm expecting back the entire parent view plus the results of the partial view updated. Only the results of the partial view is displayed.
In addition, the "OnSuccess" method doesn't seem to be getting hit as I'm debugging.
Can someone please tell me what I'm doing incorrecty?
Controller:
public class HomeController : Controller
{
private DAL db = new DAL();
public ActionResult Index()
{
ViewBag.Message = "Welcome to YeagerTech!";
return View();
}
// GET: Categories/Create
public ActionResult Create()
{
return View();
}
// POST: Categories/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(Category category)
{
if (ModelState.IsValid)
{
try
{
await db.AddCategoryAsync(category);
}
catch (System.Exception ex)
{
throw ex;
}
}
return View(category);
}
public PartialViewResult ShowDetails()
{
//string code = Request.Form["txtCode"];
Category cat = new Category();
//foreach (Product p in prodList)
//{
// if (p.ProdCode == code)
// {
// prod = p;
// break;
// }
//}
cat.CategoryID = 1;
cat.Description = "Financial";
return PartialView("_ShowDetails", cat);
}
}
Parent View
#model Models.Models.Category
#{
ViewBag.Title = "Create Category";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create Category</h2>
#using (Ajax.BeginForm("ShowDetails", "Home", new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "div1",
InsertionMode = InsertionMode.Replace,
OnSuccess = "OnSuccess",
OnFailure = "OnFailure"
}, new { #class = "form-horizontal" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="body-content">
<h4>Category</h4>
<hr />
<div class="form-group">
<div class="col-offset-1 col-lg-11 col-md-11 col-sm-11 col-xs-11">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control", #placeholder = "Description" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-lg-11 col-md-11 col-sm-11 col-xs-11">
<button type="submit" id="btnCategoryCreate" class="btn btn-primary"><span class="glyphicon glyphicon-save"></span>Create</button>
</div>
</div>
</div>
}
<div id="div1">
</div>
#*<div>
#Html.ActionLink("Back to List", "Index")
#Html.Hidden("categoryCreateUrl", Url.Action("Create", "Home"))
</div>*#
#section Scripts {
<script>
$(document).ready(function ()
{
function OnSuccess(response)
{
$('#form1').trigger("reset");
}
//if (typeof contentCreateCategory == "function")
// contentCreateCategory();
});
</script>
}
Partial View
#model Models.Models.Category
<h1>Cat Details</h1>
<h2>
Cat Code: #Model.CategoryID<br />
Cat Name: #Model.Description<br />
</h2>
EDIT # 1
Parent & child view displayed (due to a missing JS file, thanks to the mention of Stephen), plus was able to programmatically clear out the form with the OnSuccess method.
I had thought since that was JS, it needed to go in the Scripts section, but did not recognize it there.
Here is the finished code.
#using (Ajax.BeginForm("ShowDetails", "Home", new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "div1",
InsertionMode = InsertionMode.Replace,
OnSuccess = "OnSuccess",
OnFailure = "OnFailure"
}, new { #id = "frm1", #class = "form-horizontal" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="body-content">
<h4>Category</h4>
<hr />
<div class="form-group">
<div class="col-offset-1 col-lg-11 col-md-11 col-sm-11 col-xs-11">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control", #placeholder = "Description" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-lg-11 col-md-11 col-sm-11 col-xs-11">
<button type="submit" id="btnCategoryCreate" class="btn btn-primary"><span class="glyphicon glyphicon-save"></span>Create</button>
<button type="reset" id="btnClear" class="btn btn-default"><span class="glyphicon glyphicon-eye-close"></span>Clear</button>
</div>
</div>
</div>
}
<div id="div1">
</div>
<script type="text/javascript">
function OnSuccess(response)
{
$('#frm1').trigger("reset");
}
function OnFailure(response)
{
alert("Whoops! That didn't go so well did it?");
}
</script>
A bit late for answer, but yet it might help someone else.
The issue might be that the javascript files required for Ajax.BeginForm are not loaded in your page.
Microsoft.jQuery.Unobtrusive.Ajax
Nuget package to search for
<package id="Microsoft.jQuery.Unobtrusive.Ajax" version="3.2.3" targetFramework="net45" />
of course that the version might differ.
Add reference to the page after your jQuery and it should work.

Using Jquery to collect data, use data to get a Partial View from the controller, and append the passed information to the end of a page

I am attempting to append a partial view to the end of my 'currently displayed page' when a selection from a dropdown menu is chosen.
This is the dropdown from my view:
<div>
#Html.DropDownListFor(x => x.DropDownInfo, Model.Info, "DefaultSelection")
#Html.ValidationMessageFor(model => model.Courses)
</div>
I am in most need here in my Jquery. What do I need to do to append the PartialView that is returned by my controller (pasted below)? My current Jquery:
$(document).ready(function() {
$("#DropDownInfo").change(function() {
var strSelected = "";
$("#DropDownInfo option:selected").each(function() {
strSelected += $(this)[0].value;
});
var url = "/Controller/PreFillMethod/?MethodString=" + strSelected;
$.post(url, function(data) {
//*****
// Assuming everything else is correct,
// what do I do here to have my partial view returned
// at the end of the currently displayed page?
//*****
});
});
});
This is the part of my controller that replies with a PartialView (I want the string from the dropdown selection to be passed into this controller to ultimately be used to fill in a field in the PartialView's form) :
public PartialViewResult PreFillCourse(string selectedFromDropDown)
{
ViewBag.selectedString = selectedFromDropDown;
MyViewModel preFill = new MyViewModel
{
Title = selectedFromDropDown, // I am using this to pre-fill a field in a form
};
return PartialView("_PartialViewForm", preFill);
}
The Partial View (in the case that it matters):
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>CourseTemplates</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
I am open to suggestions if I am approaching the situation entirely incorrectly.
My goal is to have a user select a 'template' from the drop-down menu and have that template's data autopopulate into a form below the drop-down.
My Jquery is very rough - I am using this post as a guide
you should have a div in your view
<div id ="divToAppend">
</div>
then append the partial view to your div
$(document).ready(function() {
$("#DropDownInfo").change(function() {
var strSelected = "";
$("#DropDownInfo option:selected").each(function() {
strSelected += $(this)[0].value;
});
var url = "/Controller/PreFillMethod/?MethodString=" + strSelected;
$.post(url, function(data) {
$('#divToAppend').html(data);
//*****
// Assuming everything else is correct,
// what do I do here to have my partial view returned
// at the end of the currently displayed page?
//*****
});
});
});

MVC 3 + $.ajax - response seems to be caching output from partial view

I must be missing something, silly, but here is the problem.
I have a Create action on the Transactions controller. The Create.cshtml uses jQuery to post the form to the server using a call to $.ajax. Debugging shows that everything arrives on the server as expected. I use the form data to update a record: this works fine too. I then return a partial view, passing a model to the view with default data. I can debug and verify that the model is passing nulls and 0s, ie, the default data for my model.
Problem is, what gets sent back to the browser in the response is the old data...!
I can see no reason why. Hopefully, you can...
Note: I am not using any form of output cache.
EDIT 1:
The caching is not happening in the browser. The reason I say that is that I can see in Firebug the response of the call to the AjaxCreate Action. I can also see this in Fiddler.
EDIT 2:
If you look at the code for the Partial View, you will see that each dropdownlist or textbox has the value of #Model.Transaction.[Property] printed out beside it. This, bizarrely, shows the correct value, ie, the defaults for my Transaction object, but the dropdownlists and text boxes stick with the values that were posted to the server rather than the default values for the property each one is supposed to render.
EDIT 3:
I have included the following image, so you can see the values printed to the right of each control that are being passed in. And yet the controls reflect the old data posted to the server in the previous $.ajax call. (The comment shows a date time at the moment of creating the view model, that way I could see things updating).
EDIT 4:
I have found that replacing #Html.EditorFor(...) (see view code below) with #Html.TextBox helpers removes the problem. So, what seems to be happening is that the EditorFor helpers are causing the problem. Why? I have no idea, but will post another, more specific question.
Code and markup as follows:
jQuery:
$(document).ready(function () {
$('input[name="nextRecord"]').live('click', function () {
var theForm = $(this).closest('form');
if ((theForm).valid()) {
var buttonText = $(this).val();
var action = "/input/Transactions/AjaxCreate/";
if (buttonText === "Reset") {
clearForm(theForm);
}
else {
var targetElement = $('#CreateDiv');
var _data = theForm.serialize() + '&nextRecord=' + $(this).val();
$.ajax({
url: action,
data: _data,
cache: 'false',
type: 'POST',
dataType: 'html',
success: function (html) {
$(targetElement).html(html);
createDatePickers(targetElement);
jQuery.validator.unobtrusive.parse(targetElement);
}
});
}
}
return false;
});
});
Partial View:
#model FlatAdmin.Domain.ViewModels.TransactionViewModel
#* This partial view defines form fields that will appear when creating and editing entities *#
<div class="editor-label">
Fecha
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Transaction.TransactionDate, new { #class = "date-picker" })
#Html.ValidationMessageFor(model => model.Transaction.TransactionDate) #Model.Transaction.TransactionDate.ToString()
</div>
<div class="editor-label">
Origen:
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Transaction.IdFrom, ((IEnumerable<FlatAdmin.Domain.Entities.Account>)Model.FromAccounts).Select(option => new SelectListItem
{
Text = (option == null ? "None" : option.AccountName),
Value = option.AccountId.ToString(),
Selected = (Model != null) && (option.AccountId == Model.Transaction.IdFrom)
}), "Choose...")
#Html.ValidationMessageFor(model => model.Transaction.IdFrom)#Model.Transaction.IdFrom
</div>
<div class="editor-label">
Destino:
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Transaction.IdTo, ((IEnumerable<FlatAdmin.Domain.Entities.Account>)Model.ToAccounts).Select(option => new SelectListItem
{
Text = (option == null ? "None" : option.AccountName),
Value = option.AccountId.ToString(),
Selected = (Model != null) && (option.AccountId == Model.Transaction.IdTo)
}), "Choose...")
#Html.ValidationMessageFor(model => model.Transaction.IdTo)#Model.Transaction.IdTo
</div>
<div class="editor-label">
Monto
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Transaction.IdCurrency, ((IEnumerable<FlatAdmin.Domain.Entities.Currency>)Model.AllCurrencies).Select(option => new SelectListItem
{
Text = (option == null ? "None" : option.CurrencyName),
Value = option.CurrencyId.ToString(),
Selected = (Model != null) && (option.CurrencyId == Model.Transaction.IdCurrency)
}))
#Html.EditorFor(model => model.Transaction.Amount)
#Html.ValidationMessageFor(model => model.Transaction.Amount) #Model.Transaction.Amount
</div>
<div class="editor-label">
Comentario
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Transaction.Comment)
#Html.ValidationMessageFor(model => model.Transaction.Comment)#Model.Transaction.Comment
</div>
View:
#model FlatAdmin.Domain.ViewModels.TransactionViewModel
#using FlatAdmin.Domain.Entities
#{
ViewBag.Title = "Nueva Transaccion";
}
<h2>#ViewBag.Title</h2>
<div>
#Html.ActionLink("<< Lista de Transacciones", "Index")
</div>
<br />
<div id="InputPanel">
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Elegir Actividad</legend>
<div class="editor-field">
#Html.DropDownListFor(model => model.Transaction.IdCostCentre, ((IEnumerable<FlatAdmin.Domain.Entities.CostCentre>)Model.AllCostCentres).Select(option => new SelectListItem
{
Text = (option == null ? "None" : option.Name),
Value = option.CostCentreId.ToString(),
Selected = (Model != null) && (option.CostCentreId == Model.Transaction.IdFrom)
}), "Actividades...")
</div>
</fieldset>
<fieldset>
<legend>Transaccion</legend>
<div id="CreateDiv">
#Html.Partial("_Create", Model)
</div>
<p>
<input type="submit" name="nextRecord" value="Proxima Transaccion >>" />
</p>
<p>
...o sino, para guardar y volver a la lista de transacciones:<br /><input type="submit" value="Guardar" />
</p>
</fieldset>
}
</div>
Controller Action:
[HttpPost]
public virtual ActionResult AjaxCreate(Transaction transaction)
{
if (ModelState.IsValid)
{
service.InsertOrUpdate(transaction);
service.Save();
}
service.ChosenCostCentreId = transaction.IdCostCentre;
TransactionViewModel viewModel = new TransactionViewModel();
viewModel.Transaction = new Transaction();
viewModel.CostCentre = service.ChosenCostCentre;
viewModel.AllCostCentres = service.AllCostCentres;
viewModel.AllCurrencies = service.AllCurrencies;
viewModel.FromAccounts = service.FromAccounts;
viewModel.ToAccounts = service.ToAccounts;
return PartialView("_Create", viewModel);
}
#Darin Dimitrov came up with the answer in a related thread.
Essentially, the HtmlHelpers such as Html.EditorFor, Html.TextBoxFor, etc, check first in the ModelState for existing values, and ONLY then in the Model.
As a result, I needed a call to:
ModelState.Clear();
Ignorance is so painful.
Try explicitly setting the outputcache duration to 0 on your controller action.
I think the browser isn't supposed to cache POSTs but it seems to still do that sometimes.

Resources