2

I want to display a string type as checkbox on MVC view, but returns it as string type on HTTP post. The problem is that it returns false on HTTP Post. Below is my code:

View:

  @model List<Car>

        foreach(var car in Model){
       bool isFourWheel = false;
        if(bool.TryParse(car.IsFourWheel, out isFourWheel){
        @Html.CheckBox("IsFourWheel", isFourWheel); //need to be rendered as checkbox, but returns string type on HTTP POST
    }
     }

Model:

public class Car
    {
        public string IsFourWheel { get; set; } //bad naming, but it can contain any type, include boolean
    }

Controller:

 public ActionResult Index()
        {


            var cars = new List<Car>(){ new Car(){IsFourWheel = "true"},new Car(){IsFourWheel = "false"} };
            return View(cars);
        }

        [HttpPost]
        public ActionResult Index(List<Car> cars)  **Problem IsFourWheel is false when true is selected **
        {           
            return View(cars);
        }

Any ideal would be very much appreciated.

Pingpong
  • 7,681
  • 21
  • 83
  • 209

3 Answers3

6

You can try specifying a template name in your helper:

@Html.EditorFor(car => car.IsFourWheel, "CheckBox")

And defining the template to render the data the way you want, in either ~/Views/{YourControllerName}/EditorTemplates/CheckBox.cshtml or ~/Views/Shared/EditorTemplates/CheckBox.cshtml.

You can find a whole series of post by Brad Wilson on MVC templates here:

Brad Wilson: ASP.NET MVC 2 Templates, Part 1: Introduction

It is for MVC 2, but most concepts still apply to MVC 3 as well (save for the Razor syntax).

Update:

Actually you probably don't need a custom template for this. Try using @Html.CheckBoxFor(car => car.IsFourWheel) instead.

Update 2:

Drop the following template in ~/Views/Shared/EditorTemplates:

IsFourWheel.cshtml

@functions {
    private bool IsChecked() {
        if (ViewData.Model == null) return false;
        return Convert.ToBoolean(ViewData.Model, System.Globalization.CultureInfo.InvariantCulture);
    }
}

@Html.CheckBox("", IsChecked(), new { @class = "check-box" })

Then from your view, call it like so:

@Html.EditorFor(model => model.IsFourWheel, "IsFourWheel")

I tested it and binding works in both GET and POST scenarios.

Daniel Liuzzi
  • 16,807
  • 8
  • 52
  • 57
  • Thanks. Could you please elaborate on How to implement it on the CheckBox.cshtml. – Pingpong Aug 26 '11 at 14:57
  • For an example, you could take a look at the default template for boolean fields (what CheckBoxFor renders) in the MVC source code, for example [Boolean.ascx](http://code.google.com/p/arscloud/source/browse/trunk/MvcFutures/MvcFutures/DefaultTemplates/EditorTemplates/Boolean.ascx?r=7), but as I said, unless you're doing something very specific with your `IsFourWheels` property, you probably don't need a template and can just use `@Html.CheckBoxFor(car => car.IsFourWheel)` instead. – Daniel Liuzzi Aug 26 '11 at 15:05
  • Daniel Liuzzi, @Html.CheckBoxFor requires bool type. – Pingpong Aug 26 '11 at 15:17
  • @Pingpong Yes, my bad. I updated the answer with the template, which work as expected for boolean values stored as strings. – Daniel Liuzzi Aug 26 '11 at 15:47
  • Thanks. I will use it and see if it works with my implementation. – Pingpong Aug 26 '11 at 19:53
  • I tried your advice. But it returns string of False on HTTP Post even if it is checked. Please note that the IsFourWheel above is string type. Any idea? Thanks! – Pingpong Sep 16 '11 at 16:49
  • Sorry, I can't reproduce your problem. I made a little test project, set a break-point on the POST action, and inspected the value of the model for both checked and unchecked conditions, and get "true" and "false" respectively. I'd suggest you to double check everything to make sure you didn't leave anything out. – Daniel Liuzzi Sep 16 '11 at 20:08
  • Thanks. I need to check again. Thank you for your advice again. Very appreciated. – Pingpong Sep 16 '11 at 20:48
1

You could alter your viewmodel like this:

public class Car
    {
        public string IsFourWheel { get; set; }
        public bool IsFourWheelBool { get { return bool.Parse(IsFourWheel); } }
    }

Your view would look like this:

@Html.EditFor(x => x.IsFourWheelBool);
Robin van der Knaap
  • 4,060
  • 2
  • 33
  • 48
  • that requires change of domain model, which is not the ideal one. – Pingpong Aug 26 '11 at 14:31
  • 1
    It's usually considered bad design to use your domain models in your views. (http://stackoverflow.com/questions/4865806/why-two-classes-view-model-and-domain-model) It's better to create viewmodels to facilitate your view. With a tool like automapper (http://automapper.org/) it's easy to create viewmodels from domain models. But also creating viewmodels by hand is quite do-able. – Robin van der Knaap Aug 26 '11 at 15:04
  • Thanks. I knew that. My question is only a simplified example. – Pingpong Aug 26 '11 at 15:35
1

I think it will be easier, if you add an Id to your model. Just like this
Model:

public class Car
{
    public int CarID { get; set; }
    public string IsFourWheel { get; set; }        
}


View:

@model IEnumerable<Car>
foreach (var car in Model)
{
    if(car.IsFourWheel == "true"){
        <input type="checkbox" name="carID" value="@car.CarID" checked="checked" />
    }
    else
    {
        <input type="checkbox" name="carID" value="@car.CarID" />
    }
}

Controller:

[HttpPost]
public ActionResult Index(List<int> carID)
{
    //handle selected cars here
    return View();
}
dohaivu
  • 2,038
  • 15
  • 17
  • Thank you for your advice. I updated my question. I have a collection of cars, which is needed on post. Your current solution can work for single car only. – Pingpong Aug 26 '11 at 14:30
  • When there are a collection of car, the input tag needs a id/name that allows the model binder to assign the value. – Pingpong Aug 26 '11 at 15:14