knockout observableArray remove导致错误并且无法删除

时间:2018-05-24 21:44:45

标签: javascript html knockout.js

我有一个问题,我花了两天时间试图弄明白,我会尽量把所有内容都放在这里解释背景而没有不必要的信息,但请问我会提供信息。

问题

过程是,用户选择他们想要添加到竞赛中的团队,点击添加,然后选择的团队将从主teams列表中删除,并添加到competition.teams名单。要删除团队,用户可以从选项框中选择团队,然后点击删除。这将从competition.teams数组中删除该团队,并重新添加到teams数组中。

  1. 我在一个下拉框中有一个“团队”列表,其中有一个“添加团队”按钮。单击时,它会将从下拉框中选择的团队添加到选择选项框中。当我从框中删除团队时,它将无法从绑定到选项框的数据的父列表中删除它。
  2. 当对团队进行读取时,select中的两个条目都是相同的团队名称。
  3. 期望的结果

    我希望代码能够如上所述工作,由于我对knockout / javascript的了解有限,我可能过度设计了解决方案。我对其他解决方案持开放态度,我还没有进入将其提交回服务器的阶段,我预测这不会像普通表单提交那么容易!

    例外

    Chrome控制台中的错误是:

      

    未捕获的TypeError:无法读取未定义的属性“name”       at eval(eval at parseBindingsString(knockout-min.3.4.2.js:68),:3:151)       at f(knockout-min.3.4.2.js:94)       在knockout-min.3.4.2.js:96       at a.B.i(knockout-min.3.4.2.js:118)       在Function.Uc(knockout-min.3.4.2.js:52)       在Function.Vc(knockout-min.3.4.2.js:51)       在Function.U(knockout-min.3.4.2.js:51)       在Function.ec(knockout-min.3.4.2.js:50)       在Function.notifySubscribers(knockout-min.3.4.2.js:37)       在Function.ha(knockout-min.3.4.2.js:41)

    守则

    Teams Multi select and DropDown

    屏幕截图的HTML:

    <div class="form-group">
        <div class="col-md-3">
            <label for="selectedTeams" class="col-md-12">Select your Teams</label>
            <button type="button" data-bind="enable:$root.teams().length>0,click:$root.addTeam.bind($root)"
                    class="btn btn-default col-md-12">Add Team</button>
            <button type="button" data-bind="enable:competition().teams().length>0,click:$root.removeTeam.bind($root)"
                    class="btn btn-default col-md-12">Remove Team</button>
            <a data-bind="attr:{href:'/teams/create?returnUrl='+window.location.pathname+'/'+competition().id()}"class="btn btn-default">Create a new Team</a>
        </div>
    
        <div class="col-md-9">
            <select id="teamSelectDropDown" data-bind="options:$root.teams(),optionsText:'name',value:teamToAdd,optionsCaption:'Select a Team to Add..'"
                    class="dropdown form-control"></select>
            <select id="selectedTeams" name="Teams" class="form-control" size="5"
                    data-bind="options:competition().teams(),optionsText:function(item){return item().name;},value:teamToRemove">
                    </select>
        </div>
    </div>
    

    addTeam按钮点击代码:

    self.addTeam = function () {
        if ((self.teamToAdd() !== null) && (self.competition().teams().indexOf(self.teamToAdd()) < 0)){// Prevent blanks and duplicates
            self.competition().teams().push(self.teamToAdd);
            self.competition().teams.valueHasMutated();
        }
        self.teams.remove(self.teamToAdd());
        self.teamToAdd(null);
    };
    

    removeTeam按钮点击代码:

    self.removeTeam = function () {
        self.teams.push(self.teamToRemove());
        self.competition().teams.remove(self.teamToRemove());
        self.competition().teams.valueHasMutated();
        self.teamToRemove(null);
    };
    

    Competition对象(为简洁起见,删除了一些属性):

    function Competition(data) {
        var self = this;
        self.id = ko.observable(data.id);
        self.name = ko.observable(data.name);
        self.teams = ko.observableArray(
            ko.utils.arrayMap(data.teams, function (team) {
                return ko.observable(new Team(team));
            }));
    };
    

    team对象:

    function Team(data) {
        var self = this;
        self.id = ko.observable(data.id);
        self.name = ko.observable(data.name);
    }
    

    有什么遗漏或不清楚?请询问,我将在问题上添加材料。

    解决方案

    正如@ user3297291

    所建议的那样

    问题在于,添加到competition.teams的对象在某些地方是可观察到的,而在其他地方则无法观察到。这在一些地方会导致绑定错误,它会尝试访问observable对象中的observable属性。

    更改竞争对象

    function Competition(data) {
      var self = this;
      self.id = ko.observable(data.id);
      self.name = ko.observable(data.name);
      self.teams = ko.observableArray(
        ko.utils.arrayMap(data.teams, function (team) {
          return new Team(team);
        }));
    };
    

    修改了HTML绑定(仅简化了optionsText绑定)

    <div class="form-group">
        <div class="col-md-3">
            <label for="selectedTeams" class="col-md-12">Select your Teams</label>
            <button type="button" data-bind="enable:$root.teams().length>0,click:$root.addTeam.bind($root)"
                    class="btn btn-default col-md-12">Add Team</button>
            <button type="button" data-bind="enable:competition().teams().length>0,click:$root.removeTeam.bind($root)"
                    class="btn btn-default col-md-12">Remove Team</button>
            <a data-bind="attr:{href:'/teams/create?returnUrl='+window.location.pathname+'/'+competition().id()}"class="btn btn-default">Create a new Team</a>
        </div>
    
        <div class="col-md-9">
            <select id="teamSelectDropDown" data-bind="options:$root.teams(),optionsText:'name',value:teamToAdd,optionsCaption:'Select a Team to Add..'"
                    class="dropdown form-control"></select>
            <select id="selectedTeams" name="Teams" class="form-control" size="5"
                    data-bind="options:competition().teams(),optionsText:'name',value:teamToRemove">
                    </select>
        </div>
    </div>
    

    修订添加团队功能

    self.addTeam = function () {
        if ((self.teamToAdd() !== null) && (self.competition().teams().indexOf(self.teamToAdd()) < 0)){
            self.competition().teams().push(self.teamToAdd());
            self.competition().teams.valueHasMutated();
        }
        self.teams.remove(self.teamToAdd());
        self.teamToAdd(null);
    };
    

    修订删除团队功能

    非常确定我不再需要valueHasMutated()来电,但至少它有效..

    self.removeTeam = function () {
        self.teams.push(self.teamToRemove());
        self.competition().teams.remove(self.teamToRemove());
        self.competition().teams.valueHasMutated();
        self.teamToRemove(null);
    };
    

1 个答案:

答案 0 :(得分:1)

您正在填充observableArrayobservable个实例。这通常应该做:

// Don't do this:
self.teams = ko.observableArray(
  ko.utils.arrayMap(data.teams, function(team) {
    return ko.observable(new Team(team));
  })
);

相反,请包含Team个实例而不包装它们:

// Do this instead:
self.teams = ko.observableArray(
  ko.utils.arrayMap(data.teams, function(team) {
    return new Team(team);
  })
);

现在,您可以使用“简单”optionsText绑定,就像您之前所做的那样:

data-bind="optionsText: 'name', /* ... */" 

个人偏好:当我们在每个浏览器中都有utils.arrayMap时,您不需要.map帮助器。我个人写道:

Team.fromData = data => new Team(data);
// ...
self.teams = ko.observableArray(data.teams.map(Team.fromData));