从多级动态集合添加下拉列表

时间:2017-06-14 07:28:25

标签: asp.net-mvc

我有这些模特。

ProjectViewModel

public class ProjectViewModel
    {
        //some properties..

        public IList<ScopeOfWorkViewModel> ScopeOfWork { get; set; }
    }

ScopeOfWorkViewModel

public class ScopeOfWorkViewModel
    {
        //some properties
        public IList<MaterialsViewModel> Materials { get; set; }
    }

MaterialsViewModel

 public class MaterialsViewModel
        {
            public int MaterialId { get; set; }
            [Display(Name = "Material")]
            public string MaterialName { get; set; }
            public int? Quantity { get; set; }
            public double? Cost { get; set; }
            public int? ScopeOfWorkId { get; set; }

            // should be a drop down list and get its data from database
            public IEnumerable<SelectListItem> CategoryList { get; set; }
        }

类别模型

 public partial class tblCategory
    {
        public tblCategory()
        {
            this.tblMaterials = new HashSet<tblMaterial>();
        }

        public int CategoryId { get; set; }
        public string CategoryName { get; set; }

        public virtual ICollection<tblMaterial> tblMaterials { get; set; }
    }

这是我的观点

@model MigratingDB.Models.ProjectViewModel
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal" id="ProjectForm">
        <h4>ProjectViewModel</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })

        Dynamically add ScopeOfWork and Materials<br />
        <a href="javascript:void(0)" id="addScope">Add Scope of Work</a>
        <div id="scopes">
            <div class="scope">
                <div class="materials">
                    <div class="material">
                    </div>
                </div>
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>


<div id="newScope" style="display:none">
    <div class="scope">
        <h3>Scope</h3>
        <div class="form-group">
            <label for="_#__ScopeOfWorkName" class="control-label col-md-2">Scope Of Work</label>
            <div class="col-md-10">
                <input class="form-control" type="text" id="_#__ScopeOfWorkName" name="ScopeOfWork[#].ScopeOfWorkName" value="">
            </div>
        </div>
        <input type="hidden" class="scopeindex" name="ScopeOfWork.Index" value="#" />

        <div class="materials">
            <h4>Material</h4>
            <a href="javascript:void(0)" class="addmaterial">Add Material</a>
        </div>
    </div>
</div>

<div id="newMaterial" style="display:none">
    <div class="form-group">
        <label for="_#__Materials_%__MaterialName" class="control-label col-md-2">Material</label>
        <div class="col-md-2">
            <input class="form-control" type="text" id="_#__Materials_%__MaterialName" name="ScopeOfWork[#].Materials[%].MaterialName" value="">
        </div>
    </div>

    <div class="form-group">
        <label for="_#__Materials_%__Quantity" class="control-label col-md-2">Quantity</label>
        <div class="col-md-1">
            <input class="form-control" type="text" id="_#__Materials_%__Quantity" name="ScopeOfWork[#].Materials[%].Quantity" value="">
        </div>
    </div>

    <div class="form-group">
        <label for="_#__Materials_%__Cost" class="control-label col-md-2">Cost</label>
        <div class="col-md-1">
            <input class="form-control" type="text" id="_#__Materials_%__Cost" name="ScopeOfWork[#].Materials[%].Cost" value="">
        </div>
    </div>

//build a category drop down list here

    <input type="hidden" class="materialindex" name="ScopeOfWork[#].Materials.Index" value="%" />
</div>

<script>
    var form = $('form');
    var scope = $('#newScope');
    var material = $('#newMaterial');

    form.on('click', '.addmaterial', function () {
        var clone = material.clone();
        var scopeIndex = $(this).closest('.scope').find('.scopeindex').val();
        clone.html($(clone).html().replace(/#/g, scopeIndex));

        var materialIndex = new Date().getTime();
        clone.html($(clone).html().replace(/%/g, materialIndex));
        $(this).closest('.materials').append(clone.html());
        form.data('validator', null);
        $.validator.unobtrusive.parse(form);
    });

    $('#addScope').click(function () {
        var clone = scope.clone();
        var scopeIndex = new Date().getTime();
        clone.html($(clone).html().replace(/#/g, scopeIndex));
        $('#scopes').append(clone.html());
        form.data('validator', null);
        $.validator.unobtrusive.parse(form);
    });
</script>

请在每次创建MaterialViewModel对象并将其发布到我的数据库时帮助我为该类别构建一个下拉列表。我有一个类别表,我想在其中传递其值以生成下拉列表。我更喜欢使用强类型模型而不是使用ViewBag

1 个答案:

答案 0 :(得分:0)

您的ProjectViewModel需要一个属性IEnumerable<SelectListItem>(或包含2个属性的类IEnumerable作为选项值和文本),以便您可以将选项值传递给视图(如果没有MaterialsViewModel)。您的MaterialsViewModel还需要一个属性来将所选类别绑定到。

public class ProjectViewModel
{
    // some properties..
    public IEnumerable<SelectListItem> CategoryList { get; set; }
    public IList<ScopeOfWorkViewModel> ScopeOfWork { get; set; }
}
....
public class MaterialsViewModel
{
    public int MaterialId { get; set; }
    ....
    // public int? ScopeOfWorkId { get; set; } this is not required
    public int SelectedCategory { get; set; }
    public IEnumerable<SelectListItem> CategoryList { get; set; }
}

然后在GET方法中,初始化视图模型并填充CategoryList,例如

IEnumerable categories = db.tblCategory; // select all categories
ProjectViewModel model = new ProjectViewModel()
{
    CategoryList = new SelectList(categories, "CategoryId", "CategoryName"),
    .... set other properties as required
};
return View(model);

在视图中,包含一个脚本,用于将CategoryList转换为javascript数组

<script>
    var form = $('form');
    ....
    var categories = @Html.Raw(Json.Encode(Model.CategoryList));

    form.on('click', '.addmaterial', function () {
        ....

然后在你的模板中(在`元素中),为select控件添加html(但是如果你需要的话,除了label选项之外没有任何选项)。请注意选择的附加类名。

<div class="form-group">
    <label for="_#__Materials_%__SelectedCategory" class="control-label col-md-2">Cost</label>
    <div class="col-md-1">
        <select class="form-control category" id="_#__Materials_%__SelectedCategory" name="ScopeOfWork[#].Materials[%].SelectedCategory">
            <option value="">Please select a category</option>
        </select>
    </div>
</div>

并修改脚本以根据javascript数组中的值添加选项。

form.on('click', '.addmaterial', function () {
    var clone = material.clone();
    ....
    clone.html($(clone).html().replace(/%/g, materialIndex));

    // Add the options
    var select = clone.find('.category');
    $.each(categories, function(index, item) {
        select.append($('<option></option>').val(item.Value).text(item.Text));
    });

    $(this).closest('.materials').append(clone.html());
    form.data('validator', null);
    $.validator.unobtrusive.parse(form);
});

请注意,您没有为现有项呈现任何html,因此如果任何内容无效并且您返回视图,则用户动态添加的所有数据都将丢失 - 您需要嵌套for循环。而且你没有在动态添加的元素上渲染html进行验证。