Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LabelFor not working in loop (for/foreach/template)

I'm trying to make a list of checkboxes but the Html.LabelFor is not working. It keeps generating <label for="">Some text</label>, note the empty For attribute.

I've tried a for and foreach loop before I tried using a display template but they all have this problem. I refuse to believe that writing the input fields by hand is the only solution. All help/tips are much appreciated.

The Model:

public class MyViewModel
{
    public string Name { get; set; }
    public bool Selected { get; set; }
}

The Controller:

public ActionResult MyController()
{
    var model = new List<MyViewModel>();

    model.Add(new MyViewModel() { Name= "Foo" });
    model.Add(new MyViewModel() { Name= "Bar" });

    return View(model);
}

The View:

@model IEnumerable<MyViewModel>
<div>
    @Html.DisplayFor(x => Model)
</div>

The Display Template:

@model MyViewModel
<p>
    @Html.LabelFor(model => model.Selected, Model.Name)
    @Html.EditorFor(model => model.Selected)
</p>

The Result:

<div>    
    <p>
        <label for="">Foo</label>
        <input class="check-box" data-val="true" data-val-required="The Foo field is required." 
            name="[0].Selected" type="checkbox" value="true" />
        <input name="[0].Selected" type="hidden" value="false" />
    </p>
    <p>
        <label for="">Bar</label>
        <input class="check-box" data-val="true" data-val-required="The Bar field is required." 
            name="[1].Selected" type="checkbox" value="true" />
        <input name="[1].Selected" type="hidden" value="false" />
    </p>
<div>

As you can see the <label> isn't working as intended.

like image 292
Snæbjørn Avatar asked Dec 28 '22 05:12

Snæbjørn


1 Answers

The thing is that since in your main view you directly have IEnumerable<MyViewModel>, the prefix is [0].Selected which the helpers doesn't like. You will also notice that your checkbox doesn't have an id. Here's a hack that you could employ to change this prefix:

@model MyViewModel
@{
    ViewData.TemplateInfo.HtmlFieldPrefix = "Model" + ViewData.TemplateInfo.HtmlFieldPrefix;
}

<p>
    @Html.LabelFor(model => model.Selected, Model.Name)
    @Html.EditorFor(model => model.Selected)
</p>

Obviously this assumes that your POST action argument is called model:

[HttpPost]
public ActionResult Index(IEnumerable<MyViewModel> model) { ... }

which might not always be the case.

Also note that instead of @Html.DisplayFor(x => Model) in your main view you could use @Html.EditorForModel().

Another possibility is to wrap this in a view model with a collection property:

public class MyViewModel { public IEnumerable Items { get; set; } }

where ItemViewModel:

public class ItemViewModel
{
    public string Name { get; set; }
    public bool Selected { get; set; }
}

and then your controller:

public ActionResult Index()
{
    var model = new MyViewModel
    {
        Items = new[]
        {
            new ItemViewModel() { Name= "Foo" },
            new ItemViewModel() { Name= "Bar" }
        }
    };
    return View(model);
}

[HttpPost]
public ActionResult Index(MyViewModel model)
{
    ...
}

and in the view:

@model MyViewModel
<div>
    @using (Html.BeginForm())
    {
        @Html.DisplayFor(x => x.Items)
        <button type="submit">OK</button>
    }
</div>

and in the editor template (~/Views/Shared/EditorTemplates/ItemViewModel.cshtml):

@model ItemViewModel
<p>
    @Html.LabelFor(model => model.Selected, Model.Name)
    @Html.EditorFor(model => model.Selected)
</p>

Now it will work as expected.

like image 112
Darin Dimitrov Avatar answered Dec 29 '22 17:12

Darin Dimitrov