2012-06-04 14 views
5

Estoy tratando de envolver knockout.js en clojurescript pero está volviéndose muy difícil. El problema que estoy teniendo es la referencia a la variable 'this'. Estoy pensando en renunciar y solo usar javascript directamente.envolviendo knockout.js usando clojurescript

He tomado ejemplos de http://knockoutjs.com/examples/helloWorld.html y http://knockoutjs.com/examples/contactsEditor.html

He conseguido envolver funciones fáciles con algunas macros. Por ejemplo:

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); 
}; 

se convierte en:

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

Sin embargo, algo más difícil como:

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()); 

No estoy seguro de lo que puedo hacer en clojurescript para que coincida con un código como este: this.allItems.push(this.itemToAdd())

¿Alguna idea?

+0

Si puedes mantenerte firme durante un mes, estaremos comprando abiertamente la biblioteca observable computada inspirada en Knockout.js que hemos estado usando internamente en Keming Labs. Vigila mi Github (@lynaghk). –

+0

Gracias Kevin! Estoy deseando jugar con la biblioteca. Sin embargo, existen demasiadas bibliotecas de javascript geniales que tienen problemas similares de declaración de variables que acceden a otras variables internas que no tiene Clojure. Siento que es importante tener una forma clara de interpolar entre js y cljs. Cuanto más juego con clojurescript y javascript, más me doy cuenta de que las bibliotecas de js buenas están en una forma lisa ... que solo vi la conexión después de aprender clojure. De todos modos, espero obtener tus comentarios sobre mi respuesta a continuación, – zcaudate

+0

. Echa un vistazo a http://fluentsoftware.github.com/cljs-binding/, no tan maduro como Knockout, pero ... – edtsech

Respuesta

5

Después de mucho ensayo y error, he descubierto la manera de tener la misma estructura para clojurescript como para javascript.

La macro this-as tiene algunas particularidades y sólo funciona cuando el método se pone en la clase

por ejemplo, quiero crear algo que se parece a esto en javascript:

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

tengo que hacer mucho más de codificación para conseguir el mismo objeto en clojurescript:

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

que es serio feo para un lenguaje tan bello como cloj ure. Las cosas empeoran cuando tienes funciones que se vinculan entre sí, como lo que sucede en el golpe de gracia.

Encontré que la mejor manera de crear un objeto js con un montón de this es definir un método __init__, agregarlo a la clase y luego ejecutarlo, luego eliminarlo de la clase. Por ejemplo, si quería hacer otro objeto:

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

escrito como clojurescript con __init__ y método es el siguiente:

(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__") 

Todavía hay un montón más código que javascript ... pero el Lo más importante es que obtienes el mismo objeto que javascript. Establecer todas las variables usando este formulario también permite el uso de macros para simplificar. Así que ahora he definido una macro:

(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__"))) 

y con map-> js tomado de jayq.utils:

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

Ahora puedo escribir código como este:

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

y la respuesta a Knockout:

(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)); 

que es muy agradable para mí, ya que coincide javascript muy bien ¡y es completamente legible!