Partial View and ajax - ajax

I want to update Partial View via ajax, but it does not work. Look at this model class:
public class LogOnModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
public bool IsLoggedIn { get; set; }
public string ReturnUrl { get; set; }
}
the following view:
#model ITW2012Mobile.ViewModels.LogOnModel
<div id='LogOn' style="background-color: White;">
#using (Ajax.BeginForm("LogOnAjax", "Home", new AjaxOptions { UpdateTargetId = "LogOn", OnSuccess = "logInComplete" }))
{
ITW2012Mobile.ViewModels.LogOnModel m = Model;
#Html.EditorFor(model => model.IsLoggedIn)
#Html.EditorFor(model => model.ReturnUrl)
<div>
#Html.ValidationSummary()
</div>
<div>
#Html.LabelFor(model => model.UserName)
#Html.EditorFor(model => model.UserName)
</div>
<div>
#Html.LabelFor(model => model.Password)
#Html.EditorFor(model => model.Password)
</div>
<div>
<input type="submit" value="Login" />
</div>
}
</div>
and the following controller class:
public ActionResult LogOnAjax(LogOnModel model)
{
if (!User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
model.IsLoggedIn = true;
model.ReturnUrl = Url.Action("Index", "Home");
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return PartialView("PartialViewAjaxLogOn", model);
}
else
{
return PartialView("PartialViewLogOut");
}
}
even when username/password are correct and IsLoggedIn = true and ReturnUrl!=empty view shows empty fields for these variables (but debugger shows values inside). Why and how to make it correctly?

Try clearing the values you are modifying in your action from modelstate or if you use them in html helpers the old values will be used:
ModelState.Remove("IsLoggedIn");
model.IsLoggedIn = true;
ModelState.Remove("ReturnUrl");
model.ReturnUrl = Url.Action("Index", "Home");
Also bear in mind that upon successful authentication and cookie emission you should not display a view (partial in your case). You should redirect so that the authentication cookie is sent by the client on the subsequent request. You should redirect to the return url. But since you are doing this using AJAX you should probably send some indication to the client that the authentication was successful so that you can redirect on the client.

Related

displaying validation message using Ajax.beginform in a partial

