1

I have a Marionette application with a large number of views. I want to log a debug message to the console when each is rendered (and possibly, in the future, on other events)

I am currently doing this by logging from each View's onRender method:

MyModule.MyViewType = Marionette.ItemView.extend({
    // ... view properties and methods

    onRender: function() {
      // ... other onRender code
      console.debug('MyModule.MyViewType %s %s', this.cid, 'render');
    }
});

This works, but it has several disadvantages:

  • The logging code must be added manually to each view.
  • Several views don't have custom onRender actions so I'm adding methods for the purpose of debugging only. That feels wrong.
  • If I want to alter or remove the logging methods completely (e.g. to go to production), I need to alter a lot of code.
  • If I want to add code for another event, e.g. show, I will need to add an event handler or a new method to every view.

Is there a way to log every View render without adding code to each View?

joews
  • 29,767
  • 10
  • 79
  • 91

1 Answers1

5

Yes. You can decorate Backbone.View.constructor to hook into the View creation lifecycle. You can then register callbacks for any event on all View instance..

!function() {
  // Development: log view renders and other events
  // Don't execute this function in production.


  // Save a reference to the original Backbone.View and create a new type
  // that replaces the constructor method
  var OriginalView = Backbone.View,
      LoggingView = OriginalView.extend({
        constructor: function() {

          // Execute the original constructor first
          OriginalView.apply(this, arguments);

          // Allow views to define a `type` property to clarify log messages
          var type = this.type || 'Unknown View Type',
              cid = this.cid;

          // Bind to Marionette.View's `render` event (and any other events)
          this.listenTo(this, 'render', function(e,b) {
            console.debug('%s %s - %s', type, cid, 'render');
          });
        }
      });

  // Replace Backbone.View with our decorated view
  Backbone.View = LoggingView;
}();

To log view types, add a property to your View implementations:

MyModule.MyViewType = Marionette.ItemView.extend({
    type: 'MyModule.MyViewType',
    // ... rest of the view code
});

Reliably determining a JavaScript's object "type" (constructor name) is problematic, so adding this property is the best approach to determine the type of view that is being rendered.

This answer is easily generalised to multiple event types by providing an array of the events you want to be logged:

var events = ['render', 'show', 'beforeClose'];

events.forEach(function(eventType) {
  this.listenTo(this, eventType, function() {
    console.debug('%s %s - %s', type, cid, eventType)
  });
}).bind(this);

Example output:

Projects.ViewType1 view13 - render 
Projects.ViewType2 view3 - render 
Projects.ViewType3 view6 - render 
Projects.ViewType4 view9 - render 
Projects.ViewType4 view17 - render
Projects.ViewType4 view19 - render 
Projects.ViewType2 view3 - render

This approach solves all of the problems described in the question - there is a a small amount of code, Views don't need altering directly and there is a single function call that can be omitted to remove logging in production code.

This approach is specific to Marionette - because vanilla Backbone's render method is user-defined there is no equivalent of the render event.

For more detail on extending Backbone constructor methods see Derick Bailey's blog.

Community
  • 1
  • 1
joews
  • 29,767
  • 10
  • 79
  • 91