【问题标题】:wrapping knockout.js using clojurescript使用 clojurescript 包装 knockout.js
【发布时间】:2012-06-04 06:30:00
【问题描述】:

我正在尝试将 knockout.js 包装在 clojurescript 中,但它变得非常困难。我遇到的问题是对“this”变量的引用。我正在考虑放弃并直接使用javascript。

我以http://knockoutjs.com/examples/helloWorld.htmlhttp://knockoutjs.com/examples/contactsEditor.html 为例

我已经设法用一些宏来包装简单的函数。例如:

var ViewModel = function() {
    this.firstName = ko.observable("Bert");
    this.lastName = ko.observable("Bertington");

    this.fullName = ko.computed(function() {
        // Knockout tracks dependencies automatically. It knows that fullName depends on firstName and lastName, because these get called when evaluating fullName.
        return this.firstName() + " " + this.lastName();
    }, this);
};

变成:

(defviewmodel data
  (observing :first_name "Bert")
  (observing :last_name  "Bertington")
  (computing :name [:first_name :last_name]
    (str :first_name " " :last_name)))

但是,对于更难的事情,例如:

var BetterListModel = function () {
    this.itemToAdd = ko.observable("");
    this.allItems = ko.observableArray(["Fries", "Eggs Benedict", "Ham", "Cheese"]); // Initial items
    this.selectedItems = ko.observableArray(["Ham"]);                                // Initial selection

    this.addItem = function () {
        if ((this.itemToAdd() != "") && (this.allItems.indexOf(this.itemToAdd()) < 0)) // Prevent blanks and duplicates
            this.allItems.push(this.itemToAdd());
        this.itemToAdd(""); // Clear the text box
    };

    this.removeSelected = function () {
        this.allItems.removeAll(this.selectedItems());
        this.selectedItems([]); // Clear selection
    };

    this.sortItems = function() {
        this.allItems.sort();
    };
};

ko.applyBindings(new BetterListModel());

我不确定我可以在 clojurescript 中做什么来匹配这样的代码:this.allItems.push(this.itemToAdd())

有什么想法吗?

【问题讨论】:

  • 如果你能坚持一个月,我们将开源我们在 Keming Labs 内部使用的受 Knockout.js 启发的计算可观察库。关注我的 Github (@lynaghk)。
  • 谢谢凯文!我真的很期待和图书馆一起玩。但是,有太多优秀的 javascript 库存在类似类型的问题,即声明访问 clojure 没有的其他内部变量的变量。我觉得在 js 和 cljs 之间有一种清晰的插值方式很重要。我玩clojurescript ande javascript的次数越多,我就越发现好的js库是以一种lispy的方式......我在学习clojure之后才看到这种联系。无论如何,希望在下面我的回答中得到您的 cmets
  • 看看fluentsoftware.github.com/cljs-binding,没有Knockout那么成熟,但是..

标签: clojure knockout.js clojurescript


【解决方案1】:

经过大量的反复试验,我想出了如何使 clojurescript 的结构与 javascript 的结构相同。

this-as 宏有一些特性,仅在将方法放入类时才有效

例如,我想在 javascript 中创建如下所示的内容:

var anobj = {a: 9,
             get_a: function(){return this.a;}};

我必须做更多的编码才能在 clojurescript 中获得相同的对象:

(def anobj (js-obj))
(def get_a (fn [] (this-as me (.-a me))))
(aset anobj "a" 9)
(aset anobj "get_a" get_a)

对于像 clojure 这样美丽的语言来说,这是非常丑陋的。当您拥有相互链接的功能时,情况会变得更糟,例如淘汰赛中发生的情况。

我发现创建一个包含大量 this 的 js 对象的最佳方法是定义一个 __init__ 方法,将其添加到类中然后运行它,然后将其从类中删除.例如,如果我想制作另一个对象:

var avobj = {a: this,
             b: 98,
             c: this.a
             get_a: function(){return str(this.a) + str(this.c);}};

写成 clojurescript 和 __init__ 方法看起来像这样:

(def avobj (js-obj))
(def av__init__ 
     #(this-as this 
        (aset this "a" this) 
        (aset this "b" 9) 
        (aset this "c" (.-a this))
        (aset this "get_a" (fn [] (str (.-a this) (.-c this))))))
(aset avobj "__init__" av__init__)
(. avobj __init__)
(js-delete stuff "__init__")

仍然有比 javascript 更多的代码...但最重要的是您获得与 javascript 相同的对象。使用这种形式设置所有变量还可以简化宏的使用。所以现在我定义了一个宏:

(defmacro defvar [name & body]
   (list 'do
    (list 'def name
      (list 'map->js 
        {
          :__init__ 
          (list 'fn []
              (list 'this-as 'this
                 (list 'aset 'this "a" "blah")))          
        }))
    ;(. js/console log ~name)
    (list '. name '__init__)
    (list 'js-delete name "__init__")))

并使用从 jayq.utils 获取的 map->js:

(defn map->js [m]
  (let [out (js-obj)]
    (doseq [[k v] m]
      (aset out (name k) v))
    out))

现在我可以这样写代码了:

(defvar avobj
   a this 
   b 9 
   c (.-a this)
   get_a (fn [] (str (.-a this) (.-c this))))

对于淘汰赛的答案:

(defvar name_model
    first_name (observable "My")
    last_name (observable "Name")
    name (computed (fn [] (str (. this first_name) " " (. this last_name)))))

(. js/ko (applyBindings name_model));

这对我来说真的很好,因为它非常适合 javascript 并且完全可读!

【讨论】:

    【解决方案2】:

    如果您需要明确引用 JavaScript 的 this 动态绑定,ClojureScript 提供了一个 this-as 宏:

    https://github.com/clojure/clojurescript/blob/master/src/clj/cljs/core.clj#L324

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-22
      • 2012-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-10
      • 2013-04-29
      • 1970-01-01
      相关资源
      最近更新 更多