displaying validation message using Ajax.beginform in a partial - ajax

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

Related

How does selected dropdown value get saved from View Component to main model?

ASP.NET Core 5 MVC web app. The question is HOW it works, not why it doesn't. I don't understand the mechanism and so don't want to see it fail from some "happy-fingers" coding accident in the future...
I have a main model that the controller expects on create:
public class ProductCategory : BaseClass
{
public int ProductId { get; set; }
public virtual Product Product { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
(BaseClass just has your typical record keeping fields).
I have a model for the view components; I need two, one for each dropdown, so you can easily imagine the other, with names modified to protect the innocent...:
Category:
public class CategoryList
{
public CategoryList()
{
Categories = new List<Category>();
}
public int CategoryId { get; set; }
[DisplayName("Categories")]
public List<Category> Categories { get; set; }
}
The category view component:
public class CategoryDropdownViewComponent : ViewComponent
{
private readonly ApplicationDbContext _db;
public CategoryDropdownViewComponent(ApplicationDbContext context)
{
_db = context;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var items = await GetCategoriesAsync();
var TheView = "Default";
var list = new CategoryList();
if (items.Count == 0)
{
TheView = "CategoryMaintenanceLink";
}
else
{
items.Insert(0, new Category() { Id = 0, Name = "-- Please select an option --" });
list.Categories = items;
}
return View(TheView, list);
}
private Task<List<Category>> GetCategoriesAsync()
{
return _db.Category.ToListAsync();
}
}
And the default view for category (I store this and above in ~\Shared\Components\CategoryDropdown\):
#model CoolestProjectNameEver.Models.CategoryList
<p>
#Html.DropDownListFor(model => model.CategoryId, new SelectList(Model.Categories, "Id", "Name"), new { #class = "form-control" })
</p>
So, in my controller, I kick off create:
public IActionResult Create()
{
return View();
}
And in the Create view, amongst other things, I fire up the view components:
<div class="form-group">
<label asp-for="ProductId" class="control-label"></label>
#await Component.InvokeAsync("ProductDropdown")
</div>
<div class="form-group">
<label asp-for="CategoryId" class="control-label"></label>
#await Component.InvokeAsync("CategoryDropdown")
</div>
All works and the dropdown lists are filled. I can select options for both. Now the unknown part.
On to the POST method for Create:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(ProductCategory productCategory)
{
try
{
if (ModelState.IsValid) <--- breakpoint
{
_context.Add(productCategory);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return View(productCategory);
}
The breakpoint will show the correct selected values for CategoryId and ProductId.
So the question is, did this work because of a name match in the VC model to main controller model, and it auto filled somehow?
1 if my ViewComponent model had, say SelectedValueId instead of CategoryId, then it would fail because of a mismatch?
2 How did the value from a separate model in an async ViewComponent get plugged into the main model on postback?
In fact,if you change your Create view code to:
<div class="form-group">
#await Component.InvokeAsync("ProductDropdown")
</div>
<div class="form-group">
#await Component.InvokeAsync("CategoryDropdown")
</div>
it will also successfully binding.
The model binding in asp.net core is matched according to the name. If the name matches, the corresponding attribute will be bound to the model.
In your ViewComponent, your code(model => model.CategoryId):
#Html.DropDownListFor(model => model.CategoryId, new SelectList(Model.Categories, "Id", "Name"), new { #class = "form-control" })
will be given name =CategoryId in the generated html code
Then the name CategoryId is also in your ProductCategory model, if their names match, the binding will be successful.

how to pass dropdownlist selected value to controller

binding of dropdownlist is as follows
#Html.DropDownListFor(model => model.vDeptCode, (SelectList)ViewBag.deptList, "- Select Department -", new { #class = "form-control", #id = "ddl_Dept" })
On the same view i Have add button also after clicking on add button i have to pass deptCode value to my Action for that i have done
function CreateNewVoucher()
{
window.location.href = "#Url.Action("Add", "Voucher", new { #vDeptCode =#Html.Raw(Model.vDeptCode) })";
}
but it always pass null value.
Please guide me how to pass value to my ActionResult in Controller
Look, How i am passing the DropdownList selected value to Controller Post Method.
I have add only necessary Codes here, Please follow this to get your Dropdown value.
Controller
[HttpGet]
public ActionResult Index()
{
AccountModel account = new AccountModel();
account.accountStatusList= ObjContext.Status.ToList();
return View(account);
}
Custom Model Class
public class AccountModel
{
public int selectedStatusId { get; set; }
public List<Status> accountStatusList { get; set; }
}
View
#model Nop.Models.AccountModel
#using (Html.BeginForm("Index", "ControllerName", FormMethod.Post))
{
<div class="row-fluid span12">
<div class="span3">
<p><strong>Account Status :</strong></p>
</div>
<div class="span5">
// Bind the dropdown by List.
#Html.DropDownListFor(model => model.selectedStatusId, new SelectList(Model.accountStatusList, "StatusId", "StatusName"), "--Select--")
</div>
</div>
<div class="row-fluid span12">
<button class="btn btn-inverse" type="submit" title="Search for Account"><span class="fa fa-search"> Search</span></button>
</div>
}
Controller Post Method
[HttpPost]
public ActionResult Index(AccountModel account)
{
int selectedID = account.selectedStatusId; // You can find your selectedID here..
return View(account);
}
See ViewBag vs Model.

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.

Load ValidationSummary using ajax

How to load ValidationSummary using ajax? I was trying to use MVC's ready Membership.
Simple question, but I'm stuck.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[RecaptchaControlMvc.CaptchaValidator]
public ActionResult Register(RegisterModel model, bool captchaValid, string captchaErrorMessage)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
if (captchaValid)
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
ModelState.AddModelError("", captchaErrorMessage);
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
View:
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<legend>Registration Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName)
#Html.ValidationMessageFor(m => m.UserName)
<input type="hidden" id ="some" value=""/>
</li>etc.
I don't want to redirect each time on for example, if username exists or etc.
To do this you can return a partial view as html. The rendered partial will contain the modelstate errors and therefore will display when returned as html.
Example
Could can create a class called AjaxResult
public class AjaxResult
{
public string Html { get; set; }
public bool Success { get; set; }
}
Then in your success function from the ajax call you can append the html to the appropriate element. e.g.
$.ajax({
url: 'http://bacon/receive',
dataType: "json",
type: "POST",
error: function () {
},
success: function (data) {
if (data.Success) {
$('body').append(data.Html);
}
}
});

Partial View and 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.

Resources