我使用了敲击3.x和Typescript,我想创建一个继承自KnockoutObservableArray<T>
的强类型集合类,这样我的类型可以是一个可观察的数组,但也有自己的方法。所以我尝试了这个:
export class Emails implements KnockoutObservableArray<Email>
{
//add a new email to the list
addItem() : void
{
var email = new ContactEmail();
...
this.push(email);
};
}
但是这给了我以下错误:
Class Emails declares interface KnockoutObservableArray<Email> but does not implement it:
Type 'Emails' is missing property 'extend' from type 'KnockoutObservableArray<Email>'.
如果我尝试添加extend
方法,那么它需要我实现peek
方法,依此类推,建议我需要实现所有KnockoutObservableArray
。但是我不想重新实现它,我想扩展它,因为KnockoutObservableArray
是一个界面而不是一个类,我没有看到任何方法来做到这一点。有办法吗?
答案 0 :(得分:2)
在不久的将来,这也是困扰我的事情。
为什么?
很简单,有两个原因。
的第一个原因是我从对象的角度来看并不是真正理解JavaScript。对我而言,它一直是一种&#34;脚本语言&#34;就是这样,我听到别人告诉我,但我(仍然是)是一个C#人,所以我基本上忽略了所说的内容,因为它看起来不像C#对象。
所有改变,当我开始使用Type Script时,TS以JS的形式向我展示JS输出的对象.J。
第二个原因是JS的灵活性,更重要的是淘汰赛。
我喜欢简单地只是在C#代码中更改我的JSON端点,而不是在前端进行任何更改,而不是对我的HTML进行一些调整。
如果我想在输出对象中添加一个新字段,那么我可以,然后只需向表中添加一个新列,绑定正确的字段名称和完成的工作。
太棒了。
确实是这样,直到我开始被要求提供基于行的功能。它开始很简单,例如能够删除行并执行内联编辑等操作。
这导致了许多奇怪的冒险,例如:
Knockout JS + Bootstrap + Icons + html binding
和此:
Synchronizing a button class with an option control using knockoutjs
但是这些请求变得越来越陌生,越来越复杂,我开始以疯狂的方式遍历DOM,从我所在的那一行开始,一直到父母,然后回到我的兄弟姐妹,然后把文本扯掉从相邻的元素和其他疯狂。
我开始与一个只知道和/或生活在JS领域的初级开发人员合作开展一个项目,他只是问我一个问题。
&#34;如果这一切都给你造成了太大的痛苦,那么为什么不为你的行创建一个视图模型?&#34;
所以下面的代码诞生了。
var DirectoryEntryViewModel = (function ()
{
function DirectoryEntryViewModel(inputRecord, parent)
{
this.Pkid = ko.observable(0);
this.Name = ko.observable('');
this.TelephoneNumber = ko.observable('');
this.myParent = parent;
ko.mapping.fromJS(inputRecord, {}, this);
}
DirectoryEntryViewModel.prototype.SomePublicFunction = function ()
{
// This is a function that will be available on every row in the array
// You can call public functions in the base VM using "myParent.parentFunc(blah)"
// you can pass this row to that function in the parent using "myParent.someFunc(this)"
// and the parent can simply do "this.array.pop(passedItem)" or simillar
}
return DirectoryEntryViewModel;
})();
var IndexViewModel = (function ()
{
function IndexViewModel(targetElement)
{
this.loadComplete = ko.observable(false);
this.phoneDirectory = ko.observableArray([]);
this.showAlert = ko.computed(function ()
{
if (!this.loadComplete())
return false;
if (this.phoneDirectory().length < 1)
{
return true;
}
return false;
}, this);
this.showTable = ko.computed(function ()
{
if (!this.loadComplete())
return false;
if (this.phoneDirectory().length > 0)
{
return true;
}
return false;
}, this);
ko.applyBindings(this, targetElement);
$.support.cors = true;
}
IndexViewModel.prototype.Load = function ()
{
var _this = this;
$.getJSON("/module3/directory", function (data)
{
if (data.length > 0)
{
_this.phoneDirectory(ko.utils.arrayMap(data, function (item)
{
return new DirectoryEntryViewModel(item, _this);
}));
} else
{
_this.phoneDirectory([]);
}
_this.loadComplete(true);
});
};
return IndexViewModel;
})();
window.onload = function ()
{
var pageView = document.getElementById('directoryList');
myIndexViewModel = new IndexViewModel(pageView);
myIndexViewModel.Load();
};
现在这不是想象中任何一个最好的例子(我只是把它从我不得不提出的项目中拉出来),但它确实有效。
是的,您必须确保如果在JSON中向后端添加字段,您还要将其添加到行视图模型(可以使用RequireJS或类似版本加载),以及添加该列在需要的地方/你的表/列表/其他标记。
但是为了额外的一两行,你可以轻松地添加一个函数,然后在集合的每一行都可用。
而且,就打字稿而言,上面的整个JS都是由TS编译器从实现相同模式的TS源生成的。
我有很多东西以这种方式运行(我的github页面上有几个例子 - http://github.com/shawty你可以克隆),我运行的一些应用程序有完整的视图模型基于计算的observable中的单个简单更改来调整整个UI,我有行管理它们自己的状态(包括直接与数据库交谈),然后在操作成功后更新它们在父表中的状态。
希望这会给你更多的思考。虽然你可以在你的路上摔跤,试图扩展现有的KO课程,但我认为你会发现上述模式更容易掌握。
我尝试过和你一样的方法,但是一旦我的JS朋友指出为行创建一个VM并重新使用它是多么容易,我就相当快地放弃了它。
希望它有所帮助 美女
答案 1 :(得分:2)
您实际需要的是extends
KnockoutObservableArray而不是implements
类的类。
然而,扩展需要实现而不是接口,并且这种实现在Typescript中不可用。只有一个静态界面KnockoutObservableArrayStatic
返回一个实现,完全类似于淘汰赛的ko.observableArray
实施。
我用favoring composition over inheritance解决了我的类似案例。例如:
export class Emails
{
collection = ko.observableArray<Email>();
//add a new email to the list
addItem() : void
{
var email = new ContactEmail();
...
this.collection.push(email);
};
}
您可以访问集合属性以绑定到DOM,或者如果您需要observableArray的任何功能:
<div data-bind="foreach: myEmails.collection">
...
</div>
答案 2 :(得分:1)
implements
关键字用于表示您将实现提供的接口...为了满足这个条件,您需要实现以下接口中定义的所有方法(因为您需要从KnockoutObservableArray<T>
开始关注整个链。
或者,创建一个较小的界面来表示您在自定义类中实际需要的内容,如果它是所有这些的子集,您将能够将您的界面用于自定义类以及正常类KnockoutObservableArray<T>
个类(将在结构上通过接口测试)。
interface KnockoutObservableArray<T> extends KnockoutObservable<T[]>, KnockoutObservableArrayFunctions<T> {
extend(requestedExtenders: { [key: string]: any; }): KnockoutObservableArray<T>;
}
interface KnockoutObservable<T> extends KnockoutSubscribable<T>, KnockoutObservableFunctions<T> {
(): T;
(value: T): void;
peek(): T;
valueHasMutated?:{(): void;};
valueWillMutate?:{(): void;};
extend(requestedExtenders: { [key: string]: any; }): KnockoutObservable<T>;
}
interface KnockoutObservableArrayFunctions<T> {
// General Array functions
indexOf(searchElement: T, fromIndex?: number): number;
slice(start: number, end?: number): T[];
splice(start: number): T[];
splice(start: number, deleteCount: number, ...items: T[]): T[];
pop(): T;
push(...items: T[]): void;
shift(): T;
unshift(...items: T[]): number;
reverse(): T[];
sort(): void;
sort(compareFunction: (left: T, right: T) => number): void;
// Ko specific
replace(oldItem: T, newItem: T): void;
remove(item: T): T[];
remove(removeFunction: (item: T) => boolean): T[];
removeAll(items: T[]): T[];
removeAll(): T[];
destroy(item: T): void;
destroy(destroyFunction: (item: T) => boolean): void;
destroyAll(items: T[]): void;
destroyAll(): void;
}
interface KnockoutSubscribable<T> extends KnockoutSubscribableFunctions<T> {
subscribe(callback: (newValue: T) => void, target?: any, event?: string): KnockoutSubscription;
subscribe<TEvent>(callback: (newValue: TEvent) => void, target: any, event: string): KnockoutSubscription;
extend(requestedExtenders: { [key: string]: any; }): KnockoutSubscribable<T>;
getSubscriptionsCount(): number;
}
interface KnockoutSubscribableFunctions<T> {
notifySubscribers(valueToWrite?: T, event?: string): void;
}
interface KnockoutObservableFunctions<T> {
equalityComparer(a: any, b: any): boolean;
}
以下是替代示例...假设您需要push
和remove
作为示例。 (旁注:继承是不可能的,因为这些都是接口 - 如果Knockout支持继承,可以更改类型定义以允许免工作extends
而不是努力工作implements
这个答案的版本......)
这个例子中有趣的是第一个界面 - 它是你定义的一个自定义界面,但是匹配它的任何东西都可以,包括KnockoutObservableArray<Email>
。这是一个结构类型的例子,可以节省你一些时间。结构打字要你去酒吧。
/// <reference path="scripts/typings/knockout/knockout.d.ts" />
interface MyObservableArray<T> {
push(...items: T[]): void;
remove(item: T): T[];
}
class Email {
user: string;
domain: string;
}
class ObservableEmailArray implements MyObservableArray<Email> {
push(...items: Email[]): void {
}
remove(item: Email): Email[] {
return [];
}
}
var observableArrayOne: KnockoutObservableArray<Email> = ko.observableArray<Email>();
var observableArrayTwo: MyObservableArray<Email> = new ObservableEmailArray();
function example(input: MyObservableArray<Email>) {
// Just an example - will accept input of type MyObservableArray<Email>...
}
example(observableArrayOne);
example(observableArrayTwo);
答案 3 :(得分:0)
我遇到了同样的问题并找到了解决方法。
export class ShoppingCartVM {
private _items: KnockoutObservableArray<ShoppingCartVM.ItemVM>;
constructor() {
this._items = ko.observableArray([]);
var self = this;
(<any>self._items).Add = function() { self.Add(); }
return <any>self._items;
}
public Add() {
this._items.push(new ShoppingCartVM.ItemVM({ Product: "Shoes", UnitPrice: 1, Quantity: 2 }));
}
}
您无法将ShoppingCartVM转换为KnockoutObservableArray,但您可以引入像Steve Fenton回答的常见界面。