백본보기 : 부모로부터 이벤트 상속 및 확장


115

Backbone의 문서는 다음과 같이 말합니다.

이벤트 속성은 이벤트 해시를 반환하는 함수로 정의되어 이벤트를 프로그래밍 방식으로 쉽게 정의하고 상위 뷰에서 상속 할 수 있습니다.

부모의보기 이벤트를 상속하고 확장하는 방법은 무엇입니까?

부모보기

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

어린이보기

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

답변:


189

한 가지 방법은 다음과 같습니다.

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

또 다른 것 :

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

이벤트가 기능인지 객체인지 확인하려면

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

훌륭합니다 ... ChildView에서 상속하는 방법을 보여주기 위해 이것을 업데이트 할 수 있습니다 (프로토 타입 이벤트가 함수 또는 객체인지 확인) ... 아니면이 전체 상속 항목을 지나치게 생각하고있을 수도 있습니다.
— brent

@brent 물론입니다. 세 번째 사례를 추가했습니다
— soldier.moth

14
내가 착각하지 않았다면 parentEvents = _.result(ParentView.prototype, 'events');'수동으로' events함수 인지 확인 하는 대신 사용할 수 있어야 합니다.
— Koen.

3
@Koen. _.result내가 전에 알아 차리지 못했던 밑줄 유틸리티 함수를 언급 한 +1 . 관심이있는 사람을 위해 다음은이 테마에 대한 다양한 변형이있는 jsfiddle입니다. jsfiddle
— EleventyOne

1
여기에 2 센트를 투자하기 위해 두 번째 옵션이 최상의 솔루션이라고 생각합니다. 나는 이것이 진정으로 캡슐화 된 유일한 방법이라는 순수한 사실 때문에 이것을 말한다. 사용되는 유일한 컨텍스트는 this인스턴스 이름으로 부모 클래스를 호출하는 것과 비교하는 것입니다. 이것에 대해 대단히 감사합니다.
— 제시 제임스 잭슨 테일러

79

soldier.moth 대답은 좋은 것입니다. 더 단순화하면 다음을 수행 할 수 있습니다.

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

그런 다음 일반적인 방법으로 두 클래스에서 이벤트를 정의하십시오.


8
당신은 아마 스왑 원하는하지만 좋은 전화, this.events및 ParentView.prototype.events모두 동일한 이벤트 핸들러를 정의 할 경우, 그렇지 않으면 부모의 핸들러는 자녀의 우선합니다.
— soldier.moth

1
@ Soldier.moth, 알았어 내가 그것을 편집했습니다{},ParentView.prototype.events,this.events
— AJP

1
분명히 이것은 작동하지만 내가 아는 것처럼 delegateEvents이벤트를 바인딩하기 위해 생성자에서 호출됩니다. 에서 확장 할 때 왜 initialize너무 늦지 않았습니까?
— SelimOber

2
까다 롭지 만이 솔루션에 대한 내 문제는 다음과 같습니다. 다양하고 풍부한 뷰 계층 구조가있는 경우 필연적 initialize으로 몇 가지 경우 (그런 다음 해당 기능의 계층 관리도 처리해야 함)를 작성해야합니다. 이벤트 개체를 병합합니다. events병합 자체 를 유지하기 위해 나에게 더 깨끗한 것 같습니다 . 그 존재는 내가이 방법을 생각하지 않았을, 그것은 : 다른 방식으로 사물을보기로 강제하는 것이 좋다 말했다
— EleventyOne

1
이 대답은 더 이상 유효하지 않습니다. delegateEvents가 초기화되기 전에 호출되기 때문입니다 (버전 1.2.3의 경우 해당됨). 주석이 추가 된 소스에서 쉽게 수행 할 수 있습니다.
— Roey

12

또한이 defaults메서드를 사용 하여 빈 개체를 만들지 않을 수 있습니다 {}.

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

2
이로 인해 상위 핸들러가 하위 핸들러 뒤에 바인딩됩니다. 대부분의 경우 문제가되지 않지만 하위 이벤트가 상위 이벤트를 취소 (재정의하지 않음)해야하는 경우 불가능합니다.
— Koen.

10

당신이 커피 스크립트를 사용하고 기능을 설정하면 events, 당신은 사용할 수 있습니다 super.

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

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

