Integrating JSON feed with Backbone JS


I'm currently working on a project where I type a keyword inside an input box and when I click send it hits a PHP server with a link like (localhost/json-status.php?query=input text) and it returns whatever is after "query=" in json format. Now I've accomplished this with jQuery and I'm trying to do this again in backbone js.

$("#updateStatus").click(function(){

   var query = $("#statusBar").val();

   var url = "json-status.php" + "?query=" + query;

   $.getJSON(url,function(json){
            $.each(json.posts,function(i,post){
         $("#content").append(
            '<div>'+
               '<p>'+post.status+'</p>'+
            '</div>'
         );
      });
   });
});

I've pretty much ported over what I did in jQuery over to backbone js and it's not working out as expected so far, please let me know if my approach is correct and how I can solve my problem.

backbone code:

(function ($) {
   Status = Backbone.Model.extend({
      status: null
   });

   StatusList = Backbone.Collection.extend({
      initialize: function (models, options) {
         this.bind("add", options.view.addStatusList);
      }
   });

   AppView = Backbone.View.extend({
      el: $("body"),
      initialize: function () {
         this.status = new StatusList( null, { view: this });
      },
      events: {
         "click #updateStatus":   "getStatus",
      },
      getStatus: function () {
         var url = "json-status.php" + "?query=" + $("#statusBar").val();
         var statusModel;

         $.getJSON(url,function(json){
            $.each(json.posts,function(i,post){
               statusModel = new Status({ status: post.status });
               this.status.add( statusModel );
            });
         });
      },
      addStatusList: function (model) {
         $("#status").prepend("<div>" + model.get('status') + "</div>");
      }
   });

   var appview = new AppView;
})(jQuery);

PHP server code which returns in json format (this works fine):

<?php
    $getQuery = $HTTP_GET_VARS["query"];

    $json='
    {"posts":[
        {
            "status": "' . $getQuery . '"
        }
    ]}
    ';
    echo $json;
?>

And if you wish to copy/paste what I have so far it's:

<!DOCTYPE html>
<html>
<head>
    <title>JSON Test</title>
</head>
<body>

    <input value="What's on your mind?" id="statusBar" /><button id="updateStatus">Update Status</button>

    <div id="content">

    </div>

    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
    <script src="http://ajax.cdnjs.com/ajax/libs/underscore.js/1.1.4/underscore-min.js"></script>
    <script src="http://ajax.cdnjs.com/ajax/libs/backbone.js/0.3.3/backbone-min.js"></script>

    <script type="text/javascript">
        $("#statusBar").click(function() {
            $(this).val("");
        });

        (function ($) {
            Status = Backbone.Model.extend({
                status: null
            });

            StatusList = Backbone.Collection.extend({
                initialize: function (models, options) {
                    this.bind("add", options.view.addStatusList);
                }
            });

            AppView = Backbone.View.extend({
                el: $("body"),
                initialize: function () {
                    this.status = new StatusList( null, { view: this });
                },
                events: {
                    "click #updateStatus":  "getStatus",
                },
                getStatus: function () {
                    var url = "json-status.php" + "?query=" + $("#statusBar").val();
                    var statusModel;

                    $.getJSON(url,function(json){
                        $.each(json.posts,function(i,post){
                            statusModel = new Status({ status: post.status });
                            this.status.add( statusModel );
                        });
                    });
                },
                addStatusList: function (model) {
                    $("#status").prepend("<div>" + model.get('status') + "</div>");
                }
            });

            var appview = new AppView;
        })(jQuery);
    </script>

</body>
</html>

Thank you for your time.


Julien's code.

StatusList = Backbone.Collection.extend({
    model: Status,
    value: null,
    url: function(){ return "json-status.php?query=" + this.value;}
});

AppView = Backbone.View.extend({
    el: $("body"),
    initialize: function () {
        _.bindAll(this, "render");// to solve the this issue
        this.status = new StatusList( null, { view: this });
        this.status.bind("refresh", this.render);
    },
    events: {
        "click #updateStatus" :"getStatus",
    },
    getStatus: function () {
        this.status.value =  $("#statusBar").val();
        this.status.fetch(this.status.value);
    },
    render: function () {
        var statusEl = $("#status");
        this.status.each( function(model) {
            statusEl.prepend("<div>" + model.get('status') + "</div>");
        });
    }
});

var appview = new AppView;

Full HTML (part 2):

<!DOCTYPE html>
    <html>
    <head>
        <title>JSON Test</title>
    </head>
    <body>

        <input value="What's on your mind?" id="statusBar" />
        <button id="updateStatus">Update Status</button>

        <div id="status">

        </div>

        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
        <script src="http://ajax.cdnjs.com/ajax/libs/underscore.js/1.1.4/underscore-min.js"></script>
        <script src="http://ajax.cdnjs.com/ajax/libs/backbone.js/0.3.3/backbone-min.js"></script>

        <script type="text/javascript">
            $("#statusBar").click(function() {
                $(this).val("");
            });

            Status = Backbone.Model.extend();

            StatusList = Backbone.Collection.extend({
                model: Status,
                value: null,
                url: function(){ return "json-status.php?query=" + this.value;}
            });

            AppView = Backbone.View.extend({
                el: $("body"),
                initialize: function () {
                    _.bindAll(this, "render");// to solve the this issue
                    this.status = new StatusList( null, { view: this });
                    this.status.bind("refresh", this.render);
                },
                events: {
                    "click #updateStatus" :"getStatus",
                },
                getStatus: function () {
                    this.status.value =  $("#statusBar").val();
                    this.status.fetch(this.status.value);
                },
                render: function () {
                    var statusEl = $("#status");
                    this.status.each( function(model) {
                        statusEl.prepend("<div>" + model.get('status') + "</div>");
                    });
                }
            });

            var appview = new AppView;

        </script>

    </body>
    </html>

And the PHP is still the same from the one originally documented.

As for your general design, you should use a Backbone.Model and Collection to fetch your statuses:

Status = Backbone.Model.extend();

StatusList = Backbone.Collection.extend({
  model: Status,
  value: null
  url: function(){ return "json-status.php" + "?query=" + this.value;
});

Your view should be listening to the StatusList and not the StatusList creating a binding to the view:

AppView = Backbone.View.extend({
  el: $("body"),
  initialize: function () {
    _.bindAll(this, "render");// to solve the this issue
    this.status = new StatusList( null, { view: this });
    this.status.bind("refresh", this.render);
  },
  events: {
    "click #updateStatus":  "getStatus",
  },
  getStatus: function () {
    this.status.value =  $("#statusBar").val();
    this.status.fetch()
  },
  render: function () {
    var statusEl = $("#status")
    this.status.each( function(model){
      statusEl.prepend("<div>" + model.get('status') + "</div>");
    }
  }
});

There is a couple of things here:

  • an attribute on the model is defined by set/get not by a js attributes like you did with status
  • try to decouple stuff views know about models but models do not know about views

FYI (from the documentation)

We've taken the opportunity to clarify some naming with the 0.5.0 release. Controller is now Router, and refresh is now reset.

So if you are using the latest version dont forget to change refresh to reset in this line:

this.status.bind("refresh", this.render);