jashkenas / backbone

Give your JS App some Backbone with Models, Views, Collections, and Events
http://backbonejs.org
MIT License
28.08k stars 5.37k forks source link

Enhance the way we reference a view's sub-element #889

Closed gschammah closed 12 years ago

gschammah commented 12 years ago

This is just a proposal for a common situation I am while working in a backbone project.

It is common for me to access my views' sub-elements many times. Here I put a simple example:

var myView = Backbone.View.extend({
  el: '#myDiv',

  hideButton: function() {
    this.$('#myButton').hide();
  },

  showButton: function() {
    this.$('#myButton').show();    
  }
});

This works fine, but if the button id changes in the future, then I have to change that id in to places in my view (a real project will demand even more changes). So what I always end up doing is something like this:

var myView = Backbone.View.extend({
  el: '#myDiv',

  _initialize_elements: function() {
     this.elements = {
        myButton: this.$('#myButton')
     };
  },

  initialize: function() {
    this._initialize_elements();
  },

  hideButton: function() {
    this.elements.myButton.hide();
  },

  showButton: function() {
    this.elements.myButton.show();    
  }
});

For me this approach is fine, but I don't like to manually initialize every view every single time to store the jQuery reference to each of my view's sub-elements, and also is very easy to forget to do it.

I think the best approach would be for a Backbone view to have an elements object that works in a similar way than the events object.

E.g.:

var myView = Backbone.View.extend({
  el: '#myDiv',

  elements: {
    myButton : '#myButton'
  },

  hideButton: function() {
    this.elements.myButton.hide();
  },

  showButton: function() {
    this.elements.myButton.show();    
  }
});

Then, before the delegateEvents function is called in Backbone.View constructor, we can replace the value of each elements key a jQuery object. I will request for a pull request soon. I would love to hear your comments.

(Please note that this would be useful just in the cases where the HTML elements existed before the view instantiation)

TheCloudlessSky commented 12 years ago

I don't think this should be baked in. In your initialize() methods, why do you need the extra call to _initialize_elements()? You could just have the one initialize() or you could do something like this:

var MultiElementView = Backbone.View.extend({

  constructor: function () {

    Backbone.View.prototype.constructor.apply(this, arguments); 

    _.each(this.elements, function(value, key) {
      if (_.isString(value)) {
        this.elements[key] = this.$(value);
      }
      else {
        // Do something with non-string elements.
      }
    }, this);
  }
});

var SomeOtherView = MultiElementView.extend({ 
  elements: { 
    a: "#a", 
    b: "#b" 
  } 
});

var v = new SomeOtherView({ el: "#foo" });
console.log(v.elements); // a, b are now jQuery objects.
tomasztunik commented 12 years ago

it means each time you'd create new jquery object and make sizzle dom search call as usually if you are using templating you woudln't even have access to the DOM of the view until it is rendered. You'd need to make it much smarter and cache the jquery object after first search....

anyways don't think it shouldn't be part of Backbone - if you need quick references to elements you should just define them in the render or initialize method IMO

gschammah commented 12 years ago

I am doing something similar to what @TheCloudlessSky suggested right now, I created a Base view that converts my elements jQuery selectors to jQuery objects, and extend all views from that one.

SpineJS has something like this that I think is pretty useful. Thanks for your comments

gschammah commented 12 years ago

One more thing, in @TheCloudlessSky suggestion, is not possible to refer to any jQuery reference in the elements object in the initialize method, since you are calling the constructor, and the constructor invokes initialize(), so elements wont contain the jQuery references yet.

jashkenas commented 12 years ago

This is a duplicate of previous tickets -- take a look to see further conversation on the subject. This isn't something that should be built-in to backbone...