0

I am having the below PaymentInformationModel Class. Which is having a complex type CreditCardDetailModel. When I submit my form the CreditCartDetail property remain empty, I am expecting it to be filled with all the details entered by the user. Do I need to do custom binding or there is a default binding trick which I am missing.

PaymentInformationModel

 public class PaymentInformationModel
{

    public string PaymentAmount { get; set; }


    public string TransactionReference { get; set; }

    public string Description { get; set; }
    public CreditCardDetailModel CreditCardDetail{get;set;}

}

CreditCardDetailModel

public class CreditCardDetailModel
    {


        public string CardNumber { get; set; }


        public string Name { get; set; }


        public string ExpiryDate { get; set; }



        public int CardSecurityCode { get; set; }

        public CreditCardType CardType { get; set; }


    }

VIEW

    @model PaymentInformationModel

@using (Html.BeginForm("", "Payment", FormMethod.Post, new { Id = "Form1", @class = "form-horizontal" }))
{
    <div class="container">
        <div class="panel panel-default">
            <div class="panel-heading">Payment Information</div>
            <div class="panel-body">
                <div class="form-group">
                    @Html.LabelFor(x => x.PaymentAmount, new { @class = "control-label col-sm-2" })
                    <div class="input-group col-sm-3">
                        <span class="input-group-addon">$</span>
                        @Html.TextBoxFor(m => m.PaymentAmount, new { @class = "form-control col-sm-10" })
                    </div>
                    @Html.ValidationMessageFor(m => m.PaymentAmount, "", new { @class = "help-block" })
                </div>
                <div class="form-group">
                        @Html.LabelFor(m => m.TransactionReference, new { @class = "control-label col-sm-2" })
                        @Html.TextBoxFor(t => t.TransactionReference, new { @class = "form-control col-sm-10" })
                </div>
                <div class="form-group">
                    @Html.LabelFor(l => l.Description, new { @class = "control-label col-sm-2" })
                    @Html.TextAreaFor(t => t.Description, new { @class = "form-control col-sm-10"  })
                </div>

            </div>
        </div>
       @Html.Action("CreditCardDetail")
        <p class="log"></p>

    </div>
    <button type="submit" name="btnSubmit" id="btnSubmit" class="btn btn-success">PAY</button>
}

Controller

[HttpPost]
        public ActionResult Index(PaymentInformationModel model)
        {
            if (ModelState.IsValid)
            {
                return View();
            }
            return View();
        }



 public PartialViewResult CreditCardDetail()
        {
            return PartialView("CreditCardDetail_Partial");
        }
8
  • Your CreditCardDetail property has a getter only. The DefaultModelBinder cannot set it. Change the property to public CreditCardDetailModel CreditCardDetail { get; set; } Commented Jul 8, 2016 at 1:00
  • Why does your CreditCardDetailModel have a property for another CreditCardDetailModel? Commented Jul 8, 2016 at 1:02
  • I have updated my code. Commented Jul 8, 2016 at 1:16
  • What does @Html.Action("CreditCardDetail") return? (show the relevant code) - Unless its generating the correctly prefixed name attributes, then it will not bind Commented Jul 8, 2016 at 1:19
  • 1
    @maxspan, Does it generate form controls with name="CreditCardDetail.CardNumber", name="CreditCardDetail.ExpiryDate" etc. And why are you using @Html.Action() for this. The correct approach is to use an EditorTemplate for typeof CreditCardDetailModel Commented Jul 8, 2016 at 1:24

1 Answer 1

1

Your use of @Html.Action() is calling a controller method that returns a partial view of CreditCardDetailModel which will generate inputs with name attributes such as

<input name="CardNumber" .... />
<input name="ExpiryDate" .... />

but in order to bind to your model, they need to be

<input name="CreditCardDetail.CardNumber" .... />
<input name="CreditCardDetail.ExpiryDate" .... />

Its unclear why your using @Html.Action() to call a server method that contains no logic, and you could just have easily use @Html.Partial("CreditCardDetail_Partial"), although that will still result in the same incorrect name attribute.

You need to use an EditorTemplate for typeof CreditCardDetailModel. Move your CreditCardDetail_Partial.cshtml file to the /Views/Shared/EditorTemplates folder and rename it to CreditCardDetailModel.cshtml (i.e. to match the name of the class). Then in the view, use

@Html.EditorFor(m => m.CreditCardDetail)

which will generate the correct name attributes for binding to your model.

Sign up to request clarification or add additional context in comments.

4 Comments

Actually I already created a ParitalView for CreditCardDetail model. And so I binded my parital view with its parent model i.e PaymentInformationModel which have CreditCardDetailModel property and I binded all my controls like this. @Html.TextBoxFor(m => m.CreditCardDetail.CardNumber, new { @class="form-control", placeholder= "Card Number" }). Also I wanted a customised look and feel for my view so I didnot used EditorFor, But definitely its a good option.
Using EditorFor() has nothing to do with a customised look and feel for my view (you can style it however you want)
so I can I used my Partialview in EditorFor.
Yes. Just rename it and move it to the EditorTemplates folder as per my answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.