In Asp.net mvc 5. we have a login page which implements HTML.BeginForm() to post data to controller , if the username and password are incorrect, we are sending error back to the front end using partial views. Everything work fine, In case of error only error message is displayed on the screen and nothing else.
Controller
[HttpPost]
public ActionResult SingleSSOMPLogin(SSoStateModel model)
{
//Check the Code with SP providers and Authorization server.
if(model.SAMLAssertion.validMPData)
{
return RedirectToAction("SSOServiceMP" , "SAML");
}else
{
//Error message processed.
model.errorMessage = SSOState.SAMLAssertion.errorMessage;
return PartialView("_LoginError" , model);
}
}
The view contain the following Code
<div class="peripheral">
<div class="panel panel-default panel-peripheral">
#{Html.Partial("_LoginError", Model);}
<div class="panel-heading clearfix">Please Log In</div>
<div class="panel-body">
<div class="brand-logo eop-logo">
<script type="text/javascript">
function changeImage() {
document.getElementById("Logo").src = '#Url.Content("~/Content/images/TopLogo.gif")';
}
</script>
<img id="Logo" width="200" src='#Url.Content("~/Content/images/TopLogo.gif")' onerror="changeImage()" />
#{ Html.EnableClientValidation(true);}
<!--Ajax call to Controller-->
#using (Html.BeginForm("SingleSSOMPLogin", "Accounts"))
{
#Html.ValidationSummary(true);
<div id="group-email" class="form-group col-md-12 ">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control", placeholder = "Please enter your Email address" })
#Html.ValidationMessageFor(m => m.Email)
</div>
<div id="group-password" class="form-group col-md-12">
#Html.PasswordFor(m => m.Password, new { #class = "form-control", placeholder = "Please enter your password" })
#Html.ValidationMessageFor(m => m.Password)
</div>
<div class="form-group">
<div class="col-md-12">
<button type="submit" class="btn btn-primary pull-left">Login</button>
<a id="forgot" href='#Url.Action("ForgotPassword","Accounts")' class="btn btn-link btn-sm pull-right">Forgot your password?</a>
</div>
</div>
}
</div>
</div>
</div>
</div>
Partial View
#{
Layout = null;
}
#if (Model.Result!= null)
{
<div class="alert alert-warning alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<strong>#Model.Result.errorMessage</strong>
</div>
}
When Error occur, I get redirect to same view again with all query parameter gone and display only error message.
How to solve following issue?
The partial view will return only the fragment you defined.
So when called from a "complete" view with
#{Html.Partial("_LoginError", Model);}
it will generate the corresponding part of the view.
In your situation what is most common is to add a model error and return the complete view (that must have a ValidationSummary section):
[HttpPost]
public ActionResult SingleSSOMPLogin(SSoStateModel model)
{
//Check the Code with SP providers and Authorization server.
if(model.SAMLAssertion.validMPData)
{
return RedirectToAction("SSOServiceMP" , "SAML");
}else
{
//Error message processed.
ModelState.AddModelError("error", SSOState.SAMLAssertion.errorMessage);
return View(model);
}
}
If you want to use the partial view you have to call it from an javacript ajax call and insert the response in your page. With jQuery it is something more or less like:
$.ajax({ url: <partial view url>,
type: 'GET',
success: function (result) {
$(updateSelector).hide().html(result).effect("fade", "fast");
}
});
Related
Can please someone help me here?! Thank you!
I have a view that displays a list of products along with an "Add Product" button for each. I am calling the CreateNewProduct method for each "Add Product" click to find the status of the product. Depending on the status, either I need to stay on the same view or I need to call a modal popup. I am able to do this by creating a modal popup in a different view. But I want to call the modal popup div (also pass the modal) from the same view where it displays a list of products. Is this possible?
public ActionResult CreateNewProduct(int productId)
{
var sharedProduct = _productTemplateService.GetSharedProducts(productId);
var _finalSharedProducts = (sharedProduct.Any(t => t.productId != productId));
if (_finalSharedProducts)
{
var sharedProdctTemplate = _productTemplateService.GetSharedProduct(productId);
return View("ModalView", new SharedModel
{
SharedProduct = sharedProdctTemplate
});
}
else
{
_productTemplateService.CreateNewProduct(productId);
return RedirectToAction("Details", "ProductTemplate");
}
}
Model View Code
<div class="modal fade" id="myModal" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Shared Product</h4>
</div>
<div class="modal-body">
<div class="flex-row">
<div class="col-6">
<div class="d-flex flex-row">
<div class="p-2">Product ID</div>
<div class="p-2">Product Types</div>
<div class="p-2">Status</div>
</div>
#foreach (var productTemplate in Model.SharedProduct )
{
<div class="d-flex flex-row">
<div class="p-2">#productTemplate.ProductId</div>
<div class="p-2">#productTemplate.ProductType</div>
<div class="p-2">#productTemplate.StatusCode</div>
</div>
}
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<p>
#Html.ActionLink("Edit", "Edit", new { /* id = Model.PrimaryKey */ }) |
#Html.ActionLink("Back to List", "Index")
</p>
<script type="text/javascript">
$(document).ready(function () {
$('#myModal').modal('show');
});
</script>
UPDATE:
I made it working. This is what I did. At the end, I have mentioned issues I am facing.
Link, modal and script in my main view - Detail View (called from ProductTemplate Controller)
<td>Add New Product</td>
<div class="modal fade" id="mymodel" role="dialog" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Shared Products</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<div class="modal-body" id="mymodelbody">
</div>
</div>
</div>
<script>
var loadModal = function (productId, customerId) {
$.ajax({
type: 'GET',
url: '/NewProductTemplate/CreateNewProduct',
cache: false,
data: {
productId: productId,
customerId: customerId
},
dataType: 'html',
success: function (data) {;
$("#mymodelbody").html(data);
$("#mymodel").modal("show");
}
});
}
</script>
NewProductTemplateController Code
public ActionResult CreateNewProduct(Guid productId, Guid customerId)
{
var sharedProduct = _productTemplateService.GetSharedProducts(productId);
var _finalSharedProducts = (sharedProduct.Any(t => t.productId != productId));
if (_finalSharedProducts)
{
var sharedProdctTemplate = _productTemplateService.GetSharedProduct(productId);
return PartialView("_shared", new SharedModel
{
SharedProduct = sharedProdctTemplate
});
}
else
{
_productTemplateService.CreateNewProduct(productId);
return RedirectToAction("Details", "ProductTemplate");
}
}
Partial view _shared.view code
#model SharedModel
#using (Html.BeginForm("ShareProduct", "NewProductTemplate", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="flex-row">
<div class="col-6">
<div class="d-flex flex-row">
<div class="p-2">Product ID</div>
<div class="p-2">Product Types</div>
<div class="p-2">Status</div>
</div>
#for (var i = 0; i < Model.SharedProducts.Count(); i++)
{
#Html.HiddenFor(model => model.SharedProducts.ElementAt(i).ProductId)
#Html.HiddenFor(model => model.SharedProducts.ElementAt(i).CustomerId)
#Html.HiddenFor(model => model.SharedProducts.ElementAt(i).ProductType)
#Html.HiddenFor(model => model.SharedProducts.ElementAt(i).StatusCode)
#Html.HiddenFor(model => model.SharedProducts.ElementAt(i).IsShared)
<div class="d-flex flex-row">
<div class="p-2">#Html.DisplayFor(model => model.SharedProducts.ElementAt(i).ProductId)</div>
<div class="p-2">#Html.DisplayFor(model => model.SharedProducts.ElementAt(i).ProductType)</div>
<div class="p-2">#Html.DisplayFor(model => model.SharedProducts.ElementAt(i).StatusCode)</div>
#if (Model.SharedProducts.ElementAt(i).StatusCode == VersionStatus.PUBLISHED)
{
<div class="p-2">#Html.EditorFor(m => m.SharedProducts.ElementAt(i).IsShared)</div>
}
</div>
}
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-sm btn-primary" />
<button type="button" class="btn btn-sm btn-primary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
PROBLEM:
1) When I save submit button in modal pop-up (partial view), it calls ShareProduct method from NewProductTemplate controller. For some reason, model SharedModel's SharedProducts property is null when it gets to controller code. Can you please help me here why it gets null?
public ActionResult ShareProduct (SharedModel shareModel)
{
//Access ShareProducts from shareModel
return RedirectToAction("Details", "ProductTemplate");
}
PROBLEM:
2) I want to load popup only if the product is shared, otherwise I just want to redirect to Detail view as mentioned in NewProductTemplate controller's CreateNewProduct method. Problem is that it loads Detail view also in popup if product is not shared since that's what my script is doing. Is there any way that I can check data in Success function before showing modal popup? If data/html contains Shared text, I would like to load the normal Detail view. I am just assuming. I tried to do so but with no success.
Detail method in ProductTemplate Controller
public ActionResult Details()
{
var productTemplate = _productTemplateService.GetAllProducts(User);
return View(new DetailsModel
{
ProductTemplate = productTemplate,
});
}
(This is for Bootstrap 3.3.7 Hopefully it's relevant for the version you're on)
I handle this by popping open the modal on the client side from my main view. The link that pops the modal contains the URL to the controller method that will render the actual contents (list of products, in your case). This controller method should return a partial view.
Modal in my main view:
<div class="modal fade name-of-my-modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content"></div>
</div>
</div>
Link in my main view:
<a class="btn btn-default btn-xs" data-toggle="modal" data-target=".name-of-my-modal" role="button" href="/SomeController/SomeMethodThatReturnsPartialView/234">Show Modal</a>
My controller method for the partial view:
public ActionResult SomeMethodThatReturnsPartialView(int id)
{
var model = GetProducts();
return PartialView("_IndexPartial", model);
}
My partial view that will populate the actual modal contents:
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title">Title goes here</h4>
</div>
<form class="form-horizontal" id="SomeId" name="SomeName" role="form">
<div class="modal-body">
<div>Product 1</div>
<div>Product 2</div>
<div>Product 3</div>
<div>Product 4</div>
</div>
</form>
Also, if the contents of the modal change frequently, or are variable based upon the ID you pass to the partial controller method, then you'll want to clear the modal contents when closing it. From your main view:
$(document).on('hidden.bs.modal', '.modal', function (e) {
// Handles the event thrown when a modal is hidden
$(this).removeData('bs.modal');
$(this).find(".modal-content").empty();
});
Let me know if this helps, and whether anything needs to be clarified.
Problem 2 you could return a JSON result and put the HTML in a string as shown here:
https://www.codemag.com/article/1312081/Rendering-ASP.NET-MVC-Razor-Views-to-String
you could also set a boolean on the returned JSON for whether to redirect.
If it is a redirect do that in Javascript on the success using
window.location
I have a View, Contact, that loads n number of Caller partial views, m number of Child partial views, and one CallNote partial view all loaded via Ajax once the document is ready.
I'm able to add and remove Callers and Children too, so these numbers are not static.
Contact.cshtml, with some stuff removed:
#using Birth_To_Five.ViewModels
#model CallDetailViewModel
<div class="container">
<ul class="nav nav-tabs">
<li class="active">Call Detail</li>
#* Other tabs not shown here *#
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="tab-1">
#using (Html.BeginForm("SubmitCallDetailsAsync", "Home", FormMethod.Post))
{
<div class="well">
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.CallThreadViewModel.Id)
<span style="color: red">
#Html.ValidationSummary()
</span>
#* Call Details *#
<div class="row">
<fieldset>
<legend>Call Details</legend>
</fieldset>
</div>
<div class="row">
<div class="form-group">
#Html.LabelFor(m => m.EnteredByEmail, new { #class = "control-label" })
#Html.ValidationMessageFor(m => m.EnteredByEmail, "", new { #class = "text-danger" })
#Html.TextBoxFor(m => m.EnteredByEmail, new { #class = "form-control", placeholder = "Who took the call" })
</div>
#* Other stuff *#
</div>
#* Caller Details *#
<div class="row">
<fieldset>
<legend>Callers</legend>
</fieldset>
</div>
#* Render each existing caller. Each caller gets their own well to create a visual separation between them. *#
#foreach (var callerViewModel in Model.CallerViewModels)
{
<div class="progress" id="callerLoadingBar-#callerViewModel.Id" data-callerid="#callerViewModel.Id" data-calldetailid="#Model.Id">
<div class="progress-bar progress-bar-striped active" role="progressbar" style="width: 100%">Loading Caller...</div>
</div>
}
<div id="newCaller"></div>
<div class="row">
#* Button to search for and add a caller *#
</div>
#* Children Details *#
<div class="row">
<fieldset>
<legend>Children</legend>
</fieldset>
</div>
#* Render each existing child. Each child gets their own well to create a visual separation between them. *#
#foreach (var childViewModel in Model.ChildViewModels)
{
<div class="progress" id="childLoadingBar-#childViewModel.Id" data-childid="#childViewModel.Id" data-calldetailid="#Model.Id">
<div class="progress-bar progress-bar-striped active" role="progressbar" style="width: 100%">Loading Child...</div>
</div>
}
<div id="newChild"></div>
<div class="row">
#* Button to search for and add a child *#
</div>
<div class="progress" id="callNoteLoadingBar">
<div class="progress-bar progress-bar-striped active" role="progressbar" style="width: 100%">Loading Call Note...</div>
</div>
</div>
<div class="row">
<div class="form-group">
<button class="btn btn-danger" type="reset">Reset</button>
</div>
<div class="form-group">
<button class="btn btn-primary" type="submit">Submit</button>
</div>
</div>
}
</div>
</div>
</div>
#section scripts
{
#Scripts.Render("~/bundles/jqueryval")
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
#Scripts.Render("~/bundles/calldetailscripts")
}
Snippet from my JS script, callDetailFunctions:
$(document).ready(function () {
getCallNote('#callNoteLoadingBar', $('#Id').val());
getAllCallers();
getAllChildren();
});
// function getAllWhatever(){ Foreach loading bar, addCaller/Child/CallNotePartialView(..., ..., ..., etc.); }
function addWhateverPartialView(divToReplace, thingIWantId, callDetailId) {
$.ajax({
url: '/Home/GetWhateverPartialViewAsync',
data: {
thingIWantId,
callDetailId
},
type: "GET",
error: function (xmlHttpRequest, textStatus, errorThrown) {
alert("Request: " + xmlHttpRequest.toString() + "\n\nStatus: " + textStatus + "\n\nError: " + errorThrown);
},
success: function (data) {
$(divToReplace).replaceWith(data);
}
});
}
Here in my HomeController I have the SubmitCallDetailsAsync method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SubmitCallDetailsAsync(CallDetailViewModel callDetailViewModel)
{
using (var unitOfWork = new UnitOfWork(ApplicationDbContext))
{
// Call Details
var callDetailServices = new CallDetailServices();
await callDetailServices.AddOrUpdateCallDetailFromCallDetailViewModelAsync(callDetailViewModel, ModelState, unitOfWork);
// Callers
var callerServices = new CallerServices();
await callerServices.AddOrUpdateCallersFromCallDetailsViewModelAsync(callDetailViewModel, ModelState, unitOfWork);
// Children
var childServices = new ChildServices();
await childServices.AddOrUpdateChildrenFromCallDetailsViewModelAsync(callDetailViewModel, ModelState, unitOfWork);
// Call Note
var callNoteServices = new CallNoteServices();
await callNoteServices.AddOrUpdateCallNoteFromCallDetailsViewModelAsync(callDetailViewModel, ModelState, unitOfWork);
// Check the model state (returns true if it's good, false otherwise.
// Also spits out some debug text for me to tell me what broke the Model)
if (!UtilityServices.CheckModelState(ModelState))
{
callDetailViewModel.DirectionChoices =
await unitOfWork.DirectionChoiceRepo.GetAllAsSelectListItemsAsNoTrackingAsync();
return View("Contact", callDetailViewModel);
}
await unitOfWork.CompleteAsync();
}
return RedirectToAction("Index");
}
The gist of what's happen is that I have a loading bar as a placeholder for each Caller, Child, and the Call Note and then when the document loads I go and get those on $(document).ready()
My problem is that when I submit Contact.cshtml and hit a model validation error I get sent back to my Contact page, which reloads all the Callers, Children, and the Call Note, thus losing all changes.
What should/can I do to handle this scenario?
With deloopkat's comment, I was able to get this working (for the most part)
I changed my Contact page to use Ajax.BeginForm() and added a partialCallDetail id to the section I want to replace with a Partial View result:
#using (Ajax.BeginForm("SubmitCallDetailsAsync", "Home", new AjaxOptions() {HttpMethod = "POST", UpdateTargetId = "partialCallDetail", OnSuccess = "onSuccess"})) #* <----------- Note the UpdateTargetId *#
{
<div class="well">
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.CallThreadViewModel.Id)
<span style="color: red">
#Html.ValidationSummary()
</span>
#* Call Details *#
<div id="partialCallDetail"> #* <------------------ This whole div gets replaced by the Submit function when the Model Validation fails *#
#* All of the same stuff as before in my original post *#
</div>
</div>
<div class="row">
<div class="form-group">
<button class="btn btn-danger" type="reset">Reset</button>
</div>
<div class="form-group">
<button class="btn btn-primary" type="submit">Submit</button>
</div>
</div>
}
#section scripts
{
<script>
function onSuccess(data) {
///<summary>
/// When the Ajax form is submitted, this function gets called with the return data.
/// Determine if it contains a redirectUrl and go there if it does
///</summary>
if (data.redirectUrl !== undefined) {
window.location.replace(data.redirectUrl);
}
}
</script>
}
I created a separate Partial View, _PartialCallDetail, that renders each Caller, Child, and CallNotes PartailView on the spot rather than calling a function via Ajax on $(document).ready()
...
#* Render each existing caller. Each caller gets thier own well to create a visual seperation between them. *#
#foreach (var callerViewModel in Model.CallerViewModels)
{
Html.RenderPartial("_PartialCallerInfo", callerViewModel);
}
...etc.
I then changed my Submit function to this:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SubmitCallDetailsAsync(CallDetailViewModel callDetailViewModel)
{
using (var unitOfWork = new UnitOfWork(ApplicationDbContext))
{
// Call Details
var callDetailServices = new CallDetailServices();
await callDetailServices.AddOrUpdateCallDetailFromCallDetailViewModelAsync(callDetailViewModel, ModelState, unitOfWork);
// Callers
var callerServices = new CallerServices();
await callerServices.AddOrUpdateCallersFromCallDetailsViewModelAsync(callDetailViewModel, ModelState, unitOfWork);
// Children
var childServices = new ChildServices();
await childServices.AddOrUpdateChildrenFromCallDetailsViewModelAsync(callDetailViewModel, ModelState, unitOfWork);
// Call Note
var callNoteServices = new CallNoteServices();
await callNoteServices.AddOrUpdateCallNoteFromCallDetailsViewModelAsync(callDetailViewModel, ModelState, unitOfWork);
// Check the model state
if (!UtilityServices.CheckModelState(ModelState))
{
// Setup all drop downs
callDetailViewModel.DirectionChoices =
await unitOfWork.DirectionChoiceRepo.GetAllAsSelectListItemsAsNoTrackingAsync();
foreach (var callerViewModel in callDetailViewModel.CallerViewModels)
{
await callerServices.SetupSelectListItemsAsync(callerViewModel, unitOfWork);
}
foreach (var childViewModel in callDetailViewModel.ChildViewModels)
{
childViewModel.SexChoices = await unitOfWork.SexChoiceRepo.GetAllAsSelectListItemsAsNoTrackingAsync();
}
// Return the ViewModel with Validation messages
if (Request.IsAjaxRequest()) return PartialView("_PartialCallDetail", callDetailViewModel);
return View("Contact", callDetailViewModel);
}
await unitOfWork.CompleteAsync();
}
return Json(new { redirectUrl = Url.Action("Index", "Home", null) });
}
Now when there is a Model Validation error, I send back my _PartialCallDetail view, which updates the Contact page with the existing data and activates the #Html.ValidationMessageFor(...)s
I think it's important to note that this isn't perfect in it's current state:
I now have two Views, Contact and _PartialCallDetail, that I need to update if I make a design change in the future.
The #Html.ValidationSummary() I have in my View is only for non-Ajax content, so the User's have to sift through the View to see what's wrong.
And a couple other specific-to-my-code issues too, but I'll leave those out.
But I feel pretty good that this is a step in the right direction
Im very new to MVC and messing around. This has been driving me crazy all day, i have been reading around and it seems that i may have some fundamental errors in the structure of my code. But i am struggling to see what they are exactly. As far as i can see i am not even using a child action?
The error is being thrown on the return RedirectToAction line.
I have a registration form that i am displaying in a modal. This modal will be available to see on many pages - so i created an action that returns a partial view.
[AllowAnonymous]
public ActionResult RegisterPartial()
{
return PartialView(new RegisterModel());
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult RegisterPartial(RegisterModel model)
{
if (ModelState.IsValid)
{
//do stuff
return RedirectToAction("Data", "Settings", new { id = model.UserName });
}
// If we got this far, something failed, show form again
return PartialView(model);
}
This is called from my home page at the moment - but will be called from many pages eventually.
<div class="modal fade" id="signUpModal">
#Html.Action("RegisterPartial", "Account")
</div>
SignUp
And here is the partial view itself
#model Prog.Models.RegisterModel
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<h4 class="modal-title">Signup</h4>
</div>
#using (Html.BeginForm())
{
<fieldset>
<div class="modal-body">
<p class="message-info">
</p>
#Html.ValidationSummary()
#Html.AntiForgeryToken()
<ol>
<li>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName, new { #maxlength = "13" })
#Html.ValidationMessageFor(m => m.UserName)
</li>
<li>
#Html.LabelFor(m => m.email)
#Html.TextBoxFor(m => m.email)
#Html.ValidationMessageFor(m => m.email)
</li>
<li>
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password)
</li>
<li>
#Html.LabelFor(m => m.ConfirmPassword)
#Html.PasswordFor(m => m.ConfirmPassword)
</li>
#Html.ValidationMessageFor(m => m.ConfirmPassword)
</ol>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Post</button>
</div>
</fieldset>
}
</div></div>
Any help would be most appreciated! Thanks in advance :)
Turns out it was because i didnt fill out the html begin form statement correctly.
#using (Html.BeginForm("RegisterPartial", "Account", FormMethod.Post , new { #class = "form-horizontal" }))
this works now.
The problem is your POST action is returning PartialView if modelstate is invalid. So the partial view will show as entire page instead of model. So you can go with ajax forms. There is Ajax Helper available for MVC.
Include below script in your page.
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
Your view will be
#using (Ajax.BeginForm("RegisterPartial", "Account", new AjaxOptions() {
OnSuccess = "successCallback"})
{
}
<script>
function successCallback(response)
{
var username='#Model.UserName';
if(response)
{
windows.href.location='/Settings/Data/'+username;
}
}
</script>
Your action will be
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public JsonResultRegisterPartial(RegisterModel model)
{
if (ModelState.IsValid)
{
//do stuff
return Json(new {Success=true });
}
return Json(new {Success=false});
}
It might be helpful.
I've got a controller in MVC5 for an apply form. There is a GET to return the view and a POST to take the form data from the application and process it:-
public ActionResult Apply(string jobReference)
{
Apply form = new Apply();
Session[Settings.JOBREFERENCE] = jobReference;
return View(form);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Apply(Apply form)
{
if (ModelState.IsValid)
{
}
}
When I press the to submit the form, it calls the GET controller instead of the the POST controller, despite it having [HttpPost] on the latter.
If I actively renamed the HTTPPost controller and navigate to it, I see a Server Error, with a message saying it cannot be found. Submitting the form causes the page to crash.
Form Code
#model Salt.Models.Apply
<div class='applyForRoleExt popupBox'>
<img src="/img/closePopup.png" alt="close" />
<div class="innerContainer">
#using (Html.BeginForm("Apply", "Form", FormMethod.Post, new { enctype = "multipart/form-data", #id = "ApplyForm" }))
{
#Html.AntiForgeryToken()
<h6>Apply for this role</h6>
#*<div class="haveAccount">
<span>Have an account? Apply quickly by logging in now</span>
Login to my account
</div>*#
<div class="leftCol">
<label>Name<span>*</span></label>
<div class="inputBox">
#Html.TextBoxFor(m => m.Name)
</div>
<label>Email Address<span>*</span></label>
<div class="inputBox">
#Html.TextBoxFor(m => m.EmailAddress)
</div>
<label>Telephone Number<span>*</span></label>
<div class="inputBox">
#Html.TextBoxFor(m => m.TelephoneNumber)
</div>
<label>Portfolio Link<span>*</span></label>
<div class="inputBox">
#Html.TextBoxFor(m => m.PortfolioLink)
</div>
</div>
<div class="rightCol">
<label>CV Upload</label>
<div class="inputBox">
#Html.TextBoxFor(m => m.CV, new { #type = "file" })
</div>
<label>Covering Note</label>
<div class="inputArea">
#Html.TextAreaFor(m => m.CoveringNote)
</div>
</div>
<div class="actions">
<p class="terms">By submitting this form you consent to Salt Recruitment processing your personal data in accordance with our privacy policy and agree to future contact material.</p>
<!--<button class="submit">Apply Now</button>-->
<input type="submit" name="submit" value="Apply Now" />
</div>
}
</div>
</div>
<script type="text/javascript">
$(document).ready(function () {
$.validator.unobtrusive.parse("#ApplyForm");
});
</script>
Thanks,
Mike.
It's been fixed. It took a second pair of eyes but a rule in the web.config was removing the trailing slash from the form post URL and it was defaulting to loading the GET. No idea why that would cause the problem but it did.
I used modal in my edit page and here is my code:
In the View
<button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">
Edit
</button>
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Edit Event Information</h4>
</div>
<div class="modal-body">
#Html.Action("Edit", "Admin",new { id = Model.events_info_id,#class = "btn btn-warning"})
</div>
</div>
</div>
</div>
This the controller to get the information to be display in the modal:
[ChildActionOnly]
public ActionResult Edit(int id = 0)
{
Events_Info_tbl events_info_tbl = db.Events_Info_tbl.Find(id);
if (events_info_tbl == null)
{
return HttpNotFound();
}
return PartialView(events_info_tbl);
}
This the View of the content of the Modal:
#model Online_Ballot.Models.Events_Info_tbl
<script src="~/Scripts/Datepicker.js"></script>
<div class="modal-body" style="color:green">
<h2>Edit Events</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.events_info_id)
<div class="form-group">
<label for="usr">Event Name:</label>
#Html.EditorFor(model => model.events_name, new { #class="form-control"})
#Html.ValidationMessageFor(model => model.events_name)
</div>
<div class="form-group">
<label for="usr">Votation Date:</label>
#Html.EditorFor(model => model.events_votation_date, new { #id="voters_bdate"})
#Html.ValidationMessageFor(model => model.events_votation_date)
</div>
<div class="form-group">
<label for="usr">Votation Place:</label>
#Html.EditorFor(model => model.events_place, new { #class="form-control"})
#Html.ValidationMessageFor(model => model.events_place)
</div>
<div class="form-group">
<label for="comment">Event Description:</label>
#Html.TextAreaFor(model => model.events_desc)
</div>
<div class="form-group">
<label for="usr">Is active:</label>
#Html.EditorFor(model => model.is_active, new { #class="form-control"})
#Html.ValidationMessageFor(model => model.is_active)
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save changes</button>
</div>
}
This Controller will update the data:
// POST: /Admin/Edit/5
[ValidateAntiForgeryToken]
public ActionResult Edit(Events_Info_tbl events_info_tbl)
{
if (ModelState.IsValid)
{
db.Entry(events_info_tbl).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(events_info_tbl);
}
When I try to run this code I got this error:
Child actions are not allowed to perform redirect actions.
But, it updates the data, I guess it not allowed to call RedirectToAction function but I need to. How I am going to fix this one?
Your child action shouldn't be attempting to update anything. It should be getting data, populating something, and returning some view.
You probably want to split your action into two actions. One for rendering the "Edit" form and another for updating that form.
[HttpPost]
public ActionResult Edit(Events_Info_tbl events_info_tbl)
{
if (ModelState.IsValid)
{
//Save
return RedirectToAction("Index");
}
//Return view
//NOTE: Make sure your page state is preserved i.e. your modal is open
return View(events_info_tbl);
}
[ChildActionOnly]
public PartialViewResult RenderEditForm(int id)
{
//Build form data
return PartialView("_EditForm");
}
Another note, you have some collision here to how the "Editor" and "Partial" razor helpers can work. Child actions are good for views where it doesn't make sense to fit the data into the controllers model. If the model fits then just use "Editor" or "Partial"
UPDATE
This...
#Html.Action("Edit", "Admin",new { id = Model.events_info_id,#class = "btn btn-warning"})
appears to be pointing to this...
public ActionResult Edit(Events_Info_tbl events_info_tbl)
when you actually want it to point to this...
public ActionResult Edit(int id = 0)
edit the controller accordingly in Html.Action
#Html.Action("Edit", "MyController",new { id = Model.events_info_id,#class = "btn btn-warning"})