title: Extending Backbone.View
body: <p>I'm trying to extend my Backbone.View class. 
  Initially i come up with the idea of creating my own interface to simplify the amount of code required to remove childViews.</p>
  
  <p>Basically, an array and some methods to iterate over each view on the array calling remove for each one. This is specially useful on lists and things like that.</p>
  
  <p>Then i tried something similar to this and realize that Objects create from the class were sharing the same instance on their properties.</p>
  
  <pre><code>Backbone.SuperView = Backbone.View.extend({
    childViews: ['global']
  })
  
  
  var App_View = Backbone.SuperView.extend({
     initialize: function(options){
         this.childViews.push(options.viewName);
  console.log('app_View =&gt;' + this.childViews);
     }
  })
  
  var App2_View = Backbone.SuperView.extend({
     initialize: function(options){
         this.childViews.push(options.viewName);
  console.log('app2_View =&gt;' + this.childViews);
     }
  })
  
  new App_View({viewName: 'a'});
  new App2_View({viewName: 'b'});
  </code></pre>
  
  <p>This outputs:</p>
  
  <pre><code>app_View =&gt;  global ,a 
  app2_View =&gt; global ,a ,b
  </code></pre>
  
  <p>I don't want the same instance of childViews in all the objects that i create from Backbone.SuperView! Like you can imagine i pretend to have an array childViews in all my objects created from that class but i with different memory instances, so each view has it's own childviews.</p>
  
  <p>Then i come up with the the following concept but i still think this is not the right way of doing things:</p>
  
  <p><strong>MY SUPERVIEW CLASS</strong></p>
  
  <pre><code>;(function($, _ , Backbone) {
     'use strict';
  
     Backbone.SuperView = Backbone.View.extend({
        initialize: function(){
           this._views = {};
           this._listViews = [];
           this._bindedElements = [];
  
           _.extend(this,
              {
              _destoryAllViews: function(){
                 this._removeListViews();
                 this._removeViews();
                 this.remove();
              },
              // destroy a view atual e as subviews e listviews
              destroy: function(options){
                 options = options || {};
                 var defaults = {timeout : 200};
                 if (_.isEmpty(options)){
                    options = defaults;
  
                    setTimeout($.proxy(function(){
                       this._destoryAllViews();
                       this._cleanExternalEventBinds();
                    },this), options.timeout );
                 } else {
                    this._destoryAllViews();
                    this._cleanExternalEventBinds();
                 }
              },
              addListView: function(view){
                 this._listViews.push(view);
              },
              // manual view cleaner
              emptyListView: function(destroy){
                  this._removeListViews();
              },
              /*
                 views with same keys are destroyed and then assigned
              */
              set: function(key, view, options){
                 options = options || {};
                 var destroy = ('destroy' in options ) ? options.destroy : true;
  
                 if (key in this._views){
                    this._views[key].view.destroy();
                 }
  
                 this._views[key] = { 'view': view, 'destroy':  destroy };
  
                 return view;
              },
              get: function(key){
                 return this._views[key].view;
              },
              _removeViews: function(){
                 _.each( this._views, function( viewSettings ){
                    if (viewSettings.destroy){
                       if (viewSettings.view.destroy){
                          viewSettings.view.destroy();
                       }
                    }
                 });
              },
              _removeListViews: function(){
                 _.each( this._listViews, function( view ){
                    if (view.destroy){
                       view.destroy();
                    }
                 });
              },
              setEvent: function(el, eventName, callback){
                 var $el = $(el),
                    callback = (_.isFunction(callback)) ? callback : this[callback];
  
                    $el.bind(eventName + '.' + this.cid, callback);
  
                 this._bindedElements.push(el);
              },
              _cleanExternalEventBinds: function(){
                 var _that = this;
                 _.each(this._bindedElements, function(el){
                    el.off('.' + _that.cid );
                 });
              }
  
           });
  
        }
     });
  
  
     //_.extend(Backbone.SuperView.prototype, )
  
  
     return Backbone;
  
  })($, _ , Backbone);
  </code></pre>
  
  <p><strong>MY HTML</strong></p>
  
  <pre><code>&lt;a href="#" class="my-btn"&gt;My-btn&lt;/a&gt;
  &lt;div class="app-placeholder"&gt;&lt;/div&gt;
  </code></pre>
  
  <p><strong>MY SCIRPT</strong></p>
  
  <pre><code>var c = Backbone.Collection.extend({});
  
          var C = new c([{
              name: 'Frank',
              age: 12
          },
          {
              name: 'Maria',
              age: 19
          },
          {
              name: 'Richard',
              age: 20
          }])
  
          var header_View = Backbone.SuperView.extend({
              events: {
                  'click': 'log'
              },
              initialize: function(){
                  Backbone.SuperView.prototype.initialize.call(this);
                  this.$mybtn = $('.my-btn');
                  this.setEvent(this.$mybtn, 'click', function(e){
                      e.preventDefault();
                      alert('hello');
                  });
                  this.setEvent(this.$mybtn, 'click', 'innerHello')
                  // this.setEvent(this.$mybtn, 'click', 'log')
                  this.$el.append('&lt;h1&gt;My Header&lt;/h1&gt;')
                  return this;
              },
              innerHello: function(e){
                  // e.preventDefault();
                  alert('hello 2');
              },
              log: function(){
                  console.log(this._bindedElements);
              }
          });
  
          var item = Backbone.SuperView.extend({
              template: _.template('&lt;%= age %&gt;||&lt;%=name%&gt;'),
              events: {
                  'click': 'log'
              },
              initialize: function(options){
                  Backbone.SuperView.prototype.initialize.call(this);
                  this.model = options.model;
                  this.render();
              },
              render: function(){
                  this.$el.html('').append(this.template(this.model.toJSON()))
              },
              log: function(){
                  console.log(this._bindedElements);
              }
          })
  
          var b = Backbone.SuperView.extend({
              initialize: function(){
                  Backbone.SuperView.prototype.initialize.call(this);
                  this.set('myView', new header_View());
                  this.$el.html( '' ).append( this.get('myView').el );
                  this.collection = C;
                  this.render();
              },
              render: function(){
                  this.emptyListView();
                  this.addAll();
              },
              addAll: function(){
                  this.collection.forEach(function(model){
                      var _item = new item({model:model});
                      this.addListView(  _item );
                      this.$el.append( _item.el );
                  },this)
              }
          })
  
          var m = Backbone.View.extend({
              initialize: function(){
  
              },
          })
  
          var B = new b();
          $('.app-placeholder').append(B.el);
  </code></pre>
  
  <p>This doesn't feel right and i'm looking for a more solid solution.
  <strong>Do i really have to call the initializer in every single view?</strong></p>
  
  <p>The above code works like this:</p>
  
  <ol>
  <li><p>Run the script will render a list of 3 items and an header.</p></li>
  <li><p>The header_view uses a method create by my own to bind events to DOM elements outside the view</p></li>
  <li><p>It inserts the dom element into an array and later, when the element is destroy the event is also detached from that global element preventing memory and event issues. </p></li>
  <li><p>In the above case only clicking in the header_view will output bounded elements from _bindedElements because iniside header_View initialize i call this.setEvent($el, 'eventName',callback)</p></li>
  </ol>