Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone model without collection. Sending duplicate data to server?

I have a Backbone app powered by a rails backend. I have an Invitation resource and I can send invitations by sending a POST request to the create action of the invitation's controller.

My Backbone model looks like this (coffeescript):

class Invitation extends Backbone.Model
  urlRoot: '/invitations'

And the template for the form to send an invitation follows. I tried to make it as close to the normal rails forms as possible as it seems like Rails would deal with that best:

<form action="/invitations" accept-charset="UTF-8" id="new_invitation" class="new_invitation" method="post">
  <input id="invitation_recipient_name" class="invitation_recipient_name" type="text" name="invitation[recipient_name]" />
  <input id="invitation_recipient_email" class="invitation_recipient_email" type="text" name="invitation[recipient_email]" />
  <input type="submit" class="btn primary" name="commit" id="invite" value="Send Invitation" />
</form>

Here is my Backbone View for that model and template

class InvitationView extends Backbone.View
  # this is the template shown above
  template: JST['backbone/templates/invitation']
  events:
    'click #invite': 'sendInvite'

  render: ->
    $(this.el).html this.template()
    this
  sendInvite: (e) ->
    e.preventDefault()
    name = this.$('#invitation_recipient_name')
    email = this.$('#invitation_recipient_email')
    this.model.save
      recipient_name: name.val()
      recipient_email: email.val()

The problem is that when I click the submit button and the sendInvite method is called, my server receives data with the following structure:

Parameters: {"recipient_name"=>"A name", "recipient_email"=>"[email protected]", "invitation"=>{"recipient_email"=>"[email protected]", "recipient_name"=>"A name"}}

Now this actually works, since my invitations#create action expects to work with parameters of the form: params[:invitations], this is the standard for rails. However the fact that the name and email are being sent twice in the request seems a sign that something is wrong with my setup.

Am I doing something wrong or is this correct?

Here is my controller action if anyone wants to see it:

  # POST /invitations
  def create
    @invitation = current_user.sent_invitations.new params[:invitation]

    respond_to do |format|
      if @invitation.save
        format.json { render_for_api :invitation, json: @invitation, status: :created }
      else
        format.json { render json: @invitation.errors, status: :unproccessible_entity }
      end
    end
  end

Edit This is what my Invitation model looks like if I set the attributes and log it just before saving:

  Invitation
    _changed: false
    _changing: false
    _escapedAttributes: Object
      __proto__: Object
    _previousAttributes: Object
      recipient_email: "[email protected]"
      recipient_name: "Dave"
      __proto__: Object
    attributes: Object
      recipient_email: "[email protected]"
      recipient_name: "Dave"
      __proto__: Object
    cid: "c17"
    __proto__: ctor

THat log is produced by the following code btw:

sendInvite: (e) ->
    e.preventDefault()
    name = @$('#invitation_recipient_name')
    email = @$('#invitation_recipient_email')
    @model.set recipient_name: name.val(), recipient_email: email.val()
    console.log "THe model to save", @model

Edit 2
This is how I'm instantiating my view in my router. How should I change this so that Backbone automatically keeps track of my models attributes, even though I'm not fetching and setting them from the server?.

  var TeamRouter = Backbone.Router.extend({
    routes: {
      'members': 'members'
    },

    members: function() {
      @invite = new MoveOutOrg.Models.Invitation();
      @inviteView = new MoveOutOrg.Views.InvitationView({
        model: @invite
      });
      $('#stage').append(@inviteView.render().el);
    }
  });
like image 307
David Tuite Avatar asked Sep 11 '11 16:09

David Tuite


2 Answers

The params[:recipient_name] and params[:recipient_email] fields are a result of your call to model.save(). The passed in parameters are optional. You don't need to pass anything to save if the model is already created with the correct values. The missing piece is how you instantiated the view.

Also I'd change the event to "submit #new_invitation". It's more descriptive. You care about the form being submitted... not that they clicked the button. It also catches the case where they submit the form without clicking the button (by pressing enter for instance).

To link the view to the model, use the initialize method.

initialize: ->
  @model.bind('change', @render)

The basic thing to remember is that model events bubble up through collections and to anyone else listening. So now when you save, if any of the properties were changed, a change event will be fired which will then cause the view to re-render.

like image 50
fearphage Avatar answered Nov 01 '22 20:11

fearphage


The reason why the data was duplicated in the params is due to the ActiveSupport.wrap_parameters setting being turned on in Rails for JSON requests by default. Check out /config/initializers/wrap_parameters.rb in your Rails project.

You can disable it by changing:

ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: [:json]
end

to

ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: []
end

For more info: http://guides.rubyonrails.org/action_controller_overview.html#parameters

like image 34
Ryan Wood Avatar answered Nov 01 '22 22:11

Ryan Wood