bugfix> javascript > 投稿

問題を見つけようとして2日間を費やしました。不必要な情報なしで背景を説明するためにここにすべてを載せようとしますが、尋ねてください、そして情報を提供します。

問題

プロセスは、ユーザーが競争に追加したいチームを選択し、追加をクリックすると、選択したチームがメインの teams から削除されることです。リスト、および competition.teams に追加リスト。チームを削除するには、ユーザーはオプションボックスからチームを選択し、[削除]をクリックします。これにより、チームが competition.teams から削除されます配列、および teams に再追加アレイ。

  1. ドロップダウンボックスに「チーム」のリストがあり、「チームを追加」ボタンがあります。クリックすると、ドロップダウンボックスから選択したチームが選択オプションボックスに追加されます。ボックスからチームを削除すると、オプションボックスにバインドされたデータである親リストからチームを削除できません。
  2. その後、チームが再度追加されると、選択の両方のエントリが同じチーム名になります。

望ましい結果

上記のようにコードを機能させたいのですが、ノックアウト/ javascriptの知識が限られているため、ソリューションを過剰に設計している可能性があります。私は他のソリューションを受け入れていますが、これをサーバーに送信する段階にはまだ達していません。これは通常のフォーム送信ほど簡単ではないと予測しています!

例外

Chromeコンソールのエラーは次のとおりです。

Uncaught TypeError: Cannot read property 'name' of undefined at eval (eval at parseBindingsString (knockout-min.3.4.2.js:68), :3:151) at f (knockout-min.3.4.2.js:94) at knockout-min.3.4.2.js:96 at a.B.i (knockout-min.3.4.2.js:118) at Function.Uc (knockout-min.3.4.2.js:52) at Function.Vc (knockout-min.3.4.2.js:51) at Function.U (knockout-min.3.4.2.js:51) at Function.ec (knockout-min.3.4.2.js:50) at Function.notifySubscribers (knockout-min.3.4.2.js:37) at Function.ha (knockout-min.3.4.2.js:41)

コード

スクリーンショットの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 に追加されることでした一部の場所で観測可能で、他の場所では観測できませんでした。これにより、オブザーバブルオブジェクト内のオブザーバブルプロパティにアクセスしようとする場所でバインディングエラーが発生していました。

変更された競争オブジェクト

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 件
  • あなたは observableArray を埋めています   observable と  インスタンス。これは一般的にすべきことですじゃない 行う:

    // 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));
    
    

あなたの答え