How to Create Nested Model from Partial Rails 6

How to create nested model from partial Rails 6

When building an associated record over a has_one relation, instead of

@review = entry.review.build(review_params)

you need to use the following:

@review = entry.build_review(review_params)

See the documentation for more details.

Am I suppose to implement my actions in the entries_controller?

It depends on what you're after. If you have a dedicated form for adding a new review and it is not embedded in another form for creating or updating an entry then implementing a create action in ReviewsController is the straightforward solution – in this case you should also not need accepts_nested_attributes_for in Entry.

If, however, you want to be able to create or update an entry as well as its review using the same form, then you should nest the review form in the form of the entry, keep accepts_nested_attributes_for, and use actions in EntriesController. The documentation should get you started there.

How to DRY a Rails 6 partial form

To create a form that routes to a nested route you pass an array:

# app/views/jobs/_form.html.erb
<%= form_with(model: [user, job]) %>
# ...
<% end %>
# app/views/jobs/new.html.erb
<%= render 'form' user: current_user, job: @job %>
# app/views/jobs/edit.html.erb
<%= render 'form' user: current_user, job: @job %>

But its kind of questionable why you nested the route in the first place as you're not using it as a nested resource at all. GET /users/:user_id/jobs should really show only the jobs belonging to that user and POST /users/:user_id/jobs should create a job belonging to that user and not the current user.

Use a single form_with to create and edit nested resources in Rails

You can pass an array to the form to handle both nested and "shallow" routes:

<%= form_with(model: [@series, @book], local: true) do |form| %>

<% end %>

Rails compacts the array (removes nil values) so if @series is nil the form will fall back to book_url(@book) or books_url. However you need to handle setting @series and @book properly from the controller.

class SeriesController < ApplicationController
def show
@series = Series.find(params[:id])
@book = @series.books.new # used by the form
end
end

You could instead handle this in your views by using local variables:

# app/views/books/_form.html.erb
<%= form_with(model: model, local: true) do |form| %>

<% end %>

# app/views/books/edit.html.erb
<%= render 'form', locals: { model: [@series, @book] } %>

# app/views/series/show.html.erb
<%= render 'books/form', locals: { model: [@series, @series.book.new] } %>

You can also use the shallow: true option in your routes to avoid nesting the member routes (show, edit, update, destroy):

resources :series do
resources :books, shallow: true
end

This will let you just do:

# app/views/books/edit.html.erb
<%= render 'form', model: @book %>

# app/views/books/_book.html.erb
<%= link_to 'Edit', edit_book_path(book) %>

Using a partial from another view while that model's routes are nested in the current view Rails 3

I was able to get this resolved by changing:
<%= form_for @personal_info, url: user_personal_info_index_path, html: { method: :post } do |f| %>

to:

<%= form_for @personal_info, url: new_user_personal_info_index_path, html: { method: :post } do |f| %>

form_with using a nested partial renders the form contents outside of the form/form elements tables, rendering content into a table row

Fixed!

My problem was that I was rendering some content into a table row <tr> content that was not wrapped inside of a <td>

never render content into a <tr> without wrapping that content inside of a <td>

this malformed content was breaking the form, making the contents come outside of the form as explained above

How to update @model.submodel in rails 6

I think I found the problem after @yzalavin suggestion. Somehow validations were preventing the update. I do not know the association yet.

Simply adding on: :create allowed me o update.

validates_presence_of :entry_id, :user_id, :verified, on: :create

Render partial from another model

Very strange — if you write <%= render :partial => 'room_form' %> than rails will assume that it is app/views/calculator/_room_form.html.erb, but in case of <%= render :partial => 'rooms/room_form' %> it will assume that it is app/views/rooms/_room_form.html.erb

Watch your log — there you will see which partials were rendered

NoMethodError within nested model form

Having implemented the functionality you seek, I'll give some ideas:


Accepts Nested Attributes For

As you already know, you can pass attributes from a parent to nested model by using the accepts_nested_attributes_for function

Although relatively simple, it's got a learning curve. So I'll explain how to use it here:

#app/models/plan.rb
Class Plan < ActiveRecord::Base
has_many :exercises
accepts_nested_attributes_for :exercises, allow_destroy: true
end

This gives the plan model the "command" to send through any extra data, if presented correctly

To send the data correctly (in Rails 4), there are several important steps:

1. Build the ActiveRecord Object
2. Use `f.fields_for` To Display The Nested Fields
3. Handle The Data With Strong Params

Build The ActiveRecord Object

#app/controllers/plans_controller.rb
def new
@plan = Plan.new
@plan.exericses.build
end

Use f.fields_for To Display Nested Fields

#app/views/plans/new.html.erb
<%= form_for @plans do |f| %>
<%= f.fields_for :exercises do |builder| %>
<%= builder.text_field :example_field %>
<% end %>
<% end %>

Handle The Data With Strong Params

#app/controllers/plans_controller.rb
def create
@plan = Plan.new(plans_params)
@plan.save
end

private
def plans_params
params.require(:plan).permit(:fields, exerices_attributes: [:extra_fields])
end

This should pass the required data to your nested model. Without this, you'll not pass the data, and your nested forms won't work at all


Appending Extra Fields

Appending extra fields is the tricky part

The problem is that generating new f.fields_for objects on the fly is only possible within a form object (which only exists in an instance)

Ryan Bates gets around this by sending the current form object through to a helper, but this causes a problem because the helper then appends the entire source code for the new field into a links' on click event (inefficient)


We found this tutorial more apt

It works like this:

  1. Create 2 partials: f.fields_for & form partial (for ajax)
  2. Create new route (ajax endpoint)
  3. Create new controller action (to add extra field)
  4. Create JS to add extra field

Create 2 Partials

#app/views/plans/add_exercise.html.erb
<%= form_for @plan, :url => plans_path, :authenticity_token => false do |f| %>
<%= render :partial => "plans/exercises_fields", locals: {f: f, child_index: Time.now.to_i} %>
<% end %>

#app/views/plans/_exercise_fields.html.erb
<%= f.fields_for :exercises, :child_index => child_index do |builder| %>
<%= builder.text_field :example %>
<% end %>

Create New Route

   #config/routes.rb
resources :plans do
collection do
get :add_exercise
end
end

Create Controller Action

#app/controllers/plans_controller.rb
def add_exercise
@plan = Plan.new
@plan.exercises.build
render "add_exericse", :layout => false
end

Create JS to Add The Extra Field

#app/assets/javascripts/plans.js.coffee
$ ->
$(document).on "click", "#add_exercise", (e) ->
e.preventDefault();

#Ajax
$.ajax
url: '/messages/add_exercise'
success: (data) ->
el_to_add = $(data).html()
$('#exercises').append(el_to_add)
error: (data) ->
alert "Sorry, There Was An Error!"

Apologies for the mammoth post, but it should work & help show you more info



Related Topics



Leave a reply



Submit