0

In my App i have created a View. this View is composed of a Template like a little Form. The Form has an button and in my View i create an click event to handle this button to create a new instance of another View passing the Form data to this View and put the data on html element. The problem is: if i enter in home route or in product 3 times and send a Form data, will appears 3 same Form datas.

Form view

window.userFormView = Backbone.View.extend({
  el:$("#principal"),
  events : {
    'click .userButton' : 'newUser'
 },
 initialize:function(){
   this.template = _.template($("#userFormView").html());
 },
 newUser : function(ev) {
   ev.preventDefault();
   //criamos uma nova instancia do model
   window.user_view = new userViewes({model: users});
   var u = { nome : $("#iName").val() ,sobrenome : $("#iLName").val() };
   var user = new userModel(u);
   users.add(user);
   console.log(users);
   return false;
 },
  render: function() {
    this.$el.html("");
    this.$el.html(this.template);
  }
});

Form Template View

     <script type="text/template" id="userFormView">
        <form action="" id="form-new-user" class="formulario">
          <span class="label">Name?</span><input type="text" id="iName" class="input">
          <span class="label">Last Name?</span><input type="text" id="iLName" class="input">

          <button class="userButton">Send</button>
          <hr>

        </form>
      </script>

and my route are like this:

window.AppRouter = Backbone.Router.extend({

//
// Definindo rotas
//
routes: {
    'home':     'index',
    'product': 'productsList',
    'foo1': 'doNothing1',
    'foo2': 'doNothing2'
},

index: function () {

        window.users = new userCollections();
        window.userForm  = new userFormView();
},
productsList : function() {

    window.pCollection = new productCollections();
    window.produtoForm =  new produtoFormView();
},
doNothing1: function () {
    console.log('doNothing1()');
},
 doNothing2: function () {
    console.log('doNothing2()');
 }
});
window.router = new AppRouter();
Backbone.history.start();

userViewes view

window.userViewes = Backbone.View.extend({
  // model: users,
  el: $("#userContainer"),
  initialize: function(){
    this.model.on("add", this.render, this);
    this.model.on("remove", this.render, this);
  },
  render: function() {
     var self = this;
     self.$el.html("");
     this.model.each(function(user, indice) {
       self.$el.append((new userView({model:  user })).render().$el);
    });
    return this;
  }
});

and finally userView:

window.userView = Backbone.View.extend({
   //model: new userModel(),
   tagName : 'div',
   class : "userName",
   events :{
     'click .editar'  : 'editar',
     'click .remover' : 'remover',
     'blur .sobrenome': 'fechar',
     'keypress .sobrenome' : 'onEnterUpdate',  
   },
   editar : function(ev) {
     ev.preventDefault();
     this.$('.sobrenome').attr('contenteditable', true).focus();
   },
   fechar : function(ev) {
      var sobrenome = $(".sobrenome").text();
      this.model.set("sobrenome", sobrenome);
      $(".sobrenome").val();
      this.$(".sobrenome").removeAttr("contenteditable");
  },
  onEnterUpdate : function(ev) {
     var self = this;
     if(ev.keyCode == 13) {
       self.fechar();
      _.delay(function(){
         self.$(".sobrenome").blur();
      }, 100);
    }
  },
  remover : function(ev) {
     ev.preventDefault();
     window.users.remove(this.model);
  },
  initialize: function(){
     this.template = _.template($("#userTemplate").html());
  },
  render : function() {
      this.$el.html(this.template(this.model.toJSON()));
      return this;
  }
});
adahox_
  • 111
  • 1
  • 12

2 Answers2

1

When your view is using el option, make sure you clean up the existing view before you make a new one.

As it is, every time you switch between routes (without a full page refresh) a new instance pointing to same element is created which causes more and more event handlers to be bound to the el element which is in DOM, and the views stay in memory because of the binding. Try something like:

index: function () {
    window.users = window.users || new userCollections();
    if(window.userForm){
      // clean up is important
      window.userForm.remove();
    }
    window.userForm  = new userFormView();
},

And of course, instead of repeating similar code in all routes, have a variable like this.currentView that points to the active view, and a common function that does necessary clean up

P.S: Adding properties to window object is a bad practice. Create your own name space or use the Router instance instead of window

T J
  • 42,762
  • 13
  • 83
  • 138
  • it does not work. the form appears only once time. if i back after render other view, the form element dont show again. i have implemented singleton pattern and it works fine. – adahox_ Mar 10 '17 at 18:55
  • @adahox_ Probably because you don't have a template with the element that is used for `el` option which is rendered when you go back. In that case you shouldn't use `el` option. You should let backbone create new `el` for each view instance. – T J Mar 11 '17 at 09:03
  • `el` is supposed to be used when the element is available in DOM when you're creating the view. What you're doing is not the proper way – T J Mar 11 '17 at 09:14
  • Are you saing that i have to create the Template dinamicly? the Remove() method remove that element from html? is it? – adahox_ Mar 13 '17 at 11:58
  • 1
    @adahox_ If you don't specify the `el` option backbone creates a DOM element for each view instance (You can customize it with various options). You should append this to a static container element (an element that stays in DOM even after switching between different routes). In general use `el` option on dynamic elements which you're sure will be available in DOM before creating the view instance, for example as part of a parent template. Ideally avoid using it, a lot of bugs under `Backbone` tag is caused by it's incorrect usage – T J Mar 13 '17 at 13:30
  • @adahox_ This http://stackoverflow.com/a/35500241/2333214 is an example of the kind of mess you can get into if you use it without understanding how it works. It's a good idea to avoid using it and let backbone create an element per view instance – T J Mar 13 '17 at 13:36
-2

I have found the answer. i implemented singleton pattern to get only one instance of the object. follow the code:

       var single = (function(){
        function createInstance() {
            window.userForm  = new userFormView();
            window.users     = new userCollections();
        }

        function users() {
            return window.users;
        }

        function userForm() {
            return window.userForm;
        }

        return {
            init : function() {
                 if(!window.users && !window.userForm) {
                    createInstance();
                 }else{
                    this.render();
                 }            
            },

            render: function() {
                window.userForm.render();
            }

        }
   }());

   single.init();
adahox_
  • 111
  • 1
  • 12
  • You're really over-complicating this. – Emile Bergeron Mar 10 '17 at 19:00
  • TJ gave you the right answer. Understanding it will help you solve the problem. The singleton pattern exist for a reason and it's not to solve that kind of problem. Most of the time, you should avoid the singleton pattern. And even if it was the solution, this is an overkill implementation. – Emile Bergeron Mar 13 '17 at 15:43