ruby-on-rails-3batman.js

BatmanJS and Rails3: get object attribute in controller


I assumed this will work in my BatmanJS Controller:

project = Project.find parseInt(params.id, 10), (err) ->
  throw err if err

@set 'project', project
**@set 'owner_id', project.get("owner_id")**

but project.get("owner_id") is always undefined. Im wondering if my call with Project.find is wrong, since i thought it is quite similar to rails. What am i doing wrong?

owner_id is an attribute of the project and valid in the view:

%p{"data-bind" => "owner_id"}

works and spits out the correct owner id.


Solution

  • Like many things in JS, Batman.Model.find is an asynchronous method, from the docs...

    Model.find() asks a model to call a given callback with the record with the given id.

    So, here's the order of things as they're happening in your code...

    1. Call Project.find which tells Batman.Model to call our callback function after it has retrieved the data from the DB.
    2. Assign the return value of that function (which is kind of a promise that will be filled in later).
    3. Set the 'project' keypath to that promise.
    4. Immediately try to call #get on that promise (before it has a value).
    5. Some time later the find returns from the DB and so project and therefore your 'project' keypath are updated with the value of your project - but 'owner_id' is still undefined.

    Fix this by not trying to set intermediate keypaths, but just directly binding to the attributes of your model in your views.

    Ie. have this in your controller...

    @set 'project', Project.find parseInt(params.id, 10), (err) -> throw err if err
    

    ...and then this in your view...

    <p data-bind="project.owner_id"></p>
    

    When the project eventually arrives from the DB, that element will be updated with the owner_id

    Alternatively, if you want to do something with that owner_id value in your controller, then setup an observer for the keypath, that will be triggered when the project arrives from the DB, eg.

    @observe 'project.owner_id', (id) =>
      if id
        @set 'owner', Owner.find id, (e) -> throw e if e
    
    @set 'project', Project.find parseInt(params.id, 10), (err) -> throw err if err
    

    So, when the Project.find returns it will set the 'project' and in turn the 'project.owner_id' keypaths, which will trigger your observer to go and retrieve the Owner and set the 'owner' keypath. So now you could have in your view...

    <h3 data-bind="owner.name"></h3>