I am new in .NET mvc 5 environment.
I have a view:
Index.cshtml
#model Accounts.WebHost.Models.SendUsernameReminderInputModel
#using (Ajax.BeginForm("Index", "SendUsernameReminder", null, new AjaxOptions { InsertionMode = InsertionMode.Replace, UpdateTargetId = "validationList", LoadingElementId = "loader", OnSuccess = "onSuccess", OnFailure = "onFailure" }, new { #id = "validationForm", #class = "form-inline" }))
{
#Html.AntiForgeryToken()
<div class="form-group">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control" } })
</div>
<div class="form-group hidden">
#Html.TextBoxFor(model => model.Tenant, new { #class = "form-control", #Value = "default" })
</div>
<button type="submit" class="btn btn-default">Submit</button>
}
<hr />
<div id="loader" class="alert" style="display:none">
<img src="~/Content/img/ajax-loader.gif" />
</div>
#Html.Partial("_UsernameValidation")
And a Partial view:
_UsernameValidation.cshtml
#model Accounts.WebHost.Models.SendUsernameReminderInputModel
<div id="validationList">
<table>
<tr>
<td>#Html.ValidationMessageFor(model => model.Email)</td>
<td>#Html.ValidationMessageFor(model => model.Tenant)</td>
</tr>
</table>
</div>
this is my Controller:
SendUsernameReminderController.cs
using Accounts.Entities.Models;
using Accounts.WebHost.Models;
using BrockAllen.MembershipReboot;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Accounts.WebHost.Controllers
{
public class SendUsernameReminderController : Controller
{
public readonly UserAccountService<MemberAccount> userAccountService;
public SendUsernameReminderController(UserAccountService<MemberAccount> userAccountService)
{
this.userAccountService = userAccountService;
}
[HttpGet]
public ActionResult Index(string signin)
{
ViewBag.Signin = signin;
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(SendUsernameReminderInputModel model)
{
if (ModelState.IsValid)
{
try
{
this.userAccountService.SendUsernameReminder(model.Tenant, model.Email);
return RedirectToAction("Success", model);
}
catch (ValidationException ex)
{
if (ex.ValidationResult.ToString() == "The email address for this account is not yet verified.")
{
try
{
userAccountService.ResetPassword(model.Tenant, model.Email);
return RedirectToAction("Unverified");
}
catch (ValidationException resetex)
{
ModelState.AddModelError("", resetex.Message);
ViewBag.Message = "resetex.Message";
return View();
}
}
ModelState.AddModelError("", ex.Message);
return View();
}
}
return View();
}
public ActionResult Success(SendUsernameReminderInputModel model)
{
ViewBag.Subject = "Username Emailed";
ViewBag.Message = "Your username was emailed at " + model.Email + ". If you don't receive this email within 24 hours, please check your junk mail folder or visit our Help pages to contact Customer Service for further assistance.";
return View();
}
public ActionResult Unverified()
{
ViewBag.Subject = "Email has not been verified";
ViewBag.Message = "You will receive an email from us to confirm your email or cancel your registration. If you don't receive this email within 24 hours, please check your junk mail folder or visit our Help pages to contact Customer Service for further assistance.";
return View();
}
}
}
And this is my Model:
SendUsernameReminderInputModel.cs
using System.ComponentModel.DataAnnotations;
namespace Accounts.WebHost.Models
{
public class SendUsernameReminderInputModel
{
[Required]
[EmailAddress]
public string Email
{
get;
set;
}
[Required]
public string Tenant
{
get;
set;
}
}
}
my aim is that when a user clicks the form submit button only the validation message will display below the form. unfortunately, it outputs the whole Index.cshtml in the partial and the validation message at the bottom.
If this is a bad approach please give me directions.
Thank you in advance.
U need to pass Model like View(model) ; so that the View gets the model with those errors.
And u could also use #Html.ValidationSummary to display all the errors on a fly

Cannot Bind Model Object to TextBoxFor

I am trying to enter a 'UserId' in the texbox and when I click on 'Find User' button, I need to get 'UserName' and 'Role' from Database based on the UserId and display them in the texboxes so that if Username and Role have to be edited they will be edited and Updated to Database.
But, when I enter UserId, the UserId value is available in the Model, and I am able to query teh database and get the results and assign them to the Model's UserName and Role (Model.UserName and Model.Role), but I am unable bind them to HTML.TextBoxFor.
I have a model like below
public class EditUserInfoModel
{
[Display(Name = "User ID")]
public string UserId { get; set; }
[Display(Name = "User Name")]
public string UserName { get; set; }
[Display(Name = "Role")]
[DataType(DataType.Text)]
public string Role { get; set; }
}
..and Controller like below
[HttpPost]
public ActionResult EditUserInfo(EditUserInfoModel model)
{
if (ModelState.IsValid)
{
string con = ConfigurationManager.ConnectionStrings["DMSCON"].ConnectionString;
DataTable dt = new DataTable();
SqlConnection dbCon = new SqlConnection(con);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "select user_name,user_role from user_info where
user_id=#userid";
cmd.Parameters.Add(new SqlParameter("#userid", model.UserId));
cmd.Connection = dbCon;
dbCon.Open();
SqlDataAdapter dap = new SqlDataAdapter(cmd);
dap.Fill(dt);
EditUserInfoModel myModel = new EditUserInfoModel();
foreach (DataRow row in dt.Rows)
{
myModel.UserName = row["user_name"].ToString();
myModel.Role = row["user_role"].ToString();
//model.isBlocked = (bool)row[""];
//model.isExpired = (bool)row[""];
// model.UserId = "somevalue";
}
return View("EditUserInfo", myModel);
}
return View();
}
and the View like below
#model WebDMS.Models.EditUserInfoModel
#{
ViewBag.Title = "EditUserInfo";
//Layout = "~/Views/Shared/_LayoutPage1.cshtml";
}
<h2>
EditUserInfo</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>EditUserInfoModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserId)
#Html.ValidationMessageFor(model => model.UserId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Role)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Role)
#Html.ValidationMessageFor(model => model.Role)
</div>
<input type="submit" value="Find User" />
</fieldset>
}
you need to do little modification in your controller action method and view model.
Model
public class EditUserInfoModel
{
[Required]
[Display(Name = "User ID")]
public string UserId { get; set; }
[Required]
[Display(Name = "User Name")]
public string UserName { get; set; }
[Required]
[Display(Name = "Role")]
[DataType(DataType.Text)]
public string Role { get; set; }
}
Controller:
[HttpPost]
public ActionResult EditUserInfo(EditUserInfoModel model)
{
string con = ConfigurationManager.ConnectionStrings["DMSCON"].ConnectionString;
DataTable dt = new DataTable();
SqlConnection dbCon = new SqlConnection(con);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "select user_name,user_role from user_info where
user_id=#userid";
cmd.Parameters.Add(new SqlParameter("#userid", model.UserId));
cmd.Connection = dbCon;
dbCon.Open();
SqlDataAdapter dap = new SqlDataAdapter(cmd);
dap.Fill(dt);
EditUserInfoModel myModel = new EditUserInfoModel();
foreach (DataRow row in dt.Rows)
{
myModel.UserName = row["user_name"].ToString();
myModel.Role = row["user_role"].ToString();
}
//return View("EditUserInfo", myModel); //try removing this line
return View(myModel);
}
If you use dataannotations then you do not need to check ModelState.IsValid in your action method. Your #ValidationMessage on your form fields inside your view will have more meaning then and will work as expected
you do not need to explicitly call the view name. Let the MVC-3 handles that. Instead try it as mentioned in this response.
these are not i would say managed and structured approach though. Always Close() and Dispose() your Db connections by putting them in using() block or try-catch-finally block. Make a layered structure so you can separate your database interaction from your controller actions. Hope it helps.

