0

I am trying to create a user account using Backbone 1.1.2 and Rails 4.2.beta1.

In backbone I am calling:

public create(){
    var email:string = $('#create-account-email').val(), password:string = $('#create-account-password').val(), passconf:string = $('#create-account-password-confirm').val();
    this.model.set({email: email, password: password, password_confirmation: passconf});
    this.model.save(null, {success: this.success, error: this.error});
}

which correctly calls the rails create method in my webservice with the following request parameters:

{"email":"test@test.com","password":"123456","password_confirmation":"123456"}

It is going through the filter method

def account_params
  #breakpoint
  params.require(:account).permit(:password, :password_confirmation, :email)
end

but if I place a breakpoint in the above noted spot and inspect the params object I get:

{"email"=>"test@test.com", 
 "password"=>"123456", 
 "password_confirmation"=>"123456", 
 "controller"=>"accounts", 
 "action"=>"create", 
 "account"=>{"email"=>"test@test.com"}, 
 "format"=>"json"}

Which to me looks correct at first glance. But the account creation fails because the password is nil

if inspecting the result of account_params I only get:

{"email" => "test@test.com")

Why is the password not being included in the account parameter object. I am using all default sacaffolded code with rails and default configuration for Backbone.

SnareChops
  • 13,175
  • 9
  • 69
  • 91
  • can you post your form? – Mandeep Oct 05 '14 at 18:01
  • @Mandeep I edited the above code for you. It is a form, but I'm reading it off the form using `$().val()` so I included that code for you. – SnareChops Oct 05 '14 at 18:04
  • Ah alright and how are you sending these params to server? Looking at your params hash they don't seem to nest properly – Mandeep Oct 05 '14 at 18:14
  • @Mandeep I'm currently just letting backbone do it's thing. Do I need to modify the way backbone sends the parameters. It was my understanding that backbone + rails *just work* – SnareChops Oct 05 '14 at 18:17
  • 1
    No, they don't *just work* together out of the box. Backbone wants to send `{p1:v1, p2:v2}` but Rails wants to see `{model_name: {p1:v1, p2:v2}}`. There are gems that help glue things together or you can sort out a `sync` override yourself. Do some googling for "backbone-rails". – mu is too short Oct 05 '14 at 18:59

1 Answers1

0

I found a GitHub repo for a ruby gem backbone-rails and used the sync override that they included here.

Backbone._sync = Backbone.sync;
Backbone.sync = function (method:string, model:Backbone.Model, options?: any){
    if(options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
        options.contentType = 'application/json';
        var data:any = JSON.stringify(options.attrs || model.toJSON(options));
        if(model.paramRoot) {
            data = {};
            data[model.paramRoot] = model.toJSON(options);
        } else {
            data = model.toJSON();
        }
        options.data = JSON.stringify(data);
    }
    return Backbone._sync(method, model, options);
}

I removed some items I didn't need, but this wraps the backbone attributes into an object and sends it to the rails app in the format it needs.

So the parameters:

{"email":"test@test.com",
 "password":"123456",
 "password_confirmation":"123456"}

Become:

{"account":
    {"email":"test@test.com",
     "password":"123456",
     "password_confirmation":"123456"}
}

This isn't without a little bit of reworking the backbone application code...

As I am using typescript I had to add the _sync() method to the backbone.d.ts file using the following signature.

declare module Backbone {

    function _sync(method: string, model: Model, options?: JQueryAjaxSettings):any;

    ...
}

I also needed to add the following to Backbone.Model

class Model extends ModelBase implements OptionalDefaults {

    paramRoot: string;

    ...
}

And then I added the paramRoot property to my model:

export class Account extends Backbone.Model {
    public urlRoot: string;
    public validation:any;
    public paramRoot:string;
    constructor(attributes?: any, options?: any){
        this.paramRoot = 'account';
        this.urlRoot = 'http://mydomain.fake/accounts';
        this.validation = {
            email: {
                required: true,
                pattern: 'email'
            },
            password: {
                required: true,
                minLength: 6
            }
        };
        super(attributes, options);
    }
}

There are other ways to allow the _sync() method and the paramRoot property to pass the compiler errors without modifying the backbone.d.ts file but instead extending the objects and adding it in another .d.ts file. But this was ok for me as I plan on using this in future projects and I don't mind having the .d.ts file being slightly modified.

Hope this helps someone in the future.

SnareChops
  • 13,175
  • 9
  • 69
  • 91