問題を見つけようとして2日間を費やしました。不必要な情報なしで背景を説明するためにここにすべてを載せようとしますが、尋ねてください、そして情報を提供します。
問題
プロセスは、ユーザーが競争に追加したいチームを選択し、追加をクリックすると、選択したチームがメインの
teams
から削除されることです。リスト、および
competition.teams
に追加リスト。チームを削除するには、ユーザーはオプションボックスからチームを選択し、[削除]をクリックします。これにより、チームが
competition.teams
から削除されます配列、および
teams
に再追加アレイ。
- ドロップダウンボックスに「チーム」のリストがあり、「チームを追加」ボタンがあります。クリックすると、ドロップダウンボックスから選択したチームが選択オプションボックスに追加されます。ボックスからチームを削除すると、オプションボックスにバインドされたデータである親リストからチームを削除できません。
- その後、チームが再度追加されると、選択の両方のエントリが同じチーム名になります。
望ましい結果
上記のようにコードを機能させたいのですが、ノックアウト/ 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);
};
あなたは
observableArray
を埋めていますobservable
と インスタンス。これは一般的にすべきことですじゃない 行う:代わりに、
Team
を含めます それらをラップしないインスタンス:これで、「単純な」
optionsText
を使用できます 先ほどやったように、バインディング:個人の好み: あなたは
utils.arrayMap
を必要としません.map
があるときのヘルパー すべてのブラウザで。私は個人的に書きます: