I'm using partial pages within modal dialogues to handle various CRUD actions elsewhere on a website and it's working fine, I have done everything the same with the login (shown below), but the HttpPost actionresult in the controller is not getting hit and I'm not sure why.
Instead the HttpGet is being called.
What have I missed?
Login partial
#model Models.User
<script language="Javascript" type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script language="Javascript" type="text/javascript" src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<script type="text/javascript">
$(function () {
$('#btn-login').click(function () {
$('#modal-container').modal('hide');
});
$('#btn-close').click(function () {
$('#modal-container').modal('hide');
});
});
</script>
<div class="login-container">
#using (Html.BeginForm())
{
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="login-table">
<div>
#Html.LabelFor(m => m.Username)
#Html.TextBoxFor(m => m.Username)
#Html.ValidationMessageFor(m => m.Username, "", new { #class = "text-danger" })
</div>
<div>
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password)
#Html.ValidationMessageFor(m => m.Password, "", new { #class = "text-danger" })
</div>
</div>
<div>
<button type="submit" id="btn-login" class="btn btn-sm">Login</button>
<button type="button" class="btn btn-default modal-close-btn" data-dissmiss="modal">Cancel</button>
</div>
}
</div>
Account Controller Login Action Methods
[AllowAnonymous]
[HttpGet]
public ActionResult Login()
{
return PartialView("Login");
}
[HttpPost]
public ActionResult Login(User model)
{
if(ModelState.IsValid)
{
// do stuff
}
return RedirectToAction("Index", "Announcements");
}
Your GET method is marked with [AllowAnonymous] suggesting your controller is marked with [Authorize], which means that you POST method must also be marked with [AllowAnonymous] (the user is authorized until the code in this method is executed)
[HttpPost]
[AllowAnonymous]
public ActionResult Login(User model)
{
if(ModelState.IsValid)
{
// do stuff
}
return RedirectToAction("Index", "Announcements");
}
Try this in form attribute
#Html.BeginForm("Login", "Home", , new { modal= Model }, FormMethod.Post)
//Login- action Name that has to be hit
//Home - Controller name
Related
I have an old MVC website I'm looking at. I don't understand the following code in my Account controller. (I believe this was code generated by ASP.NET.)
Controller
//
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
return code == null ? View("Error") : View();
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
AddErrors(result);
return View();
}
View
#model OnBoard101.Models.ResetPasswordViewModel
#{
ViewBag.Title = "Reset password";
}
<h2>#ViewBag.Title</h2>
#using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Reset your password</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Code)
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Password, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.ConfirmPassword, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.ConfirmPassword, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-success" value="Reset" />
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
These methods are called when the user uses the Forgot Password feature and then clicks the link that is sent to them in an email.
As best I can tell, the POST handler correctly detects an invalid code in the link (it produces an Invalid Token error when the code is not value). But I don't understand how. The GET handler seems to simply discard the code argument.
I don't get how this code works. How does model.Code ever get populated?
The ResetPassword feature does use code from the link.
Model binding retrieves data from various sources such as route data, form fields, and query strings.
It inspects the query string parameters for matching properties on the model used by the view
While it may appear like the code is being discarded by the Controller GET action, the code is still a part of the request and used by the view.
And since the view explicitly binds to a model
#model OnBoard101.Models.ResetPasswordViewModel
which has a matching public Code property (case-insensitive)
public string Code { get; set; }
it will bind it to the model in the view during the GET and then use it (the model) to populate a hidden form field (as shown in your View markup)
#Html.HiddenFor(model => model.Code)
So now when the form is submitted, the POST action will bind that field to the model posted back to the action and perform the validation
The same could have been achieved with
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code) {
if (code == null) return View("Error");
var model = new ResetPasswordViewModel {
Code = code
};
return View(model);
}
But since the built-in model binding functionality would initialize a model if one was not provided, the above code really does not add anything additional to what the framework does out-of-the-box.
Can anyone tell me how to read the value of checked radio button from my View and save this value in database attribute type_of_user. the following is part of the view:
<div class="form-group">
<div class="control-label col-md-2">
Account Type: </div>
<div class="col-md-10">
#Html.RadioButton("AccountType", "Individual", true) Individual
#Html.RadioButton("AccountType", "Organization", false) Organization
#Html.ValidationMessageFor(model => model.Type_of_user)
</div>
</div>
and in The controller I placed this code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include="Username,Password,Status,Email,Type_of_user,admin_id")] Account account)
{
if (ModelState.IsValid)
{
db.Accounts.Add(account);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.admin_id = new SelectList(db.Adminstrators, "Admin_ID", "Phone", account.admin_id);
return View(account);
}
I have an LogInOrRegister page in my application, that calls 2 child actions
LogInOrRegister.cshtml
#{
ViewBag.Title = "Log in";
}
#Html.Action("Login", "Account", new { returlUrl = ViewBag.ReturnUrl})
#Html.Action("Register", new { returlUrl = ViewBag.ReturnUrl})
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
The Login PartialView is:
#model Com.WTS.Portal.Models.LoginModel
<hgroup class="title">
<h1>#ViewBag.Title</h1>
</hgroup>
<section id="loginForm">
<h2>Use a local account to log in.</h2>
#using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl })) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Log in Form</legend>
<ol>
<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)
#Html.ValidationMessageFor(m => m.Password)
</li>
<li>
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe, new { #class = "checkbox" })
</li>
</ol>
<input type="submit" value="Log in" />
</fieldset>
}
</section>
My AccountController.cs contains the following code:
[AllowAnonymous]
public PartialViewResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return PartialView();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public PartialViewResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.Email, model.Password, persistCookie: model.RememberMe))
{
RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return PartialView(model);
}
I see correctly the 2 partial view the I GET the page LogInOrRegister.cshtml
When I submit the form, if there is validation errors in the form, the view is displayed (no layout) instead of the partial view that should be part of the LogInOrRegster
Any idea ?
Ok, I found te solution. By passing route parameters to the partial view form :
#using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl }))
I think we change the behaviour of the child action. Only by removing the route attribute :
#using (Html.BeginForm())
The PartialView is rendered in its container. Also, we can define the POST action as ChildActionOnly, it still work:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[ChildActionOnly]
public PartialViewResult Login(LoginModel model, string returnUrl)
So if you look at the discussion about found here https://stackoverflow.com/a/10253786/30850 you will see that the ChildActionOnlyAttribute is used so that an Action can be rendered inside of a view but can not be served to the browser.
If the return type is PartialViewResult,specify the partialview name along with model in the method public PartialViewResult Login(LoginModel model, string returnUrl) otherwise use ActionResult as return type.ActionResult is an abstract class where as PartialViewResult is a subclass.
So here, I need to add comment for video in one view.
I have the main view code like this that displaying video and comment for that video.
<!-- language: C# -->
#model AzureMediaPortal.ViewModels.ViewModelWatch
#{
ViewBag.Title = "Watch";
}
<div id="videoPlayer">
</div>
<h2>#Html.DisplayFor(model => model.media.Title)</h2>
<h3> By #Html.DisplayFor(model => model.media.UserId) at #Html.DisplayFor(model => model.media.UploadDate) </h3>
#Html.HiddenFor(model => model.media.Id)
#Html.HiddenFor(model => model.media.AssetId)
#Html.HiddenFor(model => model.media.FileUrl, new { id = "fileUrl" })
<div class="display-label" style="font-weight:bold">
#Html.DisplayNameFor(model => model.media.Description)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.media.Description)
</div>
<br />
<div class="display-label" style="font-weight:bold">
#Html.DisplayName("Category")
</div>
<div class="display-field">
#Html.DisplayFor(model => model.media.Category.CategoryName)
</div>
<h3>Comments</h3>
#foreach (var item in Model.comment)
{
<div class="display-label" style="font-weight:bold">
#item.UserId
</div>
<div class="display-field">
#item.Content
</div>
}
#Html.Partial("Post",new AzureMediaPortal.ViewModels.ViewModelWatch())
#section Scripts {
<script src="~/Scripts/playerframework.min.js"></script>
<script src="~/Scripts/media-player.js"></script>
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
mediaPlayer.initFunction("videoPlayer", $("#fileUrl").val());
</script>
}
and this the partial view
#model AzureMediaPortal.ViewModels.ViewModelWatch
#{
ViewBag.Title = "Post";
}
<h2>Add Comment</h2>
#Html.HiddenFor(model => model.cmnt.MediaElement.Id)
#using (Html.BeginForm("Post","Home",FormMethod.Post)) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Add Comment</legend>
<div class="editor-label" style="font-weight:bold">
#Context.User.Identity.Name
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.cmnt.Content)
#Html.ValidationMessageFor(model => model.cmnt.Content)
</div>
<p>
<input type="submit" value="Post" />
</p>
</fieldset>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
this is my ViewModel
public class ViewModelWatch
{
public MediaElement media { get; set; }
public List<Comment> comment { get; set; }
public Comment cmnt { get; set; }
}
and this is my controller
public ActionResult Watch(int id)
{
ViewModelWatch vm = new ViewModelWatch();
vm.media = _repository.GetMedia(id);
vm.comment = _repository.GetMediaComment(id);
return View(vm);
}
public ActionResult Post()
{
return View();
}
[HttpPost]
public ActionResult Post(Comment comment, int id)
{
if (ModelState.IsValid)
{
comment.UserId = User.Identity.Name;
comment.MediaElement.Id = id;
db.Comments.Add(comment);
db.SaveChanges();
return RedirectToAction("Watch");
}
return View();
}
I need to pass data from partial view and save it to database include the media.Id to know that comment inserted for the video.
Thanks so muchhh
putting scripts on a partial view is generally bad practice. Script gets inserted into the middle of the view. For saving information from a partial view I use ajax calls. To do that add a class to your post button
<input type="button" class="btnSubmit" value="Post" />
then in your script on the main page
$(document).on('click', '.btnSubmit', function(){
$.ajax({
url: '#Url.Action("Action", "Controller")',
cache: false,
async: true,
data: {
//put the data that you want to save from the partial here
id: $('#hiddenID').val(),
content: $('#Content').val()
},
success: function (_result) {
//can add something here like an alert, close a popup something along those lines
}
});
});
just make sure the inputs on your controller match exactly the names that you have defined here
[HttpPost]
public ActionResult Action(int id, string content){
//database call to save the fields
//see here for an example of returning json http://stackoverflow.com/questions/1482034/extjs-how-to-return-json-success-w-data-using-asp-net-mvc
return Json(json);
}
I would suggest using #Html.Action and get the create form from there. Also, once the comments are stored you can redirect to the parent action. That should automatically update the comments list.
But that would be a bad idea.
Rather you can put get your comments by calling action from ajax ($.ajax) and use pure.js to replace the older ones with the new comment list.
I need to implement multiple Ajax forms.
html
#using (Ajax.BeginForm("PerformAction", "Home", new AjaxOptions { OnSuccess = "OnSuccess", OnFailure = "OnFailure" }))
{
<h3>This is a demo form #1.</h3>
#Html.LabelFor(model => model.FirstName)
#Html.TextBoxFor(model => model.FirstName, null, new { #class = "labelItem" })
<input type="submit" value="Submit" />
}
<br />
<br />
#using (Ajax.BeginForm("PerformAction2", "Home", new AjaxOptions { OnSuccess = "OnSuccess2", OnFailure = "OnFailure2" }))
{
<h3>This is a demo form #2. </h3>
#Html.LabelFor(model => model.FirstName)
#Html.TextBoxFor(model => model.FirstName, null, new { #class = "labelItem" })
<input type="submit" value="Submit" />
}
<br />
<br />
<script type="text/javascript">
function OnSuccess(response) {
alert(response);
}
function OnFailure2(response) {
alert("222 Whoops! That didn't go so well did it?");
}
function OnSuccess2(response) {
alert(response);
}
function OnFailure(response) {
alert("Whoops! That didn't go so well did it?");
}
</script>
C#
[AcceptVerbs("POST")]
public ActionResult PerformAction(FormCollection formCollection, Person model)
{
model.FirstName += " Form 1";
return View(model);
}
[AcceptVerbs("POST")]
public ActionResult PerformAction2(FormCollection formCollection, Person model)
{
model.FirstName += " Form 2";
return View(model);
}
But I am facing the error about that ASP .NET MVC 4 is looking for some kind of partial form.
I don't want to use any partial form and just I need to get working JavaScript OnSuccess and OnSuccess2
Any guess how it could be done?
Using View() or PartialView(), MVC it will try to find a view with a name equals to the action name.
So, just don't return a View(model)
Return a EmptyResult() or a JSON object.