asp.net mvc 4 when submitting form for file upload the validation works only AFTER file has been uploaded to webserver. why?

I want to do the validations (required fields, length validations and terms and condition check, also file size check ... before the file will be submited to webserver)
now file is uploaded to webserver and after it (for a 2GB file it takes e.g. 20 min) i get the error messages if i leave fields like 'title' empty.
How can validations be done before?
View:
#using (Html.BeginForm("FileUpload", "Home")){
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<legend>Video Upload</legend>
<ol>
<li>
#Html.LabelFor(m => m.Title)
#Html.TextBoxFor(m => m.Title, new {#Class = "action add", title="Enter your video/movie title here." })
</li>
<li>
#Html.LabelFor(m => m.Description)
#Html.TextAreaFor(m => m.Description, new Dictionary<string,object>{{"rows","3"}})
</li>
<li>
#Html.CheckBoxFor(m => m.AGB)
#Html.LabelFor(m => m.AGB, new {#class = "checkbox" })
</li>
</ol>
<input type="file" id="fileCntrl" name="uploadFile" accept="video/*" data-val="true" data-val-required="File is required"/>
<button type="submit" id="btnUpload" value="Upload Video" title="Upload Video" class="btn">Upload Video</button>
</fieldset>
}
Model:
public class UploadModel
{
[Required]
[StringLength(100)], ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 2)]
[Display(Name = "* Title:")]
public string Title { get; set; }
[StringLength(300)], ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 2)]
[Display(Name = "Description:")]
public string Description { get; set; }
[Required]
[Display(Name = "* I agree to mowee.tv Terms And Conditions.")]
public bool AGB { get; set; }
}
Controller:
[HttpPost]
[AllowAnonymous]
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult FileUpload(HttpPostedFileBase uploadFile, UploadModel myModel)
{
if (ModelState.IsValid) // **<== i arrive here after file has been submitted to webserver**
{
try
{
if (myModel.AGB == false)
{
ModelState.AddModelError("", "Please read our Terms and Conditions");
return View("Index", myModel);
}
if (uploadFile != null && uploadFile.ContentLength > 0)
{
//write some data to database
//send mail with link for uploaded file
}
else
{
ModelState.AddModelError("", "Please choose a video/movie.");
return View("Index", myModel);
}
}
catch (Exception ex)
{
ModelState.AddModelError("", "An error occured. Try again.");
return View("ErrorUpload", myModel);
}
//model is valid
return View("SuccessUpload", myModel);
}
// model is not valid
return View("Index", myModel);
}
File fields are inaccessible via JavaScript for security reasons, so client side validation won't work against them. You'll need to perform validation on the server.

MVC3 view not updating using EF 4.1

