Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Nested Forms #192

Closed
ismaGNU opened this issue Nov 8, 2015 · 21 comments
Closed

Feature Request: Nested Forms #192

ismaGNU opened this issue Nov 8, 2015 · 21 comments
Assignees

Comments

@ismaGNU
Copy link

ismaGNU commented Nov 8, 2015

Hi guys,

Thanks in advance for this great project, really good job. I was looking for something like that such a long time.

I was digging around and I have missed and option to build nested forms. I don´t know if there is an easy way to do it. IMHO its needed for the example app when you add line items to an order. Its painful to first create an order, save it then create line items and associate it. This happens on all kind of database models and It would be amazing to have this feature.

I think you can achieve this behaviour by adding custom fields types, overriding form view. Although you must allow nested attributes on your models.

Let me know what do you think about it and set a guidelines to approach this feature.

Thanks in advance and keep pushing!

@c-lliope
Copy link
Contributor

c-lliope commented Nov 8, 2015

hey, @ismaGNU - thanks! Glad to hear you like it!

You're right - at the moment we don't support nested forms. There are a couple ways you could implement it yourself, though.

If we're sticking with the Order/LineItem example from the demo app, you could create a NestedLineItemField that took care of the nested form logic. That would look something like this:

rails generate adminsitrate:field NestedLineItem

This would create:

app/fields/nested_line_item_field.rb
app/views/fields/nested_line_item_field/_show.html.erb
app/views/fields/nested_line_item_field/_index.html.erb
app/views/fields/nested_line_item_field/_form.html.erb

You can inherit from Administrate::Field::HasMany as a starting point for the behavior. This might be a good direction to start in:

diff --git a/spec/example_app/app/fields/nested_line_item_field.rb b/spec/example_app/app/fields/nested_line_item_field.rb
index e69de29..73e2d7d 100644
--- a/spec/example_app/app/fields/nested_line_item_field.rb
+++ b/spec/example_app/app/fields/nested_line_item_field.rb
@@ -0,0 +1,4 @@
+require "administrate/fields/base"
+
+class NestedLineItemField < Administrate::Field::HasMany
+end
diff --git a/spec/example_app/app/views/fields/nested_line_item_field/_form.html.erb b/spec/example_app/app/views/fields/nested_line_item_field/_form.html.erb
index e69de29..78ae901 100644
--- a/spec/example_app/app/views/fields/nested_line_item_field/_form.html.erb
+++ b/spec/example_app/app/views/fields/nested_line_item_field/_form.html.erb
@@ -0,0 +1,7 @@
+<%= f.label field.attribute_key %>
+
+<%= f.fields_for field.attribute_key do |nested_form| %>
+  <fieldset>
+    <%= nested_form.text_field :nested_attribute %>
+  </fieldset>
+<% end %>
diff --git a/spec/example_app/app/views/fields/nested_line_item_field/_index.html.erb b/spec/example_app/app/views/fields/nested_line_item_field/_index.html.erb
index e69de29..758e0c5 100644
--- a/spec/example_app/app/views/fields/nested_line_item_field/_index.html.erb
+++ b/spec/example_app/app/views/fields/nested_line_item_field/_index.html.erb
@@ -0,0 +1 @@
+<%= pluralize(field.data.count, field.attribute.to_s.humanize.downcase) %>
diff --git a/spec/example_app/app/views/fields/nested_line_item_field/_show.html.erb b/spec/example_app/app/views/fields/nested_line_item_field/_show.html.erb
index e69de29..1166a16 100644
--- a/spec/example_app/app/views/fields/nested_line_item_field/_show.html.erb
+++ b/spec/example_app/app/views/fields/nested_line_item_field/_show.html.erb
@@ -0,0 +1,20 @@
+<% if field.resources.any? %>
+  <%= render(
+    "collection",
+    collection_presenter: field.associated_collection,
+    resources: field.resources
+  ) %>
+
+  <% if field.more_than_limit? %>
+    <span>
+      <%= t(
+        'administrate.fields.has_many.more',
+        count: field.limit,
+        total_count: field.data.count,
+      ) %>
+    </span>
+  <% end %>
+
+<% else %>
+  <%= t("administrate.fields.has_many.none") %>
+<% end %>

And here are some places in the codebase you might want to use for reference:

Right now we're focusing on stability, so it might be a while before we get to this. If you can get it working well for your project, let us know how you did it - that's probably the best way to push this forward.

Let me know if you have any questions, or if I can help out at all!

@ismaGNU
Copy link
Author

ismaGNU commented Nov 9, 2015

Thanks @Graysonwright, that was my first approach, building LineItemField and inherits from HasMany one. The problem is that I want to have a button to add as line items as I want. So I am stuck at this part.

Anyway, I really appreciate your help and I´ll let you know my progress 💃

@5minpause
Copy link
Contributor

@ismaGNU Do you have some sample code on Github for your implementation of nested form fields?

@ismaGNU
Copy link
Author

ismaGNU commented Nov 11, 2015

@5minpause no man, I am not be able to make it works like I expected. Do you have any ideas? I just make what I told before, but It doesn´t work.

@mcmire
Copy link

mcmire commented Nov 11, 2015

Not sure if this is exactly what the OP had in mind, but I've been able to incorporate Cocoon into Administrate, but with some hacks. I'll swing back around and work on a way to create a PR (or several) out of this work, because I think this is pretty important to have.

