Model zombies and good practice

I am new to the spine and I try to understand all the zombie points of view.

Zombies, according to this article :

When we bind objects together through events, but we do not try to untie them. As long as these objects are connected to each other, and there is a link in our application code to at least one of them, they will not be cleaned or garbage collected. The resulting memory leaks are like movie zombies - hiding in dark corners, waiting to jump out and eat us for lunch.

The above article proposes to create an object that controls the transitions between views, and then implement the close function to remove and untie the view.

Moreover, depending on the situation, where to call this closure function from?

I add a property in the initialize block of my parent view to save the trace of the child view. That way, I can call .remove () on it before replacing it with a new one. Is this a good practice or is there a better way?

I also do not understand why the definition of el and then rendering with

this.$el.html(this.template(this.model.attributes));

doesn't allow me to cancel browsing while it is working as expected, doing

$('#sportsManDetails').html(this.$el.html(this.template(this.model.attributes)));

As for the example, I just created a simple application that displays a list of athlete names and shows more detailed information when you click on a name.

Here's the code and the working fiddle :

HTML

 <script id="nameListTemplate" type="text/template"> <%= first %> <%= last %> </script> <script id="sportsManDetailsTemplate" type="text/template"> <ul> <li><%= first %></li> <li><%= last %></li> <li><%= age %></li> <li><%= sport %></li> <li><%= category %></li> </ul> <button class="test">Test</button> </script> <div id="sportsMenName"></div> <div id="sportsManDetails"></div> 

Js

model and collection

 var app = app || {}; app.SportsManModel = Backbone.Model.extend({}); app.SportsMenCollection = Backbone.Collection.extend({ model: app.SportsManModel }); 

Nameview

 app.NameView = Backbone.View.extend({ tagName: 'li', className: 'sportsMan', template: _.template($('#nameListTemplate').html()), initialize: function(){ this.sportsManDetailsView; }, events: { 'click': 'showSportsManDetails' }, showSportsManDetails: function(e){ if (typeof this.sportsManDetailsView !== 'undefined'){ this.sportsManDetailsView.remove(); } this.sportsManDetailsView = new app.SportsManDetailsView({ model: this.model }) }, render: function(){ this.$el.append(this.template(this.model.attributes)); return this; } }); 

NameListView

 app.NameListView = Backbone.View.extend({ el: '#sportsMenName', initialize: function(sportsMen){ this.collection = new app.SportsMenCollection(sportsMen); this.render(); }, render: function(){ this.collection.each(function(sportsMen){ this.renderContact(sportsMen); }, this); }, renderContact: function(sportsMen){ var nameView = new app.NameView({ model: sportsMen }); this.$el.append(nameView.render().el); } }); 

SportsManDetailsView

 app.SportsManDetailsView = Backbone.View.extend({ // doesn't work if I use el in conjunction with // this.$el.html(this.template(this.model.attributes)); // el: '#sportsManDetails', template: _.template($('#sportsManDetailsTemplate').html()), initialize: function(){ this.render(); }, events: { 'click .test': 'test' }, test: function(){ alert('test'); }, render: function(){ // that does not work //this.$el.html(this.template(this.model.attributes)); // is this good practice? $('#sportsManDetails').html(this.$el.html(this.template(this.model.attributes))); } }); 

app.js

 var sportsMen = [ {first: 'Quentin', last: 'Tarant', age: '34', sport: 'bike', category: '- 90kg'}, {first: 'Aymeric', last: 'McArthur', age: '54', sport: 'jetski', category: '200HP'}, {first: 'Peter', last: 'TheFat', age: '45', sport: 'curling', category: 'dunno'}, {first: 'Charles', last: 'Martel', age: '21', sport: 'Moto', category: 'MX 250cc'}, ]; $(function(){ new app.NameListView(sportsMen); }); 
+6
source share
2 answers

Just as you discover, Backbone considers itself more of a library than a framework - this leaves a lot of questions and design patterns left to the developer.

The term “zombie representation” is used to mean representations that are still associated with something (and thus alive) when you think they are dead. Usually there is a residual reference to the view from a call to model.on or similar. Basically a specific form of memory leak.

You can use the parent view to control the life cycle of the view, but this is common practice for this from the router. A router is used to delete old views and create new instances of a route event. Here is a snippet of how I often do this:

 render: function(){ this.mainView && this.mainView.remove(); // if there is already a view, remove it this.mainView = new SomeOtherKindOfViewDeterminedBySomeEvent(); // instantiate the new view this.mainView.render(); this.mainView.$el.appendTo( '#main-content' ); // append it } 

Some notes:

  • Without an explicit call to remove in the view, your application will be vulnerable to memory leaks. This is because events and view properties still exist in the background. For example, if you delete the first line of the above example, I will lose the link to the previous this.mainView , but its events still use memory. This will affect your application over time.
  • Note that I am using appendTo on the last line. When remove called in the view, its entire element, as well as its events, is deleted. If I just did this:

    this.mainView = new SomeOtherKindOfViewDeterminedBySomeEvent({ el: '#main-content' })

    Then after calling remove on this.mainView , #main-content will be removed from the DOM, so I can no longer use this selector. By adding it, I keep this #main-content around as a placeholder, so I can continue to add views to it. This is what you see when you try to untie the SportsManDetailsView , and then render it again.

As for your questions, these are:

$('#sportsManDetails').html(this.$el.html(this.template(this.model.attributes)));

It is not a good practice. First, you used a global jQuery object that defeats the Backbone approach for encapsulated views. Secondly, events are still active in the DOM from previous views, which leads to memory leaks. You can see this when you click the Test button - the handler function will fire every time you create an instance of SportsManDetailsView (the second time the warning message will be displayed twice, then three times, etc.).

You must rely on a parent view or router to handle this interaction. This or bind SportsManDetailsView to the SportsManDetailsView element and never remove it. Then, when the click event occurs in your NameView , its trigger models the event. Then your SportsManDetailsView can listen to the event in the corresponding collection and re-display it accordingly. Announce Magic Events! JavaScript is an event driven language and never forget that you have those in your artillery.

I updated JSFiddle to demonstrate some of what I was talking about.

+6
source
 el: '#sportsManDetails', 

may not work if you do not load jQuery while creating the view constructor.

Make sure you have jQuery loaded before this line

 app.SportsManDetailsView = Backbone.View.extend({ 

you can check it with

 console.log(jQuery.fn.jquery);` 
0
source

Source: https://habr.com/ru/post/975488/


All Articles