我正在尝试学习Typescript和Knockout,并开始观看有关Pluralsight的在线课程。当我遇到一些我不了解并且无法找到帮助的行为时,我正在玩这些课程材料。如果我遗漏了一些明显的东西或者没找对地方,请告诉我。否则,我希望这也能帮助那些有这个问题的人。
在我的打字稿类中,如果我这样做 -
this.extendedPrice = ko.computed(function () {
return this.product()
? this.product().salesPrice() * 0.8 + parseFloat(this.product().salesPrice())
: 0;
},
OR
this.extendedPrice = ko.computed({
read: function () {
return this.product()
? this.product().salesPrice() * 0.8 + parseFloat(this.product().salesPrice())
: 0;
},
owner: this,
});
一切正常,我可以使用其中任何一个绑定 -
<input data-bind="visible:product, value:extendedPrice"/>
OR
<input data-bind="visible:product, value:extendedPrice()"/>
但是一旦我介绍&#34;写:&#34;进入上面的
this.extendedPrice = <KnockoutComputed<number>>ko.computed({
read: function () {
return this.product()
? this.product().salesPrice() * 0.8 + parseFloat(this.product().salesPrice())
: 0;
},
write: function (value: string) {
var num = parseFloat(value.replace(/[^\.\d]/g,""));
num = isNaN(num) ? 0 : num;
var unitPrice = num / this.quantity();
this.product().salesPrice(unitPrice);
},
owner: this,
});
我观察到两件我不理解的事情 -
我无法使用<input data-bind="visible:product, value:extendedPrice()"/>
了。绑定仅在页面加载后最初起作用,此后salesPrice中的更改不会反映在extendedPrice中。只有<input data-bind="visible:product, value:extendedPrice"/>
按预期工作。
我在this.extendedPrice = ko.computed(....
语句中收到错误,除非我使用<KnockoutComputed<number>>
进行投射(如上面的代码所示)。错误说 -
"Cannot convert KnockoutComputed<{}> to KnockoutComputed<number>. Types of property 'peek' of types KnockoutComputed<{}> and KnockoutComputed<number> are incompatible:Call signatures of types '()=>{}' and '()=>number' are incompatible.(property)guitarsalesportalmodule.LineItem.extendedPrice: KnockoutComputed<number>"
我正在使用Knockout v3.1.0。
我想理解为什么Knockout在添加&#34;写的功能时会这样做:&#34;。
谢谢。
编辑1
抱歉,我之前应该提到我已经尝试过lambda而不是函数,因为我读过很多文章都提到了lambda如何处理this
。但是,它并不适合我。
我在这种情况下观察到的问题 -
我仍然需要使用ko.computed(....)
投射<KnockoutComputed<number>>
。
<input data-bind="visible:product, value:extendedPrice"/>
适用于更新salesPrice导致新的extendedPrice反映(read:
部分),反之亦然(write:
部分)的方式,但<input data-bind="visible:product, value:extendedPrice()"/>
只有read:
部分似乎有用
这是我的TS和HTML的样子。
TS
///<reference path="data.ts" />
///<reference path="../typedefinitions/jquery.d.ts" />
///<reference path="../typedefinitions/knockout.d.ts" />
module guitarsalesportalmodule {
export class Mod {
constructor(public Name: string, public Id: number) { }
}
export class Cat {
constructor(public Name: string, public Id: number) { }
}
export class Product {
salesPrice: KnockoutObservable<number>;
constructor(public modelId: number, sp: number, public listPrice: number, public rating: number, public photo: string, public description: string, public model: Mod, public category: Cat) {
this.salesPrice = ko.observable(sp);
}
}
export class LineItem {
product: KnockoutObservable<Product>;
quantity: KnockoutObservable<number>;
extendedPrice: KnockoutComputed<number>;
constructor(product: Product, quantity: number) {
this.product = ko.observable(product);
this.quantity = ko.observable(quantity);
this.extendedPrice = <KnockoutComputed<number>>ko.computed({
read: ()=>{
return this.product()
? this.product().salesPrice() * 0.8 + parseFloat(this.product().salesPrice().toString())/*Just leaving it as this.product().salesPrice() results in it being considered string and getting concatenated. Doing parseFloat(this.product().salesPrice().toString()) instead of parseFloat(this.product().salesPrice()) strangely doesn't work either saying it's not a string but a number(doesn't happen when I use function instead of ()=>).*/
: 0;
},
write: (value: string) => {
var num = parseFloat(value.replace(/[^\.\d]/g,""));
num = isNaN(num) ? 0 : num;
var unitPrice = num / this.quantity();
this.product().salesPrice(unitPrice);
},
owner: this,
});
}
}
export class Vm {
products: KnockoutObservableArray<Product>;
lines: KnockoutObservableArray<LineItem>;
grandTotal: KnockoutComputed<number>;
constructor() {
this.loadProducts();
this.lines = ko.observableArray([new LineItem(undefined, undefined)]);
this.grandTotal = ko.computed(
() => {
var total = 0;
this.lines().forEach((line) => {
if (line.extendedPrice() && line.quantity()) {
total += line.extendedPrice() * line.quantity();
}
});
return total;
},
this);
}
loadProducts() {
this.products = ko.observableArray([]);
$.each(data.Products, (i) => {
this.products.push(new Product(
data.Products[i].ModelId,
data.Products[i].SalePrice,
data.Products[i].ListPrice,
data.Products[i].Rating,
data.Products[i].Photo,
data.Products[i].Description,
data.Products[i].Model,
data.Products[i].Category));
});
}
addLineItem = () => {
this.lines.push(new LineItem(undefined, undefined));
}
removeLineItem = (line) => {
this.lines.remove(line);
}
formatCurrency(curr: string) {
return "$" + parseFloat(curr).toFixed(2);
}
}
export var viewModel = new Vm();
}
//apply bindings
window.onload = () => {
ko.applyBindings(guitarsalesportalmodule.viewModel);
};
HTML
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<link href="../css/fonts.css" rel="stylesheet" />
<link href="../css/styles.css" rel="stylesheet" />
<script src="../scripts/jquery-2.1.0.js"></script>
<script src="../scripts/knockout-3.1.0.js"></script>
<script src="../scripts/data.js"></script>
<script src="../scripts/guitarsalesportal.js"></script>
</head>
<body>
<div class="showroom">
<table>
<tr>
<th>Product</th>
<th>Price</th>
<th>Quantity</th>
<th>Extended Price</th>
<th>Remove</th>
</tr>
<tbody data-bind="foreach: lines">
<tr>
<td>
<select data-bind="options:$parent.products, value:product, optionsText:'description', optionsCaption:'Choose...'"></select>
</td>
<td data-bind="if:product">
<input data-bind="value:product().salesPrice"/>
</td>
<td>
<input data-bind="visible:product, value:quantity"/>
</td>
<td>
<input data-bind="visible:product, value:extendedPrice()"/>
<!--<input data-bind="visible:product, value:extendedPrice"/>-->
</td>
<td>
<a href="#" data-bind="click: $parent.removeLineItem">Remove</a>
</td>
</tr>
</tbody>
</table>
<a href="#" data-bind="click: addLineItem">Add</a>
<div>Grand Total: <span data-bind="text:formatCurrency(grandTotal()), valueUpdate:'afterkeydown'"></span></div>
</div>
</body>
</html>
谢谢。
答案 0 :(得分:1)
最有可能this
是错误的。尝试使用lambda而不是function
:
this.extendedPrice = ko.computed( () => { // Notice () =>
return this.product()
? this.product().salesPrice() * 0.8 + parseFloat(this.product().salesPrice())
: 0;
},
这将确保this
独立于调用上下文。
答案 1 :(得分:1)
关于第2项:
Types of property 'peek' of types [...] are incompatible
这是因为extendedPrice
被声明为number
,但写入块中的value
参数是string
。如果您将其更改为:
write: (value: number)