Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swap/switch/exchange backbone.js views in place?

I'm implementing view/edit Views in backbone.js for a contacts manager. The web suggested creating a Contact class with child views called ContactView and ContactEdit. The problem is, these need to occupy the same el in the DOM, so I can't nest the children inside the parent. That's because from the outside, I want parent views to only refer to Contact, as if the children were private. I tried this and it works the first time I render():

initialize: function() {
    this.view[0] = new CL.Views.ContactView({model: this.model, el: this.el});
    this.view[1] = new CL.Views.ContactEdit({model: this.model, el: this.el});
},
render: function() {
    this.view[0].render();
    return this;
}

But then I can't swap the views. I tried this.view[0].remove() and everything I can think of, but just can't get the browse and edit views to swap with each other using the same el.

I'm thinking that this means it will be better to have two templates inside one view and just exchange them, which already mostly works. I'm thinking it's backbone.js doesn't handle inheritance well for views at the same level in the DOM.

I'd prefer to avoid backbone.js extensions, but am up for following whatever patterns they implement. I'm trying to do this the "right" way because view/edit is such a common pattern for the forms in our app.

P.S. Another way to state this problem is, how does one hide a view and replace it with another view in backbone.js, if there is no parent view enclosing them?

Thanks in advance for any help you can provide.

like image 685
Zack Morris Avatar asked Jan 25 '13 01:01

Zack Morris


1 Answers

I do not quite understand if you want to have a view for a Collection of Models or do you want a view for dealing with a single Model?

If it is a view for a single model,

then you can stick with one view. Just have some events listening for your actions enabling or disabling editing features. (You can do this even by setting up contenteditable="true" on dom elements)

I strongly advise to use some tools like backbone.marionette or chaplinjs. They will save you a ton of time.

Following examples are for Backbone.Marionette

example template

<script type="text/template" id="contact-view-template">
  <span data-bind="firstName"></span>
  <span data-bind="lastName"></span>
  <span data-bind="email"></span>
  <a href="#" data-action="edit">
  <a href="#" data-action="save" style="display:none">
</script>

The view code:

ContactView = Marionette.ItemView.extend({
  template:"#contact-view-template",
  events:{
    "click [data-action=edit]":"edit",
    "click [data-action=save]":"save"
  },
  edit:function(){
    this.$("[data-action=edit]").hide();
    this.$("[data-action=save]").show();
    this.$("[data-bind]").attr("contenteditable",true);
  },
  save:function(){
    var value = {};
    _.each(this.$("[data-bind]",function(el){
      value[el.dataset['bind']]= $(el).val() || $(el).text();
    }));
    this.model.set(value);
    // add your validation here
    this.model.save();
  }
});

If you want to have multiple edit views, than just:

ContactListEditView = Marionette.CompositeView.extend({
  itemView:ContactView.extend({
    tagName:"li"
  }),
  itemViewContainer:"ul",
  template:_.template("<h1>ContactList</h1><p>feel free to edit</p><ul></ul>")
});

If you need 1 edit view and multiple non editable views

(i hope I haven't made any serious errors):

ContactEditView = Marionette.ItemView.extend({
  template:"#contact-edit-view", // your template & bindings
  events:{
    "click [data-action=save]":"save"
  },
  save:function(){
    var value = {};
    _.each(this.$("[data-bind]",function(el){
      value[el.dataset['bind']]= $(el).val() || $(el).text();
    }));
    this.model.set(value);
    this.model.save();
  }
});


ContactListView = Marionette.CompositeView.extend({
  itemView:Marionette.ItemView.extend({
    template:"#contact-view-template",
    events:{
      "click [data-action=edit]":"edit"
    },
    edit:function(){
      this.trigger("edit",this);
    }
  }),
  regions:{
    "edit":"[data-region=edit]"
  },
  initialize:function(){
    this.on("itemview:edit",function(view){
      this.edit.show(new ContactEditView({model:view.model}));
    }.bind(this));
  }
});
like image 198
g00fy Avatar answered Oct 01 '22 23:10

g00fy