Search code examples
javascriptbackbone.js

Backbone.js code vs regular JS code


Been reading up on Backbone.js where they give this example.

var Sidebar = Backbone.Model.extend({
  promptColor: function() {
    var cssColor = prompt("Please enter a CSS color:");
    this.set({color: cssColor});
  }
});

window.sidebar = new Sidebar;

sidebar.on('change:color', function(model, color) {
  $('#sidebar').css({background: color});
});

sidebar.set({color: 'white'});

sidebar.promptColor();

A prompt asks you for a color, when you enter one, the sidebar's color changes. My question is, why should I prefer the above code to the code below? I think it has something to do with code structure but I can't say exactly why. Is it because this example is too simple, and the advantages of writing the above code only appear when an application becomes large?

Ignore the backbone code for initializing the starting color and calling promptColor(), i left it in there for context.

function promptColor(){
    var cssColor = prompt("please enter a css color:");
    $('#sidebar').css({background:cssColor})
}

Solution

  • First of all the given example is a poor one showing DOM manipulation inside a model..! No. that is not where you manipulate DOM.

    The key thing to note is that, backbone gives structure to your application, lets you separate concerns easily.

    If you see the below example (again a poor, sidebar thingy example), the presentation logic is entirely handled by Backbone.View instance.

    • It is an independent instance, you can create n number of them
    • Backbone created the DOM element for you with specified properties
    • Backbone made it very easy to setup delegated event handlers
    • Backbone made it very easy to define the initialization code to be run upon creation of a sidebar instance
    • We don't have to traverse the entire DOM and chances of conflicts are very less since we work with each view's element inside it.
    • you can simply do instance.remove() to remove the event handlers and element from DOM
    • and much much more

    The state of a component and related data are stored in Backbone.Model instances.

    • Lets you define initial state
    • Can easily setup initialization logic
    • Events makes it easy to handle changes to data
    • Lets you validate changes to data
    • Lets you easily sync the data with a backend
    • and much more

    var Color = Backbone.Model.extend({
      defaults: {
        color: 'white',
        accepted: ['white', 'red', 'green', 'blue']
      },
      validate: function(attrs) {
        if (this.get("accepted").indexOf(attrs.color) < 0)
          return new Error();
      }
    });
    var Sidebar = Backbone.View.extend({
      attributes: {
        class: 'sidebar'
      },
      initialize: function() {
        this.listenTo(this.model, 'change:color', this.switchColor);
        this.render();
      },
      events: {
        'input .color': 'updateColor',
        'click .close': 'remove'
      },
      render: function() {
        this.$el.append($('#sidebar').html());
        return this;
      },
      updateColor: function(e) {
        this.model.set({
          color: $(e.target).val()
        }, {
          validate: true
        });
      },
      switchColor: function(model, color) {
        this.$el.css({
          background: color
        });
      }
    });
    
    for (var i = 0; i < 3; i++) {
      $('body').append(new Sidebar({
        model: new Color()
      }).el);
    }
    * {
      margin: 0;
      padding: 0;
    }
    html,
    body {
      height: 100%
    }
    .sidebar {
      display: inline-block;
      box-sizing: border-box;
      position: relative;
      height: 100%;
      padding: 5px 15px 5px 0;
      margin: 0 2px;
      border: 5px solid dodgerblue
    }
    .close {
      display: inline-block;
      position: absolute;
      top: 5px;
      right: 0;
      height: 18px;
      text-decoration:none;
      border: 0px solid grey;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.2.3/backbone-min.js"></script>
    <script type="text/template" id="sidebar">
      <input type="text" class="color" placeholder="enter bg color... red, green, blue"/>
      <a href="#" class="close">&#10006;</a>
    </script>

    Of course you can directly modify the color without setting it to a model in this particular example, the point is to show how you separate the concerns of components in real world applications.

    The models job is to hold the data and make sure it's valid at all times. View presents the data to user and lets him work with it. Model acts as the source of truth.

    If you use plain js, you'll find that your data and DOM are tightly coupled and you'll have to write custom logic to huge lot of stuff that backbone provides out of the box... like it's event system... router... collection... sync API.. (and we're not using any of them apart from the tiny use of event system which is why its a poor example)


    To answer your question: "Is it because this example is too simple" - Yes. People normally doesn't go after an MV* js framework to set up a prompt... This is like using a hand grenade to open a plastic door like Alexander mentioned.

    You'll see backbone excels when you want to perform CRUD operations, handle routing, communicate with backend etc. For eg to send the latest color (be it any data) all you need to do is add a url property to the model declaration, and call model.save(). How cool is that..?