Omurga Görünümü: Etkinlikleri üst öğeden devralın ve genişletin


115

Omurga dokümantasyonunun durumu:

Olaylar özelliği, olaylarınızı programlı olarak tanımlamanın yanı sıra bunları üst görünümlerden devralmayı kolaylaştırmak için bir olay hash'i döndüren bir işlev olarak da tanımlanabilir.

Bir ebeveynin görüş olaylarını nasıl devralır ve onları genişletirsiniz?

Ebeveyn Görünümü

var ParentView = Backbone.View.extend({
   events: {
      'click': 'onclick'
   }
});

Çocuk Görünümü

var ChildView = ParentView.extend({
   events: function(){
      ????
   }
});

Yanıtlar:


189

Bunun bir yolu:

var ChildView = ParentView.extend({
   events: function(){
      return _.extend({},ParentView.prototype.events,{
          'click' : 'onclickChild'
      });
   }
});

Bir diğeri:

var ParentView = Backbone.View.extend({
   originalEvents: {
      'click': 'onclick'
   },
   //Override this event hash in
   //a child view
   additionalEvents: {
   },
   events : function() {
      return _.extend({},this.originalEvents,this.additionalEvents);
   }
});

var ChildView = ParentView.extend({
   additionalEvents: {
      'click' : ' onclickChild'
   }
});

Olayların işlev mi yoksa nesne mi olduğunu kontrol etmek için

var ChildView = ParentView.extend({
   events: function(){
      var parentEvents = ParentView.prototype.events;
      if(_.isFunction(parentEvents)){
          parentEvents = parentEvents();
      }
      return _.extend({},parentEvents,{
          'click' : 'onclickChild'
      });
   }
});

Bu harika ... Belki bunu bir ChildView'dan nasıl miras alacağınızı göstermek için güncelleyebilirsiniz (prototip olaylarının bir işlev veya nesne olup olmadığını kontrol edin) ... Veya belki de tüm bu kalıtımla ilgili şeyleri çok düşünüyorum.
06'da brent

@brent Tabii, üçüncü durum eklendi
asker.moth

14
Yanılmıyorsam , bir işlev parentEvents = _.result(ParentView.prototype, 'events');olup olmadığını 'manuel olarak' kontrol etmek yerine kullanabilmelisiniz events.
Koen.

3
@Koen. _.resultDaha önce fark etmediğim alt çizgi yardımcı program işlevinden bahsetmek için +1 . İlgilenenler için, işte bu konuyla ilgili bir dizi varyasyon içeren bir jsfiddle
EleventyOne

1
Sadece iki sentimi buraya atmak için, ikinci seçeneğin en iyi çözüm olduğuna inanıyorum. Bunu, gerçekten kapsüllenmiş tek yöntem olduğu için söylüyorum. kullanılan tek bağlam this, ana sınıfı örnek adına göre çağırma zorunluluğudur. bunun için çok teşekkür ederim.
jessie james jackson taylor

79

Asker.moth cevabı iyi bir cevap Daha da basitleştirerek aşağıdakileri yapabilirsiniz

var ChildView = ParentView.extend({
   initialize: function(){
       _.extend(this.events, ParentView.prototype.events);
   }
});

Ardından, olaylarınızı her iki sınıfta da tipik bir şekilde tanımlayın.


8
Muhtemelen takas istiyorum gerçi İyi karar this.events& ParentView.prototype.eventsikisi de aynı olayla ilgili işleyicileri tanımlar, aksi takdirde Ebeveyn işleyicisi Çocuğun geçersiz kılar.
asker.moth

1
@ Soldier.moth, tamam olarak düzenledim{},ParentView.prototype.events,this.events
AJP

1
Açıkçası bu işe yarıyor, ama bildiğim gibi delegateEventskurucuda olayları bağlamak için çağrılıyor. Öyleyse, onu uzattığınızda initialize, neden çok geç kalmıyorsunuz?
SelimOber

