I have this relationship working using HTML and HTTP, but I'm completely lost as to make this work with Batman.js. I've made a plethora of attempts, but none have been successful. I've also attempted to implement the steps in this guide with no luck. Can anyone point me in the right direction?
My routes and models are the Batman.js equivalent of the rails files, aside from the controller, which is where I anticipate I will need to implement some fancy Batman.js
Routes
resources :tasks do
resources :task_entries
end
Models
class Task < ActiveRecord::Base
has_many :task_entries
accepts_nested_attributes_for :task_entries
# must have at least one entry, built in controller
validates :task_entries, presence: true
end
class TaskEntry < ActiveRecord::Base
belongs_to :task
has_one :item
accepts_nested_attributes_for :item
# must have an item, built in controller
validates :item, :task, presence: true
end
class Item < ActiveRecord::Base
belongs_to :parent, polymorphic: true
validates :title, presence: true
end
Controller
class TasksController < ApplicationController
def new
@task = Task.new
@task.task_entries.build(item: Item.new)
end
def task_params
returned_params = params.require(:task).permit(
task_entries_attributes: [
:status, :due_at, :defer_until, :estimated_duration, :completed_at,
item_attributes: [
:title, :description, :flagged, :visibility, :difficulty
]
]
)
end
end
Form that works
tasks/_form.html.slim
= form_for @task do |f|
= f.fields_for :task_entries do |e|
= e.fields_for :item do |i|
= i.text_field :title
= i.text_area :description
= i.check_box :flagged
= i.select :visibility, Item.visibility.options
= i.select :difficulty, Item.difficulty.options
= e.select :status, TaskEntry.status.options
= e.datetime_select :due_at
= e.datetime_select :defer_until
= e.number_field :estimated_duration
= e.datetime_select :completed_at
= f.submit
HTTP example that works
POST /tasks HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: localhost:3000
task[task_entries_attributes][0][item_attributes][title]=help+me+stack+overflow%2C+you%27re+my+only+hope
First, I'd say until this PR is merged, I wouldn't count on batman.js to encode your attributes. Instead, let batman.js encode the data its own way, then transform it in your Rails controller. (That's how we do it in Planning Center Check-Ins.)
To transform the params in your Rails controller, update the task_params
method:
def task_params
# first, permit the params without the _attributes suffix:
returned_params = params.require(:task).permit(
task_entries: [
:status, :due_at, :defer_until, :estimated_duration, :completed_at,
items: [
:title, :description, :flagged, :visibility, :difficulty
]
]
)
# then, reassign them to different keys in the params hash (and delete the old keys)
if returned_params[:task_entries].present?
task_entries_params = returned_params.delete(:task_entries)
returned_params[:task_entries_attributes] = task_entries_params
# do the same for the items params
if returned_params[:task_entries_attributes][:items].present?
items_params = returned_params[:task_entries_attributes].delete(:items)
returned_params[:task_entries_attributes][:items_attributes] = items_params
end
end
end
This is assuming batman.js models that look like this:
class MyApp.Task extends Batman.Model
# ...
@hasMany "task_entries", saveInline: true
class MyApp.TaskEntry extends Batman.Model
# ...
@encode "status", "due_at", "defer_until", "estimated_duration", "completed_at"
@hasOne "item", saveInline: true, polymorphic: true, as: "parent"
class MyApp.Item extends Batman.Model
# ...
@encode "title", "description", "flagged", "visibility", "difficulty"
@belongsTo "parent", polymorphic: true
I'd agree that transforming the params isn't ideal. Hopefully that will be cleared up in batman.js 0.17.