在附件jsfiddle example中,我正在尝试创建一个页面,用于比较两架飞机的统计数据,用户可以从选择菜单中选择每个飞机。
我使用构造函数通过ID号为每个平面构建统计数据,并从不可编辑数据数组中提取“specs”(属性)。这适用于页面加载,但是当我更改ID的值时,例如“myPlaneId”,对象“MyPlaneSpecs”不会更新。
我应该以不同的方式接近这个吗?这是我的第一个Knockout项目,所以我知道我可能错过了一些明显的东西。
请参阅JS Fiddle demo。
在该示例中,您可以看到planeId正在更新,但整体平面规格不是。
<h1>Plane Comparison</h1>
Your plane: <select data-bind="options: planes, optionsValue: 'id', value: myPlaneId, optionsText: 'name'"></select>
<br>
Their plane: <select data-bind="options: planes, optionsValue: 'id', value: theirPlaneId, optionsText: 'name'"></select>
<h2>Your plane specs: </h2>
ID: <span data-bind="text: myPlaneId"></span> <br>
Name: <span data-bind="text: myPlaneSpecs().planeName"></span> <br>
Max Speed: <span data-bind="text: myPlaneSpecs().speed"></span> <br>
Climb Rate: <span data-bind="text: myPlaneSpecs().climbrate"></span>
<br><br>
<h2>Their plane specs: </h2>
ID: <span data-bind="text: theirPlaneId"></span> <br>
Name: <span data-bind="text: theirPlaneSpecs().planeName"></span> <br>
Max Speed: <span data-bind="text: theirPlaneSpecs().speed"></span> <br>
Climb Rate: <span data-bind="text: theirPlaneSpecs().climbrate"></span>
<script>
// Class to represent a chosen plane
function PlaneSpecs(specsArray) {
this.specs = ko.observable(specsArray);
this.planeId = this.specs().id;
this.planeName = this.specs().name;
this.speed = this.specs().speed;
this.climbrate = this.specs().climbrate;
}
// Overall viewmodel for this screen
function DogfightViewModel() {
var self = this;
self.myPlaneId = ko.observable(0);
self.theirPlaneId = ko.observable(1);
// Non-editable catalog data
self.planes = [
{ id: 0, name: "P-47D Thunderbolt", speed: 690, climbrate: 16, turntime: 25.8 },
{ id: 1, name: "BF 109 F4", speed: 660, climbrate: 18, turntime: 20.2 },
{ id: 2, name: "F6F-3 Hellcat", speed: 639, climbrate: 22.8, turntime: 21.8 }
];
myPlaneSpecs = ko.observable(
new PlaneSpecs(self.planes[self.myPlaneId()]) //This doesn't get updated when the ID changes
);
theirPlaneSpecs = ko.observable(
new PlaneSpecs(self.planes[self.theirPlaneId()])
);
}
ko.applyBindings(new DogfightViewModel());
</script>
答案 0 :(得分:1)
您应该使用ko.computed
来实现您想要的效果。如果您要创建一个函数来通过id获取该平面,如下所示(我的下面的版本没有任何错误处理或检查以确保该平面实际存在):
function getPlaneById(id){
var selectedPlane = self.planes.filter(function(plane){ return plane.id === id })[0];
return new PlaneSpecs(selectedPlane);
}
然后你可以改变你的myPlaneSpecs和他们的PlanApecs来计算:
self.myPlaneSpecs = ko.computed(function(){
return getPlaneById(self.myPlaneId());
});
self.theirPlaneSpecs = ko.computed(function(){
return getPlaneById(self.theirPlaneId());
});
此外,您并没有真正使用您在PlaneSpecs构造函数中创建的observable,所以我建议删除它,而是执行以下操作:
function PlaneSpecs(specs) {
this.planeId = specs.id;
this.planeName = specs.name;
this.speed = specs.speed;
this.climbrate = specs.climbrate;
}
您还可以使用with
绑定简化您的平面信息绑定,如下所示(可以对myPlaneSpecs
和theirPlaneSpecs
同时完成):
<div data-bind="with:myPlaneSpecs">
<h2>Your plane specs: </h2>
ID: <span data-bind="text: planeId"></span> <br>
Name: <span data-bind="text: planeName"></span> <br>
Max Speed: <span data-bind="text: speed"></span> <br>
Climb Rate: <span data-bind="text: climbrate"></span>
</div>
编辑:添加淘汰模板的使用。
由于您以相同的方式显示两个平面规格,您还可以为平面规格显示创建模板,如下所示:
<script type="text/html" id="planeTemplate">
ID: <span data-bind="text: planeId"></span> <br>
Name: <span data-bind="text: planeName"></span> <br>
Max Speed: <span data-bind="text: speed"></span> <br>
Climb Rate: <span data-bind="text: climbrate"></span>
</script>
之后,您可以使用该模板显示myPlaneSpecs
和theirPlaneSpecs
的平面规格,并带有以下标记:
<h2>Your plane specs: </h2>
<div data-bind="template:{ name: 'planeTemplate', data: myPlaneSpecs}"></div>
<h2>Their plane specs: </h2>
<div data-bind="template:{ name: 'planeTemplate', data: theirPlaneSpecs}"></div>
_编辑:添加指向更新的jsfiddle的链接
我已使用上述代码更新了您的小提琴,包括模板用法,您可以在http://jsfiddle.net/ar6dY/6/
找到答案 1 :(得分:1)
选项绑定集有几个怪癖(“options”,“optionsValue”,“optionsText”和“value”)。
如果使用optionsValue绑定,则“value”绑定的observable(myPlaneId)将使用每个选项的值设置(在您的情况下为“id”属性的值)。
当MyPlaneId值发生变化时,您没有任何内容会重新计算myPlaneSpecs observable上的属性。对于任何其他可观察到的变化,观察者不会改变。这就是计算的可观察量。
如果您不提供optionsValue绑定,那么整个对象文字将存储在后备observable中。
考虑到这一点,你可以大大简化这个例子
如果您不喜欢使用虚拟绑定,请将下面的HTML更改为将myPlane()或itsPlane()添加到每个相应的范围。
即
<span data-bind="text: id"></span>
到
<span data-bind="text: myPlane().id"></span>
<h1>Plane Comparison</h1>
Your plane:
<select data-bind="options: planes, value: myPlane, optionsText: 'name'"></select>
<br>
Their plane: <select data-bind="options: planes, value: theirPlane, optionsText: 'name'"></select>
<h2>Your plane specs: </h2>
<!-- ko with: myPlane -->
ID: <span data-bind="text: id"></span> <br>
Name: <span data-bind="text: name"></span> <br>
Max Speed: <span data-bind="text: speed"></span> <br>
Climb Rate: <span data-bind="text: climbrate"></span>
<!-- /ko -->
<br><br>
<h2>Their plane specs: </h2>
<!-- ko with: theirPlane -->
ID: <span data-bind="text: id"></span> <br>
Name: <span data-bind="text: name"></span> <br>
Max Speed: <span data-bind="text: speed"></span> <br>
Climb Rate: <span data-bind="text: climbrate"></span>
<!-- /ko -->
// Overall viewmodel for this screen
function DogfightViewModel() {
var self = this;
// Non-editable catalog data
self.planes = ko.observableArray( [
{ id: 0, name: "P-47D Thunderbolt", speed: 690, climbrate: 16, turntime: 25.8 },
{ id: 1, name: "BF 109 F4", speed: 660, climbrate: 18, turntime: 20.2 },
{ id: 2, name: "F6F-3 Hellcat", speed: 639, climbrate: 22.8, turntime: 21.8 }
] );
self.myPlane = ko.observable(self.planes()[0]);
self.theirPlane = ko.observable(self.planes()[1]);
}
ko.applyBindings(new DogfightViewModel());