0

I have this View Model:

public class MemberRegisterVM
{
    public Users User { get; set; }
    public Location Location { get; set; }
}

Which Consists from User and Location Models

User:

public partial class Users : Persons
{
    public Guid? UserRoleId { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public DateTime? RegisterDate { get; set; }
    public DateTime ExpireDate { get; set; }
    public bool IsLock { get; set; }
    public string DigitalSignture { get; set; }
    public Guid? BranchID { get; set; }
    public int PersonType { get; set; }
    public Guid Token { get; set; }
}

public partial class Users
{
    public string NewPassword { get; set; }
    public string ConfirmPassword { get; set; }
    public string ConfirmDigitalSignture { get; set; }

}

Location :

   public  class Location
    {
        public int ID { get; set; }
        public int ParentId { get; set; }
        public int TypeId { get; set; }
        public int Code { get; set; }
        public string Name { get; set; }
    }

For Single Model [for example: User Model] i know if I use a Standard html input like below it'll send my data to controller as the referenced Model based on Its Name attribute:

View:

@model User

<input name="UserName" Id="UserName" type="text" />

Controller:

public ActionResult Submit(User user)
{
      //user.UserName is filled here 
}

Now my question is how to name my Standard HTML elements to send the data to controller as my view model (which consists several models)? using html.helper i will reference it as below for example:

@Html.TextBoxFor(x => x.User.UserName)

Will it be like below?

<input type="Text" name="User.UserName" id="User.UserName">

Or something else? because I've tried above but it still sends value as null on form submit.

Update:

I've excluded other fields on the form, they are based on html.helpers and pass the value correctly, however there are 2 fields that are not working right one would be Gender and second would be BirthDate, since User Model inherits from Person model i'll add it here as well:

Person Model:

public class Persons
{
    public bool Gender { get; set; }
    public Datetime BirthDate { get; set; }
}

Here is my View

@model Models.MemberRegisterVM

@{
    Layout = "~/Views/Shared/_SiteLayout.cshtml";
}
@using Entities.Texts;
@using (@Html.BeginForm("Register", "MembershipAuth", FormMethod.Post))
{
    <div class="form-horizontal">
        <div class="col-sm-6">
                <div class="col-sm-12">
                    <div class="control-label col-sm-2">
                        @Labels.Gender
                    </div>
                    <div class="col-sm-5">
                        <input name="User.Gender" type="radio" id="men_gender" value="0" />
                        <label for="men_gender">@Labels.Male</label>
                    </div>
                    <div class="col-sm-5">
                        <input name="User.Gender" type="radio" id="female_gender" value="1" />
                        <label for="female_gender">@Labels.Female</label>
                    </div>
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-12">
                    <div class="control-label col-sm-2">
                        @Labels.BirthDay
                    </div>
                    <div class="col-sm-10">
                        <input type="text" name="User.BirthDate" id="User_BirthDate" />
                    </div>
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-12">
                    <div class="g-recaptcha" data-sitekey="6LfLWCkUAAAAAPSp-Fr8bp5egiX8Pw7oKxzZbkmk" style="text-align: center"></div>
                </div>
            </div>
            <div class="form-group">
                <input type="submit" class="btn btn-primary" value="Sign Up" />
            </div>
        </div>
    </div>

}

And This is my ActionResult on controller:

[HttpPost]
[AllowAnonymous]
public ActionResult Register(Models.MemberRegisterVM vm, FormCollection form)

{
   //vm.User.Gender is not filled here as well as vm.User.BirthDate
}
Masoud Andalibi
  • 3,168
  • 5
  • 18
  • 44
  • 2
    Yes it will generate that `name` attribute (but the `id` attribute will be `id="User_UserName"` - did you check the html your generating)? –  Jul 23 '17 at 11:50
  • And if your code is not working, then show it :) –  Jul 23 '17 at 11:52
  • @StephenMuecke yes i did, i assume mvc understand the data through the name attribute, but when i do not want to use HTML.Helpers and use Standard HTML, like what HTML.Helpers create it does not send the value to controller and i receive the value as null. – Masoud Andalibi Jul 23 '17 at 11:53
  • @StephenMuecke sure in a second. – Masoud Andalibi Jul 23 '17 at 11:54
  • @StephenMuecke updated, just a note, there is a datepicker which will be initialized on the input tag that consists the BirthDate, as a JavaScript component – Masoud Andalibi Jul 23 '17 at 12:08
  • Slightly confusing because you have now shown a class named `Person` (does the view model now contain a property `public Person User { get; set; }`). But your property is `bool` and you cannot bind a `bool` to a value of `0` or `1` (your radio buttons need values of `True` and `False`). –  Jul 23 '17 at 12:13
  • As far as the `BirthDate` - is the value a valid date for the server culture? Check the value of `ModelState` to see if there are any errors. But why are you not just using the strongly types `HtmlHelpers` which will always work correctly. –  Jul 23 '17 at 12:15
  • I apologize for confusion, Well Since User Model is inherited from Persons Model, i thought i can get the values by just referencing User Model on My viewmodel. also you are right have to change that data type to byte perhaps. – Masoud Andalibi Jul 23 '17 at 12:15
  • @StephenMuecke Alright. – Masoud Andalibi Jul 23 '17 at 12:16
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/149927/discussion-between-stephen-muecke-and-valkyriee). –  Jul 23 '17 at 12:16

2 Answers2

1

Your name attributes are correct, however your Gender property will not bind correctly (the value will always be false) because your generating radio buttons with values of 0 and 1. In order to bind to a bool, they need values of True and False, e.g.

<input name="User.Gender" type="radio" id="men_gender" value="True" />

although your property name/type does not really make sense for Gender (either making the type an enum with "Male" and "Female", or changing the name to bool IsMale would be more appropriate)

You have not given enough details about what value is being posted for the BirthDate property, but if its not binding, then this is most likely due to the format of the date not matching the culture of the server (for example if a value of 7/24/2017 was posted but the server has a culture that accepts dates in dd/MM/yyyy format).

Its not clear why you want to generate the html manually. Your losing 2-way model binding (for example if your editing existing data or need to return the view because ModelState is invalid, the control will not display the correct value), and your not getting any client side validation. At the very least you should be inspecting the value of ModelState in the POST method to see what validation errors have been added.

As a side note, there is no reason to include the FormCollection form parameter in your method since your binding to a model.

0

Look First of All I recommend you to:-

  • Use @Html helpers whenever possible like this

    @Html.TextBoxFor(x => x.User.UserName)
    
  • View Models should be simple or complex objects but not derive from any class as it is just a data transfer object, this will help you make most benefit from MVC default model binder and model state provider, make your code more cleaner and help you write less code by avoiding coding against form collection or Request.Form have a look Here

  • For date time you may use a string model property and do manual convert or override the default model binder behavior in the global.asax for example

    ModelBinders.Binders[typeof(DateTime)] = 
       new DateAndTimeModelBinder() { CustomFormat = "yyyy-mm-dd" };
    

or whatever format according to the javascript picker

After these points we can say that:- In MVC

@Html.TextBoxFor(x => x.User.UserName)

is equal to

<input type="Text" name="User.UserName" id="User_UserName">

or

<input type="Text" name="User.UserName" id="User.UserName">

the id doesn't matter the name matter, the point here is that the model binder will bind it by default only if this attribute is one of the simple or complex VM object attributes and not a derived one

I hope this is of any help.

  • Suggesting to use a `string` for a `DateTime` is dreadful advice. And the `id` does matter having a `.` (dot) means that any jquery or css selector will interpret it as the start of a class selector. –  Jul 23 '17 at 23:00
  • What if the DateTime Format you want is not compatible with the server Culture you want to use? I mean the name matter and the id doesn't matter from the default model binder point of view. So if you want the model binder collect the form values and binds it just use form names that matches the model attributes – Mohammed Elshennawy Jul 25 '17 at 07:18
  • Typically you create a custom `ModelBinder` to handle a different culture on the server (or you post the value in ISO format - `yyyy-MM-dd` - which will always bind) –  Jul 25 '17 at 07:24