I have an Ember.js app (1.0.0-rc.3) using Ember Data (Rev 12) to persist to a Rails backend. On the client side, I have two models that have a one-to-many relationship:
App.Child = DS.Model.extend(
name: DS.attr('string')
parent: DS.belongsTo('App.Parent')
App.Parent = DS.Model.extend(
name: DS.attr('string')
children: DS.hasMany('App.Child')
)
I have configured my store to expect JSON from Rails that embeds relationships rather than side-loading them:
App.Store = DS.Store.extend(
revision: 12
adapter: DS.RESTAdapter.extend(serializer: DS.RESTSerializer.extend(init: ->
#_super()
#map "App.Child",
parents:
embedded: "always"
))
)
And then I have a form for creating a new App.Child record. In the childNewController for the new App.Child form view, I create a new App.Child and a new App.Parent as follows:
#transaction = #get('store').transaction()
#set('content', #transaction.createRecord(App.Child, {}))
#set('content.parent', #transaction.createRecord(App.Parent, {}))
I have textfields bound to the various attributes of both child and parent. When I click the save button on the form, an action in the childNewController commits the changes with #transaction.commit().
THE PROBLEM
When the transaction is committed, Rails receives two JSON POST requests—one to the ParentController with JSON that looks like:
{"parent"=>{"name"=>"William"}}
...and a second POST to the ChildController with JSON that looks like:
{"child"=>{"name"=>"Henry", "parent"=>{"name"=>"William"}}}
Now the Rails ChildController and ParentController are both trying to save the new parent.
Ideally, the request to the ParentController would go away, since I don't want it, and it lacks sufficient information to establish a relationship with the child. I would simply ignore the request to ParentController, but Ember Data is expecting a response from the server.
Another option would be to let ParentController save the new parent, and then ChildController could look up the newly-saved parent record and create the linkage to the new child record. The problem is that the sequence of the two requests is unpredictable.
Because of this sequencing issue, I tried putting Parent.find_or_create_by_name(parent_name) in both Rails controllers, on the theory that if one controller has already created the new parent, the other would only execute a lookup of that new parent record. However, this resulted in duplicate parent records!
I'm really at a loss to understand why Ember Data insists on pushing duplicate JSON representations of the parent to the backend. I'm sure it's a simple configuration error on my part.
Update
I found an acceptable workaround for now by chaining the commits:
newParent = #transaction.createRecord(App.Parent, {name: parentName})
currentChild = #get('content') # we assigned a new App.Child to the childNewController in the new child route's controller setup
currentStore = #get('store')
newParent.one('didCreate', this, ->
now = new Date()
transaction = currentStore.transaction()
transaction.add currentChild
transaction.commit()
transaction = null
)
newParent.get('transaction').commit()
#transaction = null
Is there a better way to do this?
Related
I want to create an entry and its child from the from ui5 so I used the approach of create entry the problem is I want the user to be able to update values of the child nodes so I did the following
var oParentContext = this._oODataModel.createEntry("/Parent",
{ changeId: "edit", properties: {object}, success: this._fnEntityCreated.bind(this), error: this._fnEntityCreationFailed.bind(this) });
for (var i = 0; i < childArray.length; i++) {
var child = childArray[i];
aChildCtx = this._oODataModel.createEntry("/child", {
changeId: "edit",
properties: child,
context: oParentContext
});
aChildEntries.push(aChildCtx.getPath().substring(1));
}
this.getView().setBindingContext(oParentContext);
// I attached also the relation to the front end
this.getView().getModel().setProperty("ToChild", aChildEntries, oParentContext);
In the view I did the binding to the relation ToChild to the table for the user to enter his values.
The display of the parent and child works however I am facing a problem that the view is issuing a get request to Odata with the temporary ID/ToChild. I couldn't find any solution for this. How should we do a deep insert in standard?
P.S. I don't want to use deep_create
Thanks
Best Regards
The create requests are currently being created asynchronously. The definitive parent ID will only be known in the frontend once the parent create request returns, but the requests to create the children are generated before this.
I can see two ways you could make sure the children will be created with the definitive parent id:
Send the parent and child create requests synchronously. Wait until the parent create request has returned succesfully before creating the children.
Send both as a change set. Let the implementation of the oData-service create the parent first and then the children with the now-created parent ID. (You are now setting changeId, the API specifies changeSetId)
I'm relatively new to rails, and trying to write a program that involves querying data (a user_id and list of options) from an external API in one controller (Controller A), pushing the options to a view such that a user can select the option they want (via <select_tag>, and then sending both the user_id and selected_option to a different controller (Controller B).
I use an event listener to send an ajax post request to Controller B when an item is selected from my dropdown list. The issue is that the ajax request creates a new class instance, where in reality I want to reference the instance of Controller B that I instantiated in Controller A.
How I can get both the user_id and selected_option into Controller B based on the architecture I have described?
I have a wizard situation where I create a Parent object, and then build a form with 2 nested children.
The parameters that get submitted look like this:
Parameters: {"room"=>
{"parents_attributes"=>
{"0"=>{"name"=>"r2", "phone"=>"07443107986"},
"1"=>{"name"=>"", "phone"=>""}}},
"commit"=>"Go!", "id"=>"step03"}
(the commit and id are from the wicked wizard step)
If the user refreshes the page, the id's for these children change and the parameters look like this:
Parameters: {"room"=>
{"parents_attributes"=>
{"1"=>{"name"=>"r2", "phone"=>"07443107986"},
"2"=>{"name"=>"", "phone"=>""}}},
"commit"=>"Go!", "id"=>"step03"}
Since the id's are generated by the fields_for.
My controller code retrieves the data like this (the room is saved in the session on a previous step):
#room = Room.find(session[:room_id])
#room.parents.build(room_params[:parents_attributes]['0'])
#room.parents.build(room_params[:parents_attributes]['1'])
This obviously only works if the user does not refresh the page. Also, if validations fire the id's of the children change too.
What is a better way to retrieve these parent_attributes from the params hash?
EDIT
In the wizard step, the child objects are built like this:
when :step03
#room = Room.find(session[:room_id])
2.times{ #room.parents.build }
You can try following to extract hash keys dynamically:
room_params[:parents_attributes].each {|k,_| #room.parents.build(room_params[:parents_attributes][k])}
In ASP.NET, when I want to send a list of model instances to view layer, I convert them into another type (ModelView) by the following code:
var userViewModels = users.select(new {
Name = Name,
UserName = Username
});
I do this because I don't want to send all of my user model data (like password) to view layer. I put this code in my business logic Layer.
I'm using AJAX, and I'm sending my data by JSON protocol. What is the best practice in Ruby on Rails to do a similar action?
In RoR you simply pass models to your views.
Also, view in RoR can directly access (though it's not recommended) models from database. So "hiding" models does not make sense here.
You can consider rails generated scaffold as a close to the best practices.
Controller is used for selecting models from database
View is used for rendering model to the user
Model is the main place to store business logic
Although it is probably unnecessary, as dimakura pointed out, you can select arbitrary attributes when finding by using the select method. Your example would end up looking something like:
#users = User.select(:name, :username)
In my rails app, I want to have a sortable list as part of an object creation. The best practice suggested in Railscast adds the acts_as_list plugin and then initiates AJAX calls to update item position. However, AJAX calls won't work on an unsaved model, which is the situation with new.
One solution would be to save the model immediately on new and redirect to edit. This would have a nice side effect of persisting any change so the user could resume work should he be interrupted.
However, this solution adds the unwanted complexity of saving an invalid model, compromising rails' validation processes. Is there any better way to allow AJAX + validations without going into too much work?
Your new action has the same access to parameters that any other action has. You could pass parameters for the unsaved object back to the new action and an object re-initialized with attributes set could be returned back to the view. For instance:
controller:
class WidgetsController < ApplicationController
def new
#widget = params.has_key?(:widget) ? Widget.new(params[:widget]) : Widget.new
end
..
end
Then in your view you'd have to send params to the new action via a link or a form post.
you can temporary store unsaved object in a 'session'.
like (this code must be in controller)
my_model = MyModel.new(params[:my_model])
session[:my_model_tmp] = my_model