如何使用knockout js和jquery填充select标签?

时间:2014-01-17 00:52:38

标签: jquery asp.net-mvc knockout.js

我只是尝试填充我使用jquery渲染的几个选择标记。

来自服务器的传入模型:

public class DepthChartsVm
{
    public List<DepthChartModel> ReturnDepthCharts {get;set;}
}

public class DepthChartModel
{
    public Team Team {get;set;}
    public int SportId {get;set;}
}

public class Team
{
    // various properties
    public List<AthleteInfo> AthleteInfo {get;set;}
    public List<positions> BasketBallPositonList {get;set;}
}

public class AthleteInfo
{
    // various properties
    List<DepthChart> DepthChartPositions {get;set;}
}

public class DepthChart
{
    // various properties
    prop ...etc
}

public class Positions
{
   // various properties
   prop ... etc.
}

HTML:

                                <div class="col-sm-4">
            <label for="teamDropDown" class="text-center">Teams</label>
            <select id="teamDropDown" data-bind="options: depthVm.ReturnDepthCharts, value: selectedTeam,
                optionsText: function(item){return item.Team.FullName},
                optionsCaption: 'select'"></select>
            <p data-bind="if: selectedTeam">
                <strong>Sport: </strong> <span data-bind="text: selectedTeam().Team.Sport"></span><br />
                <strong>Season: </strong> <span data-bind="text: selectedTeam().Team.Season"></span>
            </p>
        </div>
        <!--Athletes-->
        <div class="col-sm-4">
            <div data-bind="if: selectedTeam">
                <h3 class="text-center">Athletes</h3>
                <div data-bind="foreach: selectedTeam().Team.AthleteInfo">
                    <p>
                        <strong>Name: </strong><span data-bind="text: FirstName()
                            + ' ' + LastName()"></span>
                    </p>
                    <div data-bind="foreach: DepthChartPositions()">
                        <p>
                            <input class="hidden" name="InputUpdateDepthChart.RosterId"
                                   data-bind="value: RosterId"/>
                            Position: <select name="InputUpdateDepthChart.PositionId"
                                    data-bind="options: $root.selectedTeam().Team.BasketBallPostionList, 
                                    optionsText: 'Position', value: PositionId, optionsValue: 'Id'"></select>
                            String: <select name="InputUpdateDepthChart.StringId"
                                    data-bind="options: DepthStrings, optionsText: 'String',
                                        value: StringId, optionsValue: 'Id'"></select>
                        </p>
                        <p>
                            <button data-bind="click: $root.addPosition">Add Position</button>
                        </p>
                    </div>
                </div>
            </div>
        </div>
    </div>
    // script that serializes the model and passes it to knockout
    <script>
        // json encode incoming model

        var depthModelData = @Html.Raw(JsonConvert.SerializeObject(Model))

        // update depth chart vM
        ko.applyBindings(new UpdateDepthChartVm(depthModelData),
            document.getElementById('updateDepthChartForm'));
    </script>

查看型号:

                    var UpdateDepthChartVm = function (model) {
var self = this;
self.depthVm = ko.mapping.fromJS(model);

// selected team 
self.selectedTeam = ko.observable();

// add position
self.addPosition = function () {

    self.selectedTeam.Team.AthleteInfo.DepthChartPositions.push(new depthChartPosition());
};

function depthChartPosition(values) {
    values = values || {};
    var model = {
        RosterId: ko.observable(values.RosterId),
        PositionId: ko.observable(values.PositionId),
        StringId: ko.observable(values.StringId),
    };
    return model;
}

};

基本上我想在用户希望添加其他位置时动态复制第一段。 jquery在使用select标签呈现新段落方面确实有效,但是select字段是空的,因为它们与我的视图模型之间似乎没有绑定。除此之外,我的视图模型正在运行。我在这里缺少什么?

2 个答案:

答案 0 :(得分:1)

后续选择元素没有任何选项的原因是因为它们没有绑定。请记住,ko.applyBindings可以让您的HTML变为现实。由于这些项目是在事后添加的,因此它们不会被ko跟踪。

使用knockout的observableArray实际上有更好的方法。它更干净,干燥,可测试。你太近了!

var UpdateDepthChartVm = function (model) {
var self = this;
self.depthVm = ko.mapping.fromJS(model);

self.selectedTeam = ko.observable();

self.addPosition = function () {
    self.depthVm.DepthChartPositions.splice(0, 0, new depthChartPosition());
    // I used splice because I believe you were wanting the UI element to be added to the
    // top of the list. Otherwise you could just use `push(new depthChartPosition())`.
};

// this bit isn't required but it's my preference
function depthChartPosition(values) {
    values = values || {};
    var model = {
        RosterId: ko.observable(values.RosterId),
        PositionId: ko.observable(values.PositionId),
        StringId: ko.observable(values.StringId),
    }
    return model;
}

// you also might want to use the above constructor in your mapping. again, just my preference
function mappings() {
    return {
        DepthChartPositions: {
            create: function(options) {
                return new depthChartPosition(options.data);
            }
        }
    }
}

请记住,knockout.js是一个MVVM(Model View View-Model)框架。让视图模型数据在不知道UI的同时驱动UI,同时让UI尽可能简单。

修改

仔细查看您的视图模型,我发现有两个DepthChartPositions:一个是self.depthVm的成员(由于ko.mapping应该是observableArray),另一个是{ {1}}。后者是UI元素绑定的对象。我确实看到你的selectedTeam().Team.AthleteInfo函数正在修改正确的对象,但我不知道如何创建这个对象。我的猜测是,这不是addPosition。您可以这样测试:

observableArray

答案 1 :(得分:1)

Knockout Mapping只会在提供的对象的第一级属性中添加可观察的包装器。我假设您正在为ko.mapping.fromJS方法提供的对象是DepthChartsVm C#模型的JS表示。因此,只能观察ReturnDepthCharts属性。简单的答案是提供映射自定义,例如:

var depthChartsVmMapping = {
    Team: {
        create: function(options) {
            return ko.mapping.fromJS(options.data, teamMapping);
        }
    }
}
var teamMapping = {
    AthleteInfo: {
        create: function(options) {
            return ko.mapping.fromJS(options.data, athleteInfoMapping);
        }
    }
}
var athleteInfoMapping = {
    DeptChartPositions: {
        create: function(options) {
            return ko.mapping.fromJS(options.data);
        }
    }
}

var self = this;
self.depthVm = ko.mapping.fromJS(model, depthChartsVmMapping);

是的,我同意这使得映射模块看起来有点麻烦,但考虑到你的模型包含太多级别的可能性。例如,你真的需要加载所有球队,所有运动员和每个运动员的深度图位置吗?更传统的类似REST的实现可能是在需要时异步加载必要的资源。显然,我不知道你的应用程序是如何呈现的,但我知道在这种情况下,你可能会:

  • 一支可观察的阵容;最初只包含摘要信息
  • 存储当前所选团队的观察
  • 获取团队详细信息的操作(可能包括具有深度图表信息的运动员

您可以使用上面举例说明的自定义设置进行映射,所以如果您没有关注我,请不要担心。但是,如果你想进行一些重构,我会支持这个原因!

希望有所帮助!

var vm = {
    Teams: ko.observableArray(),
    CurrentTeam: ko.observable(),
}