8

I've got a checkbox that I want to display on my view related to a field called public, which basically says whether the particular row is public or not. In the database this is a bit field, but it allows nulls, due to how the table previously used to work.

I'm using Html.CheckBoxFor but it is complaining about this field because in the system it is not a bool type, but rather a bool? type. What I want to do is have it so that if the field value is null, then it counts as a false on the front end (unfortunately updating the database values themselves is not an option).

I have tried using the GetValueOrDefault, and putting a default value in my model file along the lines of:

public class Model
{
    public bool? Public { get; set; }

    public SearchModel()
    { 
        Public = false;
    }
}

however it was complaining about this, giving me the following error:

An exception of type 'System.InvalidOperationException' occurred in System.Web.Mvc.dll but was not handled in user code

Additional information: Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.

So i'm not sure how I can progress from here, can someone help point me in the right direction?

EDIT:

This is the code on the view that i'm trying to use to show the checkbox. In this instance i'm adding some extra html attributes so that it appears as a toggle rather than a simple checkbox:

Html.CheckBoxFor(model => model.Public, new {data_toggle = "toggle", data_off = "No", data_on = "Yes", data_size = "small"})
4
  • I've got to question why anyone would ever call a variable Public, on an unrelated note your model is called Model but the constructor for it is called SearchModel so that'll be causing some issues Commented Nov 8, 2016 at 14:57
  • Sounds like you're model binding to EF entities. This is very inflexible and generally not a good idea. Commented Nov 8, 2016 at 15:07
  • @AlfieGoodacre Good point, I've been changing some things around and must have missed that. As for the variable, that's what the field is called in the database so I've kept the variable name the same in the model file. I unfortunately didn't get any say in what the database fields were called Commented Nov 8, 2016 at 15:10
  • To make your code work you need to Instantiate the model and pass it to the view e.g. Model model = new Model(); return View(model); Maybe some controller action code would help to identify the problem. Commented Nov 8, 2016 at 16:12

5 Answers 5

15

The specific exception you're getting occurs when you pass an expression to one of the templated helpers that can't be evaluated. Bear in mind that when you're using the expression-based helpers, you're not actually passing a property by value but rather an expression that represents a property on your model and which the helper will use to reference that property, generate field names from, etc.

You haven't shown the actual code where you're doing this, but this means essentially you can't do something like:

@Html.EditorFor(m => m.Public.GetValueOrDefault())

Because the templated helper cannot resolve that as an expression that matches up with a property on your model.

As to your actual base concern here, namely setting the value to false if it's null, you just need a custom getter and setter. @utaco's answer provides the new easier C# 6.0 method of auto-implemented properties with defaults:

public bool? Public { get; set; } = false;

For previous versions of C#, you need the following:

private bool? public;
public bool? Public
{
    get { return public ?? false; }
    set { public = value; }
}

However, keeping Public as a nullable bool when you have no intention of it ever actually being null just makes your code more difficult. Assuming you can change that to just bool (i.e. this is a view model and not the actual entity class tied to your database table), then you should do so. You still want to keep the private as a nullable though. That allows you accept nulls in the setter but coerce them into false values in the getter, meaning the actual value of public will always be either true or false, i.e. not null.

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

1 Comment

Thanks very much for your help. I never actually thought of using a view model to get round this issue, but i've created one and plugged that in, and it now appears to be working
4

if you use c# 6.0 or higher you can use this:

public bool YourProp { get; set; } = false; 

1 Comment

When I do this I get the same error as in the main post
2

I've taken Chris Pratt idea but used it differently. I created a ViewModel and added a non-nullable property to update the nullable property and vice versa.

public bool? Public { get; set; }

private bool _public;
public bool _Public
{
    get { return Public ?? false; }
    set 
    { 
      _public = value; 
      Public = value; 
    }
}

Now in the View, you will use the non-nullable value for updating instead of the nullable value

Html.CheckBoxFor(model => model._Uploaded)

The only issue with this approach is that you will not get back null if saving changes. This worked for me as NULL value represent false in our program.

1 Comment

It is just worked when I created a fake property as explained here, and then fill it before to save the record and when I had to show this property on the page, I need to fill the fake property with the original property. I couldn't find another way.
1

This don't need initialisation. @Html.EditorFor(m => m.AnyNullableProperty) Below worked for me as expected.

        <div class="form-group">
            @Html.LabelFor(model => model.RequiresBatchNumberOnReceipt, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(m => m.RequiresBatchNumberOnReceipt)

                @Html.ValidationMessageFor(model => model.RequiresBatchNumberOnReceipt)
            </div>
        </div>

Comments

1

Issue

I used the @Chris Pratt solution, but it failed with error:

@Html.LabelFor(model => model.AnnounceIsActive, "فعال")
@Html.CheckBoxFor(model => model.AnnounceIsActive.GetValueOrDefault(true), new { disabled="disabled" })

Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.

Line 71:                             @Html.LabelFor(model => model.AnnounceIsActive, "فعال")
Line 72:                             @Html.CheckBoxFor(model => model.AnnounceIsActive.GetValueOrDefault(true), new { disabled="disabled" })
Line 73:                         </div>
Line 74:                     </div>

Solution

And the solution to fix it was to create a variable before passing it to the helper.

<div class="checkbox">
    @{
        var announceIsActiveValue = Model.AnnounceIsActive.GetValueOrDefault(true);
    }
    @Html.CheckBoxFor(model => announceIsActiveValue, new { disabled="disabled", @class="form-control" })
    @Html.LabelFor(model => announceIsActiveValue, "فعال")
</div>

Note:

you will not be able to use model attribute...

Comments

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.