Reputation: 3541
I'm using C#, MVC4 and jQuery. The requirements given to me are that I need to be able to add records to a database which translates into editable fields in a view.
The view has tabs, groups, lines and items which need to be populated depending on what is in the database. I have been able to achieve what I want using a viewmodel containing a list of "Tabs"; each Tab contains a list of "Groups"; each Group contains a list of "Lines" and each Line contains a list of "Items".
What ends up happening for me is that I need to repeat a lot of code that is exactly the same for the editors. This is what I want to avoid.
So in my view I've got something like:
@for (int tabIndex = 0; Model.Tabs != null && tabIndex < Model.Tabs.Count; tabIndex++)
{
<div id="[email protected][tabIndex].TabID" class="tab-content">
@for (int groupIndex = 0; Model.Tabs[tabIndex].Groups != null && groupIndex < Model.Tabs[tabIndex].Groups.Count; groupIndex++)
{
<legend>@Model.Tabs[tabIndex].Groups[groupIndex].Name</legend>
//This is the part that is not working but I would like to
AddEditorsForGroup(tabIndex, groupIndex);
...
...
And then I also have this in the view:
@{
public void AddEditorsForGroup(int TabIndex, int GroupIndex)
{
for (int lineIndex = 0; Model.Tabs[TabIndex].Groups[GroupIndex].Lines != null && lineIndex < Model.Tabs[TabIndex].Groups[GroupIndex].Lines.Count; lineIndex++)
{
for (int itemIndex = 0; Model.Tabs[TabIndex].Groups[GroupIndex].Lines[lineIndex].Items != null && itemIndex < Model.Tabs[TabIndex].Groups[GroupIndex].Lines[lineIndex].Items.Count; itemIndex++)
{
AddLabelAndEditor(TabIndex, GroupIndex, lineIndex, itemIndex);
}
}
}
public void AddLabelAndEditor(int TabIndex, int GroupIndex, int LineIndex, int ItemIndex)
{
<div class="_20">
<p>
switch (Model.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].DisplayTypeEdit.ToLower().Replace(" ", string.Empty))
{
case "checkbox":
Html.LabelFor(vm => vm.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueBoolean, Model.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].Name);
Html.CheckBoxFor(vm => vm.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueBoolean);
break;
case "dropdownlist":
Html.LabelFor(vm => vm.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueString, Model.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].Name);
Html.DropDownListFor(vm => vm.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueString, Model.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueSelectList);
break;
case "multiselectlist":
Html.LabelFor(vm => vm.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueString, Model.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].Name);
Html.DropDownListFor(vm => vm.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueString, Model.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueMultiSelectList);
break;
case "radiobutton":
Html.RadioButtonFor(vm => vm.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueString, Model.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueString);
break;
case "textarea":
Html.LabelFor(vm => vm.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueString, Model.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].Name);
Html.TextAreaFor(vm => vm.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueString);
break;
default:
Html.LabelFor(vm => vm.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueString, Model.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].Name);
Html.TextBoxFor(vm => vm.Tabs[TabIndex].Groups[GroupIndex].Lines[LineIndex].Items[ItemIndex].ValueString);
break;
}
</p>
</div>
}
}
If I repeat the code contained in AddLabelAndEditor
on several parts of the view, it works but it is a lot of code repetition.
Relevant part of my ViewModel:
public List<Tab> Tabs { get; set; }
public class Tab
{
public string Name { get; set; }
public List<Group> Groups { get; set; }
}
public class Group
{
public string Name { get; set; }
public List<Line> Lines { get; set; }
}
public class Line
{
public string Name { get; set; }
public List<Item> Items { get; set; }
}
public class Item
{
public string Name { get; set; }
public string Description { get; set; }
public string DataType { get; set; }
public string DataDefaultValue { get; set; }
public string DisplayTypeEdit { get; set; }
public string ValueString { get; set; }
public bool ValueBoolean { get; set; }
public DateTime ValeDateTime { get; set; }
public decimal ValueDecimal { get; set; }
public int ValueInt { get; set; }
public MultiSelectList ValueMultiSelectList { get; set; }
public SelectList ValueSelectList { get; set; }
}
Any thoughts, ideas, suggestions? Thanks
Upvotes: 0
Views: 478
Reputation: 9165
So, here is what you can do. In your main View, you will have:
@Html.EditorFor(model => model.Tabs)
Then, you need to create the following partial views, and place them under Views/Shared/EditorTemplates:
Tab.cshtml:
@model YourApp.ViewModels.Tab
<div class="Tab"> @* Or however you want to represent each Tab... *@
@Html.EditorFor(model => model.Groups)
</div>
Group.cshtml:
@model YourApp.ViewModels.Group
<div class="Group"> @* Or however you want to represent each Group... *@
@Html.EditorFor(model => model.Lines)
</div>
Line.cshtml:
@model YourApp.ViewModels.Line
<div class="Line"> @* Or however you want to represent each Line... *@
@Html.EditorFor(model => model.Items)
</div>
Item.cshtml:
@model YourApp.ViewModels.Item
<div class="_20">
@Html.Label(Model.Name)
@switch (Model.DisplayTypeEdit.ToLower().Replace(" ", string.Empty))
{
case "checkbox":
@Html.CheckBoxFor(model => model.ValueBoolean)
break;
case "dropdownlist":
@Html.DropDownListFor(model => model.ValueString, Model.ValueSelectList)
break;
case "multiselectlist":
@Html.DropDownListFor(model => model.ValueString, Model.ValueMultiSelectList)
break;
case "radiobutton":
@Html.RadioButtonFor(model => model.ValueString, Model.ValueString)
break;
case "textarea":
@Html.TextAreaFor(model => model.ValueString)
break;
default:
@Html.TextBoxFor(model => model.ValueString)
break;
}
</div>
Upvotes: 1
Reputation: 54656
You can create EditFor Templates for each of your types:
/Views/Shared/EditorTemplates/Tab.cshtml :
@model Tab
<div>
// Specific Tab Html
@Model.Name
@foreach (var group in Model.Groups)
{
Html.Editfor(m => group)
}
</div>
/Views/Shared/EditorTemplates/Group.cshtml
//Group html etc
You can chain these and resuse them where ever:
@model viewModel
<div>
@foreach (var tab in Model.Tabs)
{
Html.Editfor(m => tab)
}
</div>
It seems what you are doing is what EditFor/Display for templates were specifically designed for.
Upvotes: 1