Skip to content

Has Many Relationship Tab

coezbek edited this page Dec 14, 2021 · 8 revisions

Question

How do you add a has_many tab to manage relationships of a model?

Answer

There is a plan to eventually implement a "subresource" feature, allowing easy management of has_many associations, either inline or from a separate form. However here is how you would accomplish it in the mean time.

I'll use an example below of a Project that has_many Tasks. First we'll add the tab and table on the parent resource:

Trestle.resource(:projects) do
  form do |project|
    tab :project do
      text_field :name
      text_area :description
    end

    tab :tasks, badge: project.tasks.size do
      table project.tasks, admin: :tasks do
        column :description, link: true
        column :done, align: :center
        column :created_at, align: :center
        actions
      end

      concat admin_link_to("New Task", admin: :tasks, action: :new, params: { project_id: project }, class: "btn btn-success")
    end
  end
end

The "New Task" link is probably the most complicated bit, but we want to ensure that we pass the project id as a parameter when building the task. To actually use this, we need to override the build_instance block in the child (Task) resource:

Trestle.resource(:tasks) do
  build_instance do |attrs, params|
    scope = params[:project_id] ? Project.find(params[:project_id]).tasks : Task
    scope.new(attrs)
  end
end

This uses the Project association to build the new task if the project id is provided, otherwise it will ignore it and simply to Task.new. You'll need to make sure that your task form includes a project_idfield somewhere, either via a select tag or hidden_field.

A couple of potential improvements are to load the task form within a dialog (which may or may not make sense for your use case), as well as to show the project select field depending on whether it has been previously set:

form dialog: true do |task|
  if task.project
    hidden_field :project_id
  else
    select :project_id, Project.all
  end

  text_field :description
  check_box :done
end

Scoped resources

If you want to use scoped resources, prefix the class name with module name and "/":

Trestle.resource(:projects) do
  form do |project|
    tab :project do
      text_field :name
      text_area :description
    end

    tab :tasks, badge: project.tasks.size do
      table project.tasks, admin: 'custom_scope/tasks' do
        column :description, link: true
        column :done, align: :center
        column :created_at, align: :center
        actions
      end
    end
  end
end