DuoCode和Knockout

时间:2015-03-29 13:51:35

标签: c# knockout.js duocode

我将DuoCode视为TypeScript的替代品,因为它使用C#,这意味着我的开发人员可以使用他们已有的C#知识,但我们也可以重用,例如验证逻辑客户端和服务器上都有。

目前Knockout没有Binding,所以我创建了自己的,非常简单

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的回答的想法

  1. 比referenceAs好,但是类型安全太冗长和太少
  2. JsFunction与上面基本相同
  3. 似乎是最好的解决方案
  4. 我现在有了这个

    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数组。

1 个答案:

答案 0 :(得分:2)

我会推荐以下内容:

  1. 您可以使用 As 扩展方法,而不是使用 Js.referenceAs Bar.As<Action<string>>()("New Value");
  2. 另一个选择是让 Observable 继承自 JsFunction ,然后你可以像这样调用 invoke Bar.invoke("New Value");
  3. 另一种选择是在扩展方法内部,与您建议的类似。定义这样的方法:

    public static void Set<T>(this Observable<T> o, T value)
    {
      o.As<Action<T>>()(value);
    }
    

    (注意它必须是扩展方法,因为 Observable 类是extern,所以在不同的静态类中定义此方法) 然后你就可以这样使用它:Bar.Set("New value");

  4. 另外我建议将作为字段而不是属性(它会生成更干净的代码)

    (披露:我与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);