2
Oldukça seçici, ancak bu çözümle ilgili sorunum şudur: Farklı ve bol bir görüş hiyerarşisine sahipseniz, kendinizi kaçınılmaz olarak initializebirkaç durumda yazarken bulacaksınız (daha sonra bu işlevin hiyerarşisini yönetmekle de uğraşmak zorunda kalacaksınız ) sadece olay nesnelerini birleştirin. eventsBirleşmeyi kendi içinde tutmak bana daha temiz görünüyor . Bununla birlikte, bu yaklaşımı düşünmemiştim ve olaylara farklı bir şekilde bakmaya zorlanmak her zaman güzeldir :)
EleventyOne

1
delegateEvents başlatmadan önce çağrıldığından bu yanıt artık geçerli değildir (bu sürüm 1.2.3 için geçerlidir) - açıklamalı kaynakta bunu yapmak kolaydır.
Roey

12

defaultsBoş nesneyi oluşturmaktan kaçınmak için yöntemi de kullanabilirsiniz {}.

var ChildView = ParentView.extend({
  events: function(){
    return _.defaults({
      'click' : 'onclickChild'
    }, ParentView.prototype.events);
  }
});

2
Bu, üst işleyicilerin alt işleyicilerden sonra bağlanmasına neden olur. Çoğu durumda bir sorun değildir, ancak bir alt olayın bir ana olayı iptal etmesi (geçersiz kılmaması) durumunda bu mümkün değildir.
Koen.

10

CoffeeScript kullanır ve bir işlevi olarak eventsayarlarsanız, kullanabilirsiniz super.

class ParentView extends Backbone.View
  events: ->
    'foo' : 'doSomething'

class ChildView extends ParentView
  events: ->
    _.extend {}, super,
      'bar' : 'doOtherThing'

Bu, yalnızca üst olaylar değişkeni bir nesne değil de bir işlevse çalışır.
Michael

6

Backbone.View'dan, hiyerarşideki olayların kalıtımını yöneten özel bir temel oluşturucu oluşturmak daha kolay olmaz mıydı.

BaseView = Backbone.View.extend {
    # your prototype defaults
},
{
    # redefine the 'extend' function as decorated function of Backbone.View
    extend: (protoProps, staticProps) ->
      parent = this

      # we have access to the parent constructor as 'this' so we don't need
      # to mess around with the instance context when dealing with solutions
      # where the constructor has already been created - we won't need to
      # make calls with the likes of the following:   
      #    this.constructor.__super__.events
      inheritedEvents = _.extend {}, 
                        (parent.prototype.events ?= {}),
                        (protoProps.events ?= {})

      protoProps.events = inheritedEvents
      view = Backbone.View.extend.apply parent, arguments

      return view
}

Bu, yeniden tanımlanmış genişletme işlevini kullanarak yeni bir 'alt sınıf' (alt sınıf oluşturucu) oluşturduğumuzda olayları hiyerarşiyi azaltmamızı (birleştirmemizi) sağlar.

# AppView is a child constructor created by the redefined extend function
# found in BaseView.extend.
AppView = BaseView.extend {
    events: {
        'click #app-main': 'clickAppMain'
    }
}

# SectionView, in turn inherits from AppView, and will have a reduced/merged
# events hash. AppView.prototype.events = {'click #app-main': ...., 'click #section-main': ... }
SectionView = AppView.extend {
    events: {
        'click #section-main': 'clickSectionMain'
    }
}

# instantiated views still keep the prototype chain, nothing has changed
# sectionView instanceof SectionView => true 
# sectionView instanceof AppView => true
# sectionView instanceof BaseView => true
# sectionView instanceof Backbone.View => also true, redefining 'extend' does not break the prototype chain. 
sectionView = new SectionView { 
    el: ....
    model: ....
} 

Özel bir görünüm oluşturarak: genişletme işlevini yeniden tanımlayan BaseView, üst görünümlerinin bildirilen olaylarını miras almak isteyen alt görünümlere (AppView, SectionView gibi) sahip olabiliriz, bunu BaseView veya türevlerinden birinden genişleterek yapar.

