1
votes

So my partial view looks like -

@model Example.Models.ForgotPasswordViewModel

@Html.ValidationSummary("", new { @class = "text-danger" })
<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 style="font-size: 16px; text-align:center; margin-top:10px;">
    @Html.ActionLink("Email Link", "ForgotPassword", "Account")
</div>

as part of asp.net's out of the box ForgotPassword view, but I turned it into a partial view. The original view was wrapped in "Html.BeginForm(..", but I obviously can't do that. (It results in a form inside a form)

How do I get it to call the Account controller's ForgotPassword method that expects a model by passing the declared @model?

Html generated by @Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) :

<input class="form-control" id="Email" name="Email" type="text" value="" />

signature for your AccountController.ForgetPassword(..) :

//
// POST: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = await UserManager.FindByNameAsync(model.Email);
        if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
        {
            // Don't reveal that the user does not exist or is not confirmed
            return View("ForgotPasswordConfirmation");
        }

        // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
        // Send an email with this link
        string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
        var callbackUrl = Url.Action(
            "ResetPassword", "Account", 
             new { userId = user.Id, code = code }, 
             protocol: Request.Url.Scheme);

        await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>");
        return View("ForgotPasswordConfirmation");
    }

    // If we got this far, something failed, redisplay form
    return RedirectToAction("Index", "Home");
}

Also as Erik pointed out, need to make sure fields are uniquely identified within the partial view. The post had Email twice.

2
You can only POST to a controller using a form or ajax (IE ActionLink does not submit a form, it just creates a hyperlink). So your options are to move this partial outside the form, or use javascript/ajax to change the forms destination, and submit the form (but be careful because now you have additional form elements that may conflict with each other on the same form). - Erik Philips
@ErikPhilips How would I use AJAX to change the form's destination? - duckmike
Sorry that was a typo, it should have read or use javascript/jquery to change.... - Erik Philips
Ok.. @ErikPhilips How would I use jQuery to change the form's destination? :) - duckmike
You cant use ajax to change the form destination, but you can use ajax to manually post the values of the form to any actionResult or API. This wouldn't be very graceful however. - Botonomous

2 Answers

3
votes

@ErikPhilips How would I use jQuery to change the form's destination? :)

I would change the Action link to a button:

@Html.ValidationSummary("", new { @class = "text-danger" })
<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 style="font-size: 16px; text-align:center; margin-top:10px;">
    <input type="button" 
            value="Email Link" 
            class="change-form-url"
            data-url="@Url.Action("ForgotPassword", "Account")"/>
</div>

Jquery:

$(document).ready(funciton()
{
  $('.change-form-url').on('click', function()
  {
    var url = $(this).data('url');
    var form = $(this).closest('form');
    form.prop('action', url);
    form.submit();
  });
});

I have not tested this, it should be very close to what you want.

0
votes

BeingForm has some overloads:

@using (Html.BeginForm("ActionName","Controller"))
{
    @Html.TextBox("Name");
    @Html.Password("Password");
    <input type="submit" value="Sign In">
}

Add the action name and controller name into the beginForm call and you can override where the Post is by default.

I suggest you call your partial view outside the main form so you will have two forms on one page (not nested). Then you can point the forgotpassword model to the correct controller method.