ErocM
ErocM

Reputation: 4662

AddModelError Not Being Passed Back to Controller

I am using the asp.net Razor with C#. I am trying to verify that a value entered is a currency but I can't seem to do it correctly.

This is in my PaymentModel:

  [Required]
  [DataType(DataType.Currency)]
  [DisplayFormat(DataFormatString = "{0:F2}", ApplyFormatInEditMode = true)]
  [Display(Name = "Payment Amount:")]
  public decimal Amount { get; set; }

This is my Prepayment view:

@model SuburbanCustPortal.Models.PaymentModel.PrePayment

@{
    ViewBag.Title = "Make a payment!";
}

<script>
$(function(){
  $("#AccountId").change(function(){
    var val=$(this).val();
    $("#currentBalance").load("@Url.Action("GetMyValue","Payment")", { custid : val });
    document.forms[0].Amount.focus();
  });
});
</script>

<h2>Make a Payment</h2>

  @using (Html.BeginForm("SendPayment", "Payment", FormMethod.Post))
  {
    @Html.ValidationSummary(true, "Please correct the errors and try again.")
    <div>
      <fieldset>
        <legend>Please enter the amount of the payment below:</legend>

        <div class="editor-label">
          Please select an account.
        </div>

        @Html.DropDownListFor(x => x.AccountId, (IEnumerable<SelectListItem>)ViewBag.Accounts)

        <div class="editor-label">
          @Html.LabelFor(m => m.AccountBalance)
        </div>
        <div class="editor-field">
          <label class="sizedCustomerDataLeftLabel" id="currentBalance">@Html.DisplayFor(model => model.AccountBalance)&nbsp;</label>
        </div>      

        <div class="editor-label">
          @Html.LabelFor(m => m.Amount)
        </div>
        <div class="editor-field focus">
          @Html.TextBoxFor(m => m.Amount, new { @class = "makePaymentText" })
          @Html.ValidationMessageFor(m => m.Amount)
        </div>

        <p>
          <input id="btn" class="makePaymentInput" type="submit" value="Pay Now"  onclick="DisableSubmitButton()"/>  
        </p>
      </fieldset>
    </div>
  }

This is my Prepayment ActionResult:

    [Authorize]
    public ActionResult PrePayment(PaymentModel.PrePayment model)
    {
      var list = new List<SelectListItem>();
      var custs = _client.RequestCustomersForAccount(User.Identity.Name);
      foreach (var customerData in custs)
      {
        var acctno = customerData.Branch + customerData.AccountNumber;
        var acctnoname = string.Format(" {0} - {1} ", acctno, customerData.Name);
        // msg += string.Format("*** {0} - {1} ***{2}", customerData.AccountId, acctnoname, Environment.NewLine);
        list.Add(new SelectListItem() { Text = acctnoname, Value = customerData.AccountId });
      }

      if (custs.Length > 0)
      {
        model.AccountBalance = String.Format("{0:C}", Decimal.Parse(custs[0].TotalBalance));
      }

      ViewBag.Accounts = list;
      return View(model);
    }

The post of the view calls SendPayment and this was my check at the begining of the view:

    if (model.Amount == 0)
    {    
      ModelState.AddModelError("Amount", "Invalid amount.");
      return RedirectToAction("PrePayment", model);
    }

I can't seem to get the PrePayment to get my error back that I sent from AddModelError. I changed it to:

    if (model.Amount == 0)
    {    
      ModelState.AddModelError("Amount", "Invalid amount.");
      return View("PrePayment", model);
    }

But it never calls the controller and the screen errors out since it doesn't have the data it is expecting.

How do I redirect back to the calling view with the errors?

==== ADDITIONAL INFO ====

Here is my PrePayment View:

[Authorize]
public ActionResult PrePayment(PaymentModel.PrePayment model)
{
    var list = new List<SelectListItem>();
    var custs = _client.RequestCustomersForAccount(User.Identity.Name);
    foreach (var customerData in custs)
    {
      var acctno = customerData.Branch + customerData.AccountNumber;
      var acctnoname = string.Format(" {0} - {1} ", acctno, customerData.Name);
      // msg += string.Format("*** {0} - {1} ***{2}", customerData.AccountId, acctnoname, Environment.NewLine);
      list.Add(new SelectListItem() { Text = acctnoname, Value = customerData.AccountId });
    }

    if (custs.Length > 0)
    {
      var amt =String.Format("{0:C}", Decimal.Parse(custs[0].TotalBalance));
      model.AccountBalance = amt;
      decimal namt;
      if (decimal.TryParse(amt.Replace(",",string.Empty).Replace("$", string.Empty), out namt))
      {
        model.Amount = namt;
      }
    }
  ViewBag.Accounts = list;
  return View(model);
}

Upvotes: 0

Views: 239

Answers (2)

Jack
Jack

Reputation: 2660

There are several issues needs to be addressed.

1.  return View("PrePayment", model);  
This will not call the controller, as the function name suggests, it only passing your object to the specified "View"(.cshtml file)

2.     return RedirectToAction("PrePayment", model);  
You will not persist modelstate data, because you are doing a redirect.

Suggested workflow which will solve your issue. At least it solved mine.

1. Get the form to post to "PrePayment" instead of SendPayment and you will create a new method with the following signature and have all you validation logic in the method
[Authorize]
[HttpPost]
public ActionResult PrePayment(PaymentModel.PrePayment model)

2. If everything goes well then redirect to the success/send payment page depending on your requirement

3. If somehow you need to pass model object onto the next action. Use TempData like following. This will temporarily persist the data till the next action. Then it get disposed:
TempData["payment"]=model;

Upvotes: 2

Shyju
Shyju

Reputation: 218832

May be you should add a data annotation to your property and use ModelState.IsValid property to validate it

[Required]
[DataType(DataType.Currency)]
[DisplayFormat(DataFormatString = "{0:F2}", ApplyFormatInEditMode = true)]
[Display(Name = "Payment Amount:")]
[Range(0.01, Double.MaxValue)]
public decimal Amount { get; set; }

In your POST method, check whether the validation is passed, if not send the model back to the view.

[HttpPost]
public ActionResult SendPayment(PrePayment model)
{
  if(ModelState.IsValid)
  {
     //do the fun stuff
  }
  return View(model);
}

Upvotes: 0

Related Questions