Olay işlevlerimizi alt görünümlerimizde programlı olarak tanımlama ihtiyacından kaçınıyoruz, çoğu durumda ana kurucuya açıkça atıfta bulunmamız gerekir.


2

@ Soldier.moth'un son önerisinin kısa versiyonu:

var ChildView = ParentView.extend({
  events: function(){
    return _.extend({}, _.result(ParentView.prototype, 'events') || {}, {
      'click' : 'onclickChild'
    });
  }
});

2

Bu aynı zamanda işe yarar:

class ParentView extends Backbone.View
  events: ->
    'foo' : 'doSomething'

class ChildView extends ParentView
  events: ->
    _.extend({}, _.result(_super::, 'events') || {},
      'bar' : 'doOtherThing')

Düz kullanmak superbenim için işe yaramıyordu, ya manuel olarak ParentViewya da miras alınan sınıfı belirliyordu.

_superHerhangi bir kahve dosyasında bulunan değişkene erişimClass … extends …


2

// ModalView.js
var ModalView = Backbone.View.extend({
	events: {
		'click .close-button': 'closeButtonClicked'
	},
	closeButtonClicked: function() { /* Whatever */ }
	// Other stuff that the modal does
});

ModalView.extend = function(child) {
	var view = Backbone.View.extend.apply(this, arguments);
	view.prototype.events = _.extend({}, this.prototype.events, child.events);
	return view;
};

// MessageModalView.js
var MessageModalView = ModalView.extend({
	events: {
		'click .share': 'shareButtonClicked'
	},
	shareButtonClicked: function() { /* Whatever */ }
});

// ChatModalView.js
var ChatModalView = ModalView.extend({
	events: {
		'click .send-button': 'sendButtonClicked'
	},
	sendButtonClicked: function() { /* Whatever */ }
});

http://danhough.com/blog/backbone-view-inheritance/


1

Backbone sürüm 1.2.3 için __super__iyi çalışır ve hatta zincirlenebilir. Örneğin:

// A_View.js
var a_view = B_View.extend({
    // ...
    events: function(){
        return _.extend({}, a_view.__super__.events.call(this), { // Function - call it
            "click .a_foo": "a_bar",
        });
    }
    // ...
});

// B_View.js
var b_view = C_View.extend({
    // ...
    events: function(){
        return _.extend({}, b_view.__super__.events, { // Object refence
            "click .b_foo": "b_bar",
        });
    }
    // ...
});

// C_View.js
var c_view = Backbone.View.extend({
    // ...
    events: {
        "click .c_foo": "c_bar",
    }
    // ...
});

... hangi - içinde A_View.js- sonuçlanır:

events: {
    "click .a_foo": "a_bar",
    "click .b_foo": "b_bar",
    "click .c_foo": "c_bar",
}

1

Bunda daha ilginç çözümler buldum makalede

Bu omurga en kullanımı süper ve ECMAscript en hasOwnProperty. İlerici örneklerinden ikincisi bir cazibe gibi çalışıyor. İşte biraz kod:

var ModalView = Backbone.View.extend({
    constructor: function() {
        var prototype = this.constructor.prototype;

        this.events = {};
        this.defaultOptions = {};
        this.className = "";

        while (prototype) {
            if (prototype.hasOwnProperty("events")) {
                _.defaults(this.events, prototype.events);
            }
            if (prototype.hasOwnProperty("defaultOptions")) {
                _.defaults(this.defaultOptions, prototype.defaultOptions);
            }
            if (prototype.hasOwnProperty("className")) {
                this.className += " " + prototype.className;
            }
            prototype = prototype.constructor.__super__;
        }

        Backbone.View.apply(this, arguments);
    },
    ...
});

Bunu kullanıcı arabirimi ve öznitelikler için de yapabilirsiniz .

Bu örnek, bir işlev tarafından ayarlanan özelliklerle ilgilenmez, ancak makalenin yazarı bu durumda bir çözüm sunar.