I am having a problem updating a record. For some reason it is not even hitting the post action in the controller and just returning:
"An item with the same key has already been added."
It seems to be behaving as if it is doing an insert rather than an update. I would appreciate a new set of eyes on this. It is probably something very simple that I have missed.
Controller:
// GET: /Manage/Regions/Edit/5
public ActionResult Edit(int id)
{
Region_CU regionEdit = (from r in db.Venues_Regions
where r.RegionsID == id
select new Region_CU
{
RegionsID = r.RegionsID,
Name = r.Name,
CalendarLink = r.CalendarLink,
MapIcon = r.MapIcon,
QtrStart = r.QtrStart,
QtrEnd = r.QtrEnd,
FacebookLikeBox = r.FacebookLikeBox,
FacebookId = r.FacebookId
// region = r
}).Single();
return View(regionEdit);
}
//
// POST: /Manage/Regions/Edit/5
[HttpPost]
public ActionResult Edit(Region_CU r)
{
if (ModelState.IsValid)
{
var v = db.Venues_Regions.First(i => i.RegionsID == r.RegionsID);
//v.RegionsID = r.RegionsID;
v.Name = r.Name;
v.CalendarLink = r.CalendarLink;
v.MapIcon = r.MapIcon;
v.QtrStart = r.QtrStart;
v.QtrEnd = r.QtrEnd;
v.FacebookLikeBox = r.FacebookLikeBox;
v.FacebookId = r.FacebookId;
//Venues_Regions v = new Venues_Regions
//{
// RegionsID = r.RegionsID,
// Name = r.Name,
// CalendarLink = r.CalendarLink,
// MapIcon = r.MapIcon,
// QtrStart = r.QtrStart,
// QtrEnd = r.QtrEnd,
// FacebookLikeBox = r.FacebookLikeBox,
// FacebookId = r.FacebookId
//};
//db.Venues_Regions.Attach(v);
//db.ObjectStateManager.ChangeObjectState(v, EntityState.Modified);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(r);
}
View :
#model THPT_Razor.Areas.Manage.Models.Region_CU
#{
ViewBag.Title = "Edit Region";
}
<h2>Edit</h2>
<link href="#Url.Content("~/Content/themes/base/jquery.ui.all.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"> </script>
<script>
$(document).ready(function () { $('.date').datepicker({ dateFormat: "mm/dd/yy" }); });
</script>
#using (Html.BeginForm()) {
#* #Html.ValidationSummary(true)*#
<fieldset>
<legend>#Html.DisplayFor(model => model.Name)</legend>
#Html.HiddenFor(model => model.RegionsID)
#Html.HiddenFor(model => model.Name)
#Html.HiddenFor(model => model.CalendarLink)
<div class="editor-label">
#Html.Label("Map Icon")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.MapIcon, new SelectList(Model.mapicons,"id","Description"))
</div>
<div class="editor-label">
#Html.Label("Quarter Start")
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.QtrStart, new { #class = "date" })
#Html.ValidationMessageFor(model => model.QtrStart)
</div>
<div class="editor-label">
#Html.Label("Quarter End")
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.QtrEnd, new { #class = "date" })
#Html.ValidationMessageFor(model => model.QtrEnd)
</div>
<div class="editor-label">
#Html.Label("Region Facebook ID")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FacebookId)
#Html.ValidationMessageFor(model => model.FacebookId)
</div>
<div class="editor-label">
#Html.Label("Region Facebook LikeBox Code")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FacebookLikeBox)
#Html.ValidationMessageFor(model => model.FacebookLikeBox)
</div>
<p>
<input type="submit" value="Update" />
</p>
</fieldset>
Edit:
I have received several good suggestions but I guess I have not been clear. Region_CU is not an Entity. Venues_Regions is what I am trying to update. see the comments in the class below for clarification. The original objective was to build a simple wrapper that had the Venues_Regions object and a list object for the map icons. However, the data annotation for the field likebox was not being passed through resulting in the the Venues_Regions object to be broken out. Now when I try to save the update it is not even hitting the http post action. I hope this clears up what I am trying to accomplish and asking for help with. Thanks again for all the help and quick responses.
//create and update
public class Region_CU
{
public Region_CU()
{
}
public List<MapIcon> mapicons { get; set; }
//public Venues_Regions region { get; set; }
// The fields below are what makes up Veunes_Region
// this was broken out from the above Venues_Region
// because the UIHint was not being passed through
public int RegionsID { get; set; }
public string Name { get; set; }
public string CalendarLink { get; set; }
public int MapIcon { get; set; }
public DateTime? QtrStart { get; set; }
public DateTime? QtrEnd { get; set; }
[UIHint("tinymce_jquery_full"), AllowHtml]
public string FacebookLikeBox { get; set; }
public string FacebookId { get; set; }
public string mapIcon { get; set; }
}
Edit #2:
After a good nights sleep the solution presented itself. In the update action all I needed to do was change from the wrapper being passed in to the Venues_Region object being passed in and now everything works.
Thanks for all the help and suggestions.
Thanks in advance for the help,
Chris
Actually all you need to do is load the venues and call TryUpdateModel. You don't even need to pass in the object. Then save the venue. Another approach is to use automapper to copy the fields between objects or use the attach method as mentioned but either way no manual field copying is required.

Is the Compare Validator Bugged

This is very basic but it always returns false on the compare validation. Anyone else running in to this problem?
public class UsersRegisterUserViewModel
{
[DisplayName("E-Mail Address")]
[Required(ErrorMessage = "E-Mail Address is required")]
[RegularExpression(#"^[A-Za-z0-9_\-\.]+#(([A-Za-z0-9\-])+\.)+([A-Za-z\-])+$", ErrorMessage = "Invalid E-mail Address")]
public string RegUsername { get; set; }
[Required]
[Display(Name = "Password")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required]
[Display(Name = "Confirm Password")]
[Compare("Password", ErrorMessage = "Passwords must match")]
[DataType(DataType.Password)]
public string RegConfirmPassword { get; set; }
}
adapters.add("equalto", ["other"], function (options) {
var prefix = getModelPrefix(options.element.name),
other = options.params.other,
fullOtherName = appendModelPrefix(other, prefix),
//element = $(options.form).find(":input[name=" + fullOtherName + "]")[0];
element = $(options.form).find(":input[name='" + fullOtherName + "']")[0];
MVC3 Compare attribute is buggy when comparing passwords independently of Account Controller. It seems it is hardcoded to only work with Account controller.
1. Cut and past email, password, confirm password from RegisterModel into a new file called ViewModels/ShortRegister.cs
2. Cut razor code ( email, password, confirm password) from register view and past it into partial view, call it "_shortRegistration".
3. Create a new controller called "ShortRegistration". Add the partial view into ShortRegistation.
5. Add related jquery scripts
Create a link on home page to ShortRegistration.
Confirmation error message always fires error message.
Remove the email from the partial view confirmation, The Compare functionality works.
Add userName to the partial view and view-model, Compare functionality fails, again password confirmation error message always displays error message.
Has this bug been fixed? I disabled Compare attribute and wrote jquery and CCS to fix this! I am more than happy to email the code to prove that Compare is buggy.
Hmm, no, I am not running into such problems. I've just tested the following code and it worked perfectly fine as expected.
Model:
public class UsersRegisterUserViewModel
{
[DisplayName("E-Mail Address")]
[Required(ErrorMessage = "E-Mail Address is required")]
[RegularExpression(#"^[A-Za-z0-9_\-\.]+#(([A-Za-z0-9\-])+\.)+([A-Za-z\-])+$", ErrorMessage = "Invalid E-mail Address")]
public string RegUsername { get; set; }
[Required]
[Display(Name = "Password")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required]
[Display(Name = "Confirm Password")]
[Compare("Password", ErrorMessage = "Passwords must match")]
[DataType(DataType.Password)]
public string RegConfirmPassword { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new UsersRegisterUserViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(UsersRegisterUserViewModel model)
{
return View(model);
}
}
View:
#model UsersRegisterUserViewModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.RegUsername)
#Html.EditorFor(x => x.RegUsername)
#Html.ValidationMessageFor(x => x.RegUsername)
</div>
<div>
#Html.LabelFor(x => x.Password)
#Html.EditorFor(x => x.Password)
#Html.ValidationMessageFor(x => x.Password)
</div>
<div>
#Html.LabelFor(x => x.RegConfirmPassword)
#Html.EditorFor(x => x.RegConfirmPassword)
#Html.ValidationMessageFor(x => x.RegConfirmPassword)
</div>
<input type="submit" value="OK" />
}
So now the question becomes: how is your code different than mine?

Resources