我正在将
DuoCode作为TypeScript的替代品,因为它使用C#,这意味着我的开发人员可以使用他们已有的C#知识,但我们也可以在客户端和服务器上重用例如验证逻辑.
目前没有Binding for Knockout所以我创建了自己的,很简单
namespace Knockout
{
[Js(Name = "ko", Extern = true)]
public static class Global
{
[Js(Name = "observable", OmitGenericArgs = true)]
public static extern Observable<T> Observable<T>();
[Js(Name = "observable", OmitGenericArgs = true)]
public static extern Observable<T> Observable<T>(T value);
[Js(Name = "computed", OmitGenericArgs = true)]
public static extern Observable<T> Computed<T>(Func<T> computed);
[Js(Name = "applyBindings")]
public static extern void ApplyBindings(object viewModel);
[Js(Name = "unwrap", OmitGenericArgs = true)]
public static extern T Unwrap<T>(Observable<T> observable);
}
[Js(Name = "ko.observable", Extern = true)]
public class Observable<T>
{
//TODO: Add more methods like subscribe, extend etc
}
}
这是一个使用它的简单模型
namespace ViewModels
{
public class FooViewModel
{
public FooViewModel()
{
Bar = Global.Observable("HelloWorld");
Computed = Global.Computed(() => Global.Unwrap(Bar) + "COMPUTED");
}
public Observable<string> Bar { get; set; }
public Observable<string> Computed { get; set; }
}
}
Computed func可以使用Global.Unwrap使用底层的可观察值,该值转换为客户端上的ko.unwap
但是为了设定价值,我没有提出一个坚实的解决方案,只有我发现的解决方案
Js.referenceAs<Action<string>>("this.Bar")("New Value");
哪种方法有许多缺点可以成为公认的解决方案
有任何想法吗?
编辑:扩展方法使它更好一点,但有一个缺点,你现在需要包括使用Knockout绑定类库编译的javascript
public static class ObservableExtensions
{
public static void SetValue<T>(this Observable<T> observable, T value)
{
Js.referenceAs<Action<T>>("observable")(value);
}
}
关于Yoav答案的想法
>比参考更好,但是类型安全太冗长和太少
> JsFunction与上面基本相同
>似乎是最好的解决方案
我现在有这个
public static class ObservableExtensions
{
public static void Set<T>(this Observable<T> observable, T value)
{
observable.As<Action<T>>()(value);
}
public static T Get<T>(this Observable<T> observable)
{
return observable.As<Func<T>>()();
}
}
它有点难过,因为它引入了一个不需要的额外函数调用
Knockout.ObservableExtensions.Set(String, this.get_Bar(), "New value");
而不仅仅是
this.get_Bar()("New value");
我有另外一个问题,我也在看淘汰观察阵列,我有这个
[Js(Name = "ko.observableArray", Extern = true)]
public class ObservableArray<T> : Observable<JsArray<T>>
{
[Js(Name = "push", OmitGenericArgs = true)]
public extern void Push(T value);
}
在我的Global KO静态课上,我有
[Js(Name = "observableArray", OmitGenericArgs = true)]
public static extern ObservableArray<T> ObservableArray<T>(T[] values);
我已经为方法参数尝试了不同的值,如JsArray,IEnumerable等,它们都在客户端生成相同的代码
ko.observableArray($d.array(System.Int32, [1, 2, 3, 4]));
这将失败,因为Knockout observable数组需要一个正常的Javascript数组.
最佳答案 我会推荐以下内容:
>而不是使用Js.referenceAs,你可以像这样使用As扩展方法:Bar.As< Action< string>>()(“New Value”);
>另一种选择是使Obsable继承自JsFunction,然后你可以调用这样的调用:Bar.invoke(“New Value”);
>另一种选择是在扩展方法内部,与您建议的类似.定义这样的方法:
public static void Set<T>(this Observable<T> o, T value)
{
o.As<Action<T>>()(value);
}
(注意,它必须是一个扩展方法,因为Observable类是extern,所以在另一个静态类中定义此方法)
然后你可以像这样使用它:Bar.Set(“New value”);
另外我建议将Bar设为字段而不是属性(它会生成更干净的代码)
(披露:我与DuoCode开发人员合作)
编辑
我同意,扩展方法是目前最好的选择.也许在不久的将来他们会支持这样的事情:
[Js(Name="")]
public void SetValue(T value)
关于你的第二个问题:查看mscorlib.js中的代码,$d.array创建了普通数组(只是为运行时类型信息提供了一些额外的属性).这里的问题是它为类似Int32的类型创建了类型化数组.所以我猜你可以创建一个对象数组而不是T,它应该工作,如下所示:
[Js(Name = "observableArray", OmitGenericArgs = true)]
public static extern ObservableArray<T> ObservableArray<T>(object[] values);