0

Delete Function:

My backbone function to delete a model (called on the click of a button) looks like this:

deleteCar: function (ev) {
        console.log(this.car.toJSON());
        this.car.destroy({
          success: function () {
            router.navigate('', {trigger:true});
          }
        });

    return false;
},

this.car is created in another function but in the main scope. As seen above, I'm logging the contents of this.car and I get a JSON like this:

Object {carId: "17", carDes: "awesome"}

Now, I'm calling this.car.destroy... and I'm observing the network tab on Chrome. I can see a DELETE request firing but there is no data appended to the request. It's just an empty request to the correct url.

How can I attach my car model to the request?

Edit:

Render Function:

This is the function that creates a new car model. It is in the same Backbone view as the Delete function above.

  render: function(options) {
      var that = this;
      if (options.id) {
          that.car = new Car({
              carId: options.id
          });
          that.car.url = 'http://localhost/Project/index.php/rest/resource/car/carId/' + options.id;
          that.car.fetch({
              success: function(car) {
                  console.log(that.car.toJSON());
                  that.car.url = 'http://localhost/Project/index.php/rest/resource/car/';
                  var template = _.template($("#edit-car-template").html());
                  var data = {
                      car: that.car
                  };
                  that.$el.html(template(data));
              }
          });
      } else {
          var template = _.template($("#edit-car-template").html());
          var data = {
              car: null
          };
          that.$el.html(template(data));
      }
  }

Edit-Car-Template

<script type="text/template" id="edit-car-template">
    <form class="edit-car-form">
        <legend>
            <%= car ? 'Edit' : 'New' %> Car</legend>

        <label>Car Description</label>
        <input name="carDes" type="text" value="<%= car ? car.get('carDes') : '' %>">
        <hr />
        <button type="submit" class="btn">
            <%=  car ? 'Update' : 'Create' %>
        </button>
        <% if(car) { %>
            <input type="hidden" name="carId" value="<%= car.get('carId') %>" />
            <button data-car-id="<%= car.get('carId') %>" class="btn btn-danger delete">Delete</button>
            <% }; %>
    </form>
</script>

Edit 2: Layout:

The view layout of my application is as follows:

layout

And the following code is used to render the CarListView (get the list of cars using the REST API)

var CarListView = Backbone.View.extend({
    el: '.page',
    render: function () {
        var that = this;
        this.cars = new Cars();
        this.cars.fetch({
            success: function () {
                var template = _.template($("#car-list-template").html());
                var data = {cars: that.cars.toJSON()};
                that.$el.html(template(data));
            }
        })
    }
});

Models

My car model and car collection are defined as follows:

var Car = Backbone.Model.extend({
    urlRoot: ROOT + 'car',
    idAttribute: 'carId'
});
var Cars = Backbone.Collection.extend({
    model: Car,
    url: ROOT + 'car'
})

Update Function:

And following is the code for saving (updating) the model once the update button is clicked. This part works perfectly.

events: {
    'submit .edit-car-form': 'saveCar',
    'click .delete': 'deleteCar'
},
saveCar: function (ev) {
    console.log('in save');
    var carDetails = $(ev.currentTarget).serializeObject();
    var car = new Car();
    car.save(carDetails, {
        success: function (car) {
            router.navigate('', {trigger: true});
        }
    });
    return false;
}
  • Can you create a [mcve]..? – T J Jan 02 '16 at 07:14
  • @TJ I was hoping I was missing something minor, I have edited the question to show my render function and tempate. –  Jan 02 '16 at 07:35
  • Does your card model has an id attribute..? – T J Jan 02 '16 at 07:56
  • @TJ I have this line in the car model: `idAttribute: 'carId',` –  Jan 02 '16 at 08:05
  • @TJ could you pls take a look at edit 2 to my question. I think there is a small problem in how my model is passed between the 2 views. Thanks. –  Jan 02 '16 at 11:51
  • By the way, you should not call `.fetch()` in any `.render()` method. Call it elsewhere (in the constructor for instance), then bind the `sync` event of your model to `render`. Otherwise there is no point in using Backbone at all : )) – moonwave99 Jan 02 '16 at 12:01
  • @moonwave99 How can I bind the `sync` even of a model to `render` function? –  Jan 02 '16 at 13:42
  • @john [see docs](http://backbonejs.org/#Events-listenTo). – moonwave99 Jan 02 '16 at 13:45

1 Answers1

4

Sounds like this is working as intended. The Backbone.Model.destroy() method will generate an HTTP DELETE call to the model's the relevant URL. It typically won't have any content in the method body, as the ID at the end of the URL should be all you need to delete the RESTful resource. If you need different behavior, Backbone expects you to override the model's sync method (see here and here). Traditionally HTTP servers ignored any body sent on an HTTP DELETE method call, and the HTTP spec doesn't mention it needing a body, so Backbone is just encouraging you to follow that example.

Note also that setting the url property on the model instance itself is not the usual way to ensure the URL has the model ID. If you're going to use a Backbone.Model outside of a Backbone.Collection, you should include the urlRoot property when specifying Car ... where you can also specify the fetch function if you really need to override it:

var Car = Backbone.Model.extend({

    urlRoot: '/Project/index.php/rest/resource/car`

    fetch: function(options) {
        // ... if needed, but note you can provide a "success" 
        // callback in the options; see below.
    }
});

Doing it that way, Backbone will ensure that any call to car.destroy() will generate the right URL. For instance:

var car = new Car({
    id: 123
});

// This will GET /Project/index.php/rest/resource/car/123
car.fetch({ 
    success: function() {
        console.log('Received: ' + car.toJSON());
        // ... anything else you need to do after fetching.
     });
});

// ... some time later ...

// This will DELETE /Project/index.php/rest/resource/car/123
car.destroy({
    success: function() {
        console.log('Car has been deleted on server');
    }
 });

Again, if your server needs more info than the model's ID to delete something, you'll have to override the sync method... but that's another question!

Peter Wagener
  • 2,073
  • 13
  • 20
  • Thank you. So this means that a DELETE call is actually something like a GET? Similar in a sense that there is nothing in the body of the call. Also, what must I return from the REST API to this delete call? –  Jan 02 '16 at 12:05
  • @john probably [200 or 204 status code](http://stackoverflow.com/a/2342589/2333214) – T J Jan 02 '16 at 12:52
  • Yeah -- returning some success status code is typical. Some API's choose to return the full contents of the thing that was deleted in the response body, though if you don't need it don't bother. – Peter Wagener Jan 02 '16 at 15:49