이것은 부모 이벤트 변수가 객체가 아닌 함수 인 경우에만 작동합니다.
— Michael

6

계층 구조의 이벤트 상속을 처리하는 Backbone.View에서 특수 기본 생성자를 만드는 것이 더 쉽지 않을 것입니다.

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
}

이를 통해 재정의 된 확장 함수를 사용하여 새 '하위 클래스'(자식 생성자)를 만들 때마다 계층 구조 아래로 이벤트 해시를 줄이 (병합) 할 수 있습니다.

# 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: ....
} 

확장 기능을 재정의하는 BaseView라는 특수 뷰를 생성함으로써 부모 뷰의 선언 된 이벤트를 상속하려는 하위 뷰 (예 : AppView, SectionView)를 BaseView 또는 그 파생물 중 하나에서 확장하여 간단하게 수행 할 수 있습니다.

대부분의 경우 부모 생성자를 명시 적으로 참조해야하는 서브 뷰에서 이벤트 함수를 프로그래밍 방식으로 정의 할 필요가 없습니다.


2

@ soldier.moth의 마지막 제안의 짧은 버전 :

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

2

이것은 또한 작동합니다.

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

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

스트레이트 super를 사용 하는 것은 나를 위해 작동하지 않았으며 수동으로 ParentView또는 상속 된 클래스를 지정했습니다 .

_super모든 coffeescript 내에서 사용할 수 있는 var 에 대한 액세스Class … 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 버전 1.2.3의 경우 __super__잘 작동하며 연결될 수도 있습니다. 예 :

// 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",
    }
    // ...
});

... A_View.js결과는 다음과 같습니다.

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

1

이 기사 에서 더 흥미로운 해결책을 찾았습니다.

백본의 사용 super 와 ECMAScript의 hasOwnProperty를 사용합니다. 두 번째 진보적 사례는 매력처럼 작동합니다. 다음은 약간의 코드입니다.

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

당신은 또한 그것을 할 수 있습니다 ui 및 속성에 .

이 예제는 함수에 의해 설정된 속성을 처리하지 않지만이 경우 기사 작성자가 솔루션을 제공합니다.


1

이 작업을 부모 클래스에서 전적으로 수행하고 자식 클래스에서 함수 기반 이벤트 해시를 지원하여 자식이 상속에 무관심 할 수 있도록하려면 (자식이 MyView.prototype.initialize재정의하는 경우 자식이 호출 해야 함 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

이 CoffeeScript 솔루션은 저에게 효과적이었습니다 (@ soldier.moth의 제안을 고려함).

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

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

0

에 ParentView이벤트가 객체로 정의되어 있고 이벤트를 동적으로 정의 할 필요가없는 ChildView경우 함수를 제거하고 _.extend직접 사용하여 soldier.moth의 답변을 더 단순화 할 수 있습니다.

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

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

0

내가 좋아하는 패턴은 생성자를 수정하고 몇 가지 추가 기능을 추가하는 것입니다.

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

});

부모를 식별 할 필요가 없기 때문에이 방법을 선호합니다. attributes및에 대해 동일한 논리를 사용합니다 defaults.


0

와, 여기에 많은 답변이 있지만 하나 더 제공 할 것이라고 생각했습니다. BackSupport 라이브러리를 사용하는 경우 extend2. 사용 extend2하면 자동으로 병합 events( defaults및 유사한 속성)이 처리됩니다.

다음은 간단한 예입니다.

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
나는 개념을 좋아하지만 원칙적으로 만 "extend2"가 적절한 함수 이름이라고 생각하는 모든 라이브러리를 전달합니다.
— Yaniv에게

본질적으로 "Backbone.extend이지만 기능이 개선 된"함수의 이름에 대해 제안 할 수있는 모든 제안을 환영합니다. Extend 2.0 ( extend2)은 제가 생각해 낼 수있는 최고 였고, 그렇게 끔찍하다고 생각하지 않습니다. Backbone에 익숙한 사람은 이미를 사용하는 데 익숙해 extend져 있으므로 새로운 명령을 외울 필요가 없습니다.
— machineghost

Github 저장소에 대한 문제를 열었습니다. :)
— Yaniv
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.