1

Bunu tamamen ana sınıfta yapmak ve çocuk sınıfta işlev tabanlı olaylar karmasını desteklemek için, böylece çocuklar kalıtım konusunda agnostik olabilir ( MyView.prototype.initializegeçersiz kılarsa çocuğun çağırması gerekir initialize):

var MyView = Backbone.View.extend({
  events: { /* ... */ },

  initialize: function(settings)
  {
    var origChildEvents = this.events;
    this.events = function() {
      var childEvents = origChildEvents;
      if(_.isFunction(childEvents))
         childEvents = childEvents.call(this);
      return _.extend({}, MyView.prototype.events, childEvents);
    };
  }
});

0

Bu CoffeeScript çözümü benim için çalıştı (ve @ asker.moth'un önerisini dikkate alır):

class ParentView extends Backbone.View
  events: ->
    'foo' : 'doSomething'

class ChildView extends ParentView
  events: ->
    _.extend({}, _.result(ParentView.prototype, 'events') || {},
      'bar' : 'doOtherThing')

0

Eğer eminseniz ParentViewnesne olarak tanımlanan olayları vardır ve dinamik olayları tanımlamak gerekmez ChildViewo fonksiyonun kurtulmak alma ve kullanarak daha da soldier.moth cevabını kolaylaştırmak mümkündür _.extenddoğrudan:

var ParentView = Backbone.View.extend({
    events: {
        'click': 'onclick'
    }
});

var ChildView = ParentView.extend({
    events: _.extend({}, ParentView.prototype.events, {
        'click' : 'onclickChild'
    })
});

0

Bunun için çok sevdiğim bir model, kurucuyu değiştirmek ve bazı ek işlevler eklemek:

// App View
var AppView = Backbone.View.extend({

    constructor: function(){
        this.events = _.result(this, 'events', {});
        Backbone.View.apply(this, arguments);
    },

    _superEvents: function(events){
        var sooper = _.result(this.constructor.__super__, 'events', {});
        return _.extend({}, sooper, events);
    }

});

// Parent View
var ParentView = AppView.extend({

    events: {
        'click': 'onclick'
    }

});

// Child View
var ChildView = ParentView.extend({

    events: function(){
        return this._superEvents({
            'click' : 'onclickChild'
        });
    }

});

Bu yöntemi tercih ediyorum çünkü değiştirmek için ebeveyni tanımlamanız gerekmiyor. Aynı mantığı attributesve için kullanıyorum defaults.


0

Vay canına, burada bir sürü cevap var ama bir tane daha vermeyi düşündüm. BackSupport kitaplığını kullanırsanız, size sunar extend2. Eğer kullanırsanız extend2, sizin için otomatik olarak birleştirme events( defaultsve benzeri mülkler) ile ilgilenir .

İşte hızlı bir örnek:

var Parent = BackSupport.View.extend({
    events: {
        change: '_handleChange'
    }
});
var Child = parent.extend2({
    events: {
        click: '_handleClick'
    }
});
Child.prototype.events.change // exists
Child.prototype.events.click // exists

https://github.com/machineghost/BackSupport


3
Konsepti beğendim, ancak tek başına ilke olarak, "expand2" nin uygun bir işlev adı olduğunu düşünen herhangi bir kitaplığa geçebilirim.
Yaniv

Esasen "Backbone.extend, ancak gelişmiş işlevselliğe sahip" olan bir işleve ne ad vereceğinize dair sunabileceğiniz tüm önerileri memnuniyetle karşılıyorum. Extend 2.0 ( extend2) bulabildiğim en iyisiydi ve bunun o kadar da kötü olduğunu düşünmüyorum: Backbone'a alışkın olan herhangi biri zaten kullanmaya alışkın extend, bu yüzden yeni bir komutu ezberlemek zorunda değiller.
machineghost

Github deposunda bununla ilgili bir sorun açtı. :)
Yaniv
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.