@c-lliope
Copy link
Contributor

@mcmire - any updates?

@mcmire
Copy link

mcmire commented Nov 23, 2015

@Graysonwright Not yet unfortunately, I haven't had much time lately.

@airjoshb
Copy link

So I have been able to incorporate Cocoon... almost, but it is getting stuck saying that the attributes are not permitted. How do I allow the attributes, when they are already in the controller?

app/fields/nested_answer_field.rb

 require "administrate/field/has_many"

    class NestedAnswerField < Administrate::Field::HasMany
      def to_s
        data
      end
    end 

app/controllers/questions_controller.rb (verified this works in normal rails scaffold edit)

    def question_params
      params.require(:question).permit( :question, :note, :kind, :has_sub, :parent_id, :answer_ids, answers_attributes: [:id, :answer, :question_id, :_destroy])
    end

Here is a screen shot showing it working after adding from normal edit page. All the js works, but get this in my log "Unpermitted parameter: answers_attributes", which are clearly allowed
screen shot 2016-03-15 at 3 55 58 pm

@nburkley
Copy link
Contributor

@airjoshb I've added nested params by overriding the resource_params method

module Admin
  class AuthorsController < Admin::ApplicationController
    def resource_params
      params.require(:author).permit(
        permitted_attributes,
        social_links_attributes: [
          :id,
          :value,
          :title
        ]
      )
    end
  end
end

Would love to see nested forms as part of Administrate.

@airjoshb
Copy link

@nburkley Your answer got me going and I devised a slightly less "invasive" approach. In the Dashboard for the model in question, you can define permitted_attributes. I now have cocoon working flawlessly! Here is a peek at my formatting

def permitted_attributes
 [ :question, :note, :kind, :has_sub, :parent_id, answers_attributes: [:id, :answer, :question_id, :_destroy]]
end

@c-lliope
Copy link
Contributor

@airjoshb that's a great approach! I'll take a crack at officially supporting & documenting that.

@c-lliope
Copy link
Contributor

@nburkley and @airjoshb - can you post the .html.erb files that you generated for the nested fields? Thanks!

@c-lliope
Copy link
Contributor

ok, I managed to recreate this in one of my own apps. I think there are some things we can do to make this process more automated, with less user input. I'm hopeful that we can build out some kind of Administrate::Field::NestedHasMany class that handles all of the permitted attribute gotchas, and intelligently creates form templates based on a list of attributes.

@c-lliope c-lliope self-assigned this Apr 11, 2016
@airjoshb
Copy link

@Graysonwright Here are my files:
fields/nested_answer_field/_form.html.erb

<div class="field-unit__label">
  <%= f.label field.attribute %>
</div>
<div id="answers">
  <%= f.fields_for field.attribute do |a| %>
    <%= render 'answer_fields', f: a %>
  <% end %>
  <div class="links">
    <%= link_to_add_association 'add answer', f, field.attribute %>
  </div>
</div>

and admin/application/_answer_fields.html.erb

<div class="nested-fields">
  <div class="field">
    <%= f.label :answer %>
    <%= f.text_field :answer, autofocus: true %>
  </div>
  <%= link_to_remove_association "remove answer", f %>
</div>

@c-lliope
Copy link
Contributor

@airjoshb thanks!

@c-lliope c-lliope added the ready label Apr 11, 2016
@nburkley
Copy link
Contributor

@Graysonwright I haven't added nested forms yet, just added an MVP with allowed editing some child values that we build on create. Looks like @airjoshb has you covered anyway.

c-lliope added a commit that referenced this issue Apr 15, 2016
Closes #192

Feature:

When I create or edit a model that has_many nested models,
I want to view and edit the attributes of the nested models
so I can set up all the relationships with a single form.

Implementation:

Introduce a new field type, `NestedHasMany`.

I considered building the feature into the existing `HasMany` field,
but this would get in the way of `HasMany` relationships
that aren't directly nested, such as in many-to-many relationships.

The `NestedHasMany` field builds off of the [Cocoon] gem.
It renders fields from the nested model,
based on the fields defined in the nested model's dashboard class.

Cocoon provides helpers and javascript
to easily add and remove nested form fields from the page.

[Cocoon]: https://github.com/nathanvda/cocoon
@c-lliope
Copy link
Contributor

In #476, we decided on a plugin structure for new field types. Nested has_many forms are a good candidate for a plugin, so I extracted this code out to https://github.com/graysonwright/administrate-field-nested_has_many.

It's rough but functional. PRs are welcome on that repository. If we get it polished enough and it turns out to be useful enough, we can absorb it into this core repository.

@nburkley @airjoshb thanks for your help!

@c-lliope c-lliope removed the ready label Apr 22, 2016
@nburkley
Copy link
Contributor

nice, thanks @Graysonwright!

@airjoshb
Copy link

very cool. Thanks! @Graysonwright

@cde
Copy link

cde commented May 5, 2016

nice! @Graysonwright, can this be used with paperclip as well? I'm trying to do nested pictures for products.
Btw, thanks everyone for your inputs and comments. It's helped to solve many issues I run to when implementing administrate.

@cde
Copy link

cde commented May 5, 2016

Also, I think this line #7 require "administrate/field/image" needs to be removed. It looks like image has been moved to a plugin. thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants