2011-08-06 10 views
21

Mam ogólną podklasę Backbone.View, która ma odbiornik zdarzeń close.Sub Class a Backbone.View Sub Class i zachowania zdarzeń

var GenericView = Backbone.View.extend({ 

    events : { 
     "click .close" : "close" 
    }, 

    close : function() { 
     console.log("closing view"); 
    } 

}); 

Chcę dokonać podklasy tej ogólnej klasy i dodać kilka nowych zdarzeń. Jednak poniższy tekst spowoduje nadpisanie super obiektu (powyżej) obiektu zdarzenia. Na przykład.

var ImplementedView = new GenericView({ 

    // setting this will stop 'close' in Super Class from triggering 
    events : { 
     "click .submit" : "submit" 
    } 

}); 

W jaki sposób utworzyć podklasę, w tym przypadku ImplementedView i zachować zdarzenia?

Znalazłem jeden sposób, aby to osiągnąć, rozszerzając obiekt zdarzenia, gdy konstruowana jest klasa potomna. Jednak muszę ponownie uruchomić this.delegateEvents(), który, jak sądzę, nie jest dobry. Czy ktoś może to komentować?

var ImplementedView = new GenericView({ 

    initialize : function (options) { 

     _.extend(this.events, { 
      "click .submit" : "submit" 
     }); 

     // re-attach events 
     this.delegateEvents(); 
    } 

}); 
+0

Mogę się mylić, ale nie widzę, żebyś gdzieś podklasy? ImplementedView jest po prostu zmienną odnoszącą się do instancji GenericView ... – PhD

+0

Musisz jawnie użyć '_.rozszerzyć (...)' lub mieć podobną funkcjonalność jak metoda '.extend' widoków ... przez backbone.js, aby uzyskać wskazówkę. Ale jeśli mam rację, to z powodu braku wyraźnego dziedzictwa w łańcuchu prototypów. Wywoływanie 'this.delegateEvents()' lub 'Backbone.View.prototype.delegateEvents.call (events)' wydaje się być sposobem, aby przejść – PhD

+0

Będę przeglądać dokumenty. powtórz swój pierwszy komentarz: Nie dzwonię do nowego operatora, choć to było podklasy poprzez rozszerzenie Backbone.Model? – Ross

Odpowiedz

28

@Nupul jest dokładnie rację: nie jesteś instacji swoją GenericView.

W rzeczywistości podklasowanie nie jest tutaj właściwym słowem, ponieważ JavaScript nie dziedziczy klasycznego dziedziczenia.

Więc najpierw postarać się zrozumieć, co się dzieje tutaj:

var GenericView = Backbone.View.extend(propertiesObj, classPropertiesObj) 

Backbone.View jest funkcją konstruktora, że ​​po wywołaniu ze słowem kluczowym new, tworzy nowy obiekt dla Ciebie.

Ponieważ jest JS, wszystkie funkcje są naprawdę funkcjonują obiekty, więc Backbone.View.extend jest tylko funkcja dyndał Backbone.View że robi kilka rzeczy:

  1. konfiguruje łańcuch prototypów, dzięki czemu można uzyskać dostęp do właściwości i funkcje zdefiniowane w „bazie” zadzwoń klasa
  2. tworzy i zwraca nową funkcję konstruktora można zadzwonić (który tutaj byłby GenericView) instancji obiektów klasy dziedziczenie
  3. kopiuje się być funkcją wiszące off funkcja konstruktora, którą zwraca aby móc dalej dziedziczyć.

więc poprawny sposób założyć łańcuch protoype chcesz to:

var ImplementedView = GenericView.extend({ 
    // implementation goes here 
}); 

a NIE:

var ImplementedView = new GenericView({//stuff}); 

ponieważ to właśnie tworzy nową instancję GenericView.

Teraz, nadal masz problem, bo gdy zrobisz coś takiego:

var impl_view = new ImplementedView; 

impl_view.events; // This isn't merged with the events you created 
        // for the GenericView 

W tym momencie istnieją różne sposoby, aby uzyskać wynik, który chcesz, tutaj jest jeden, który wykorzystuje delegateEvents niby jak, w jaki sposób zrobił. Używanie go nie jest złe, nawiasem mówiąc.

var GenericView = Backbone.View.extend({ 

    genericEvents: { 'click .close': 'close' }, 

    close: function() { console.log('closing view...'); } 

}); 

var ImplView = GenericView.extend({ 

    events: { 'click .submit': 'submit' }, 

    initialize: function(options) { 
    // done like this so that genericEvents don't overwrite any events 
    // we've defined here (in case they share the same key) 
    this.events = _.extend({}, this.genericEvents, this.events); 
    this.delegateEvents() 
    } 
}); 
+0

dzięki za szczegółową odpowiedź. –

+2

To jest dobra odpowiedź. Chociaż wadą jest konieczność wklejenia: 'this.events = _.extend (this.genericEvents, this.events);' w init każdego wydziału. Ale przypuszczam, że mogę sobie poradzić. Chciałbym jednak, żeby była bardziej automatyczna. –

+0

Kolejność parametrów jest odwrotna w tym wierszu 'this.events = _.extend (this.genericEvents, this.events); ' Powinien rzeczywiście przeczytać -' this.events = _.extend (this.events, this.genericEvents); '. Pierwszym parametrem jest miejsce docelowe, drugim jest źródło. –

1

Można znaleźć to przydatne: http://kalimotxocoding.blogspot.com/2011/03/playing-with-backbonejs-views.html

sekund Co Wspomniałem o _.extend(...) i co masz obecnie ...

+1

Po prostu chcę powiedzieć, że zamieszczone powyżej kalimotxocoding.blogspot.com nie rozwiązuje problemu i jest szczególnie okropne. Nie polecam sprawdzania tego. –

+1

Nie patrz na hak szkieletu. Spójrz na pierwszy sposób, w którym musisz jawnie rozszerzyć zdarzenia widoku nadrzędnego w widoku podrzędnym. – PhD

27

Inną opcją jest posiadanie BaseView, który zastępuje implementację Extend. Takich jak:

var BaseView = Backbone.View.extend({ 
     //base view functionality if needed 
    }); 
BaseView.extend = function(child){ 
    var view = Backbone.View.extend.apply(this, arguments); 
    view.prototype.events = _.extend({}, this.prototype.events, child.events); 
    return view; 
}; 

Spowoduje to automatyczne rozszerzenie wszystkich wydarzeń dla wszystkiego, co dziedziczy z BaseView.

+0

Ładne rozwiązanie, które centralizuje zachowanie, a nie rozprowadza je tak, jak inne rozwiązania. – WiredPrairie

+0

Bardzo ładne. Czy jest jakiś powód, dla którego robi to domyślnie w Backbone? Być może nie chcieli przesadzić z przedłużeniem()? – tybro0103

10

Lubię rozwiązanie pana golarka, ale czegoś mniej inwazyjny:

var ChildView = ParentView.extend({ 
    events : _.extend({ 
     "change input": "handleInputChange" 
    }, ParentView.prototype.events) 
}); 

Edit: Tak, trzeba to zrobić w każdej klasie dzieci, ale jest to przydatne, gdy są potrzebne tylko kilka dzieci lub gdy nie kontrolujesz macierzystej "klasy".

+0

Ale to samo dotyczy konieczności kopiowania i wklejania tego kodu dla każdego widoku podrzędnego. –

+0

Lubię używać 'ParentView.prototype.events' zamiast tworzyć' genericEvents' na rodzica.Zobacz moją odpowiedź, jak to zrobić bez kopiowania/wklejania dla każdego widoku podrzędnego. – tybro0103

+0

Zgadzam się, twoje rozwiązanie wydaje się lepsze ... chociaż teraz, gdy o tym myślę, martwienie się o tego rodzaju problemy powoduje, że czuję się nieswojo przy używaniu OOP w JS ogólnie –

3

Oto rozwiązanie, które działa dobrze dla mnie. Możesz użyć obiektu zdarzeń zarówno w podrzędnej jak i podklasie bez rozszerzania w nim każdej podklasy. Po prostu przedłużyć go raz w klasie nadrzędnej:

APP.Views.GenericWizard = Backbone.View.extend({ 
    events: { 
    'click .btn-prev' : function(){} 
    }, 
    initialize: function() { 
    _(this.events).extend(APP.Views.GenericWizard.prototype.events); 
    } 
}); 

APP.Views.RegisterWizard = APP.Views.GenericWizard.extend({ 
    events: { 
    'blur #username' : function(){} 
    }, 
}); 

var registerWizard = new APP.Views.RegisterWizard(); 
console.log(registerWizard.events); 
// Object {blur #username: function, click .btn-prev: function} 
+0

Proszę wyjaśnić powód powstania orzeczenia. Czy coś tu jest nie tak? – tybro0103

+0

Myślę, że Will Shaver jest na dobrej drodze do najlepszego rozwiązania. Co by się stało, gdybym przedłużył z RegisterWizard dodatkowe zdarzenia? Wydarzenia z RegisterWizard zostaną utracone. Musisz rozważyć pełny łańcuch prototypów, jeśli zamierzasz spróbować OOP w JavaScript. Naprawdę nie jest to świetny sposób, ale Will jest prawdopodobnie najczystszym i najdokładniejszym sposobem na zrobienie tego. – Chris

+0

@Chris, dobry punkt. Moja metoda obsługuje tylko jeden poziom podklasy. – tybro0103