2012-06-04 13 views
5

Próbuję zawinąć knockout.js w clojurescript, ale jego przekształcanie jest bardzo trudne. Problem, który mam, to odniesienie do "tej" zmiennej. Zastanawiam się nad porzuceniem i użyciem bezpośrednio javascript.zawijanie knockout.js przy użyciu clojurescript

Wziąłem przykłady off http://knockoutjs.com/examples/helloWorld.html i http://knockoutjs.com/examples/contactsEditor.html

udało mi się owinąć łatwe funkcje niektórych makr. Na przykład:

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

staje:

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

jednak czegoś mocniej odczuwalna:

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

Nie jestem pewien, co mogę zrobić w clojurescript dopasować kod jak poniżej: this.allItems.push(this.itemToAdd())

Jakieś myśli?

+0

Jeśli możesz trzymać mocno przez miesiąc, będziemy korzystać z otwartej biblioteki inspirowanej przez Knockout.js, której używamy wewnętrznie w Keming Labs. Miej oko na mój Github (@lynaghk). –

+0

Dzięki Kevin! Naprawdę nie mogę się doczekać zabawy z biblioteką. Istnieje jednak zbyt wiele świetnych bibliotek javascript, które mają podobne typy deklarowania zmiennych, które uzyskują dostęp do innych zmiennych wewnętrznych, których nie ma clojure. Uważam, że ważne jest, aby mieć jasny sposób interpolacji między js i cljs. Im więcej gram w clojurescript i ja javascript, tym bardziej odkrywam, że dobre biblioteki js są w zawiły sposób ... które widziałem tylko po nauce clojure. W każdym razie, mam nadzieję, że dostanę twoje komentarze na moją odpowiedź poniżej – zcaudate

+0

Spójrz na http://fluentsoftware.github.com/cljs-binding/, nie tak dojrzałe jak Knockout, ale .. – edtsech

Odpowiedz

5

Po wielu prób i błędów, zorientowałem się, jak mają taką samą strukturę clojurescript jako dla javascript.

this-as makro ma kilka dziwactw i działa tylko wtedy, gdy metoda jest umieszczona w klasie

na przykład chcę stworzyć coś, co wygląda jak ten w javascript:

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

muszę zrobić dużo więcej kodowanie aby uzyskać ten sam obiekt w clojurescript:

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

który jest poważnie brzydki dla języka tak piękne jak cloj ure. Sytuacja się pogarsza, gdy masz funkcje, które łączą się ze sobą, np. Co się dzieje w nokaucie.

Stwierdziłem, że najlepszym sposobem na utworzenie obiektu js z dużą ilością this jest zdefiniowanie metody __init__, dodanie jej do klasy, a następnie uruchomienie, a następnie usunięcie jej z klasy. Na przykład, jeśli chciałem zrobić inny obiekt:

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

napisany jako clojurescript zi __init__ metoda wygląda następująco:

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

Jest jeszcze cała masa więcej niż kod javascript ... ale najważniejsze jest to, że dostajesz ten sam obiekt co javascript. Ustawienie wszystkich zmiennych za pomocą tego formularza pozwala również na uproszczenie korzystania z makr. Więc teraz zdefiniowałem makro:

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

oraz z mapą-> js pobraną z jayq.utils:

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

Teraz mogę napisać kod tak:

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

i na odpowiedź na nokaut:

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

co jest naprawdę miłe dla mnie, jako że dopasowuje JavaScript naprawdę dobrze i jest całkowicie czytelny!

Powiązane problemy