3

I have 3 models - team, user and team_user (has_namy: through). In the form of edit and new team to do the ability to dynamically add members of this team with autocomplete input.

app/models/user.rb

class User < ApplicationRecord
  has_many :team_users
  has_many :teams, through: :team_users
  accepts_nested_attributes_for :team_users, :teams, allow_destroy: true
end

app/models/team.rb

class Team < ApplicationRecord
  has_many :team_users
  has_many :users, through: :team_users
  accepts_nested_attributes_for :team_users, allow_destroy: true, reject_if: proc { |a| a['user_id'].blank? }
end

app/models/team_user.rb

class TeamUser < ApplicationRecord
  belongs_to :team
  belongs_to :user
  accepts_nested_attributes_for :team, :user, allow_destroy: true
end

I need something like this:

enter image description here

The problems that I have:

  • Cocoon Gem generate only empty fields or that I will create when the template is rendered. And I need to find a user and add him to the team (in a non-editable field)
  • If I use AJAX, the @team = Team.new runs only in a new action in TeamsController and Rails doesn't remember created @team, when AJAX is trying to create team_user object. To create a object of team_user model need to run @team.team_users.build(user_id: params[:user_id]). The user_id I get from autocomplete field, but don't know @team and team_id (for example, in new action TeamsController).
Nikolay Lipovtsev
  • 657
  • 1
  • 6
  • 15
  • Have you seen http://railscasts.com/episodes/102-auto-complete-association-revised for auto-completion? – James L. Aug 16 '17 at 22:23

3 Answers3

1

1.

In this example application, rail263 example application, I use cocoon, simpleform, and select2 to create a searchable dropdown box to search a list of names to add passengers to a rental record.

I believe this is the use case you want.

The partial that does this is here: Partial for passenger lookup

It uses cocoon callbacks to cause the select2 lookup to run for each new passenger.

In the rental record model, associations are:

 belongs_to :customer
 belongs_to :vehicle

  # delete associated records..  
 has_many :passengers, dependent: :destroy
 has_many :pasenger_lists, :through => :passengers, :class_name =>    'PasengerList'

 accepts_nested_attributes_for :pasenger_lists
 accepts_nested_attributes_for :passengers,  allow_destroy: true

The callbacks that activate the select2 dropdown are here: callback

  $('#passengers')
    .on('cocoon:after-insert', function() {
      $(".dgselect2").select2({
    });

In your code, have you white listed the needed items in the strong paramaters? See here: controller

params.require(:rental_record).permit(
  :customer_id, :vehicle_id, :start_date, :end_date, :lastUpdated,
  passengers_attributes: [:id, :description, :name, :output,     :pasenger_list_id, :_destroy , pasenger_lists_attributes: [:id, :clocknum, :name, :active, :_destroy]]
  )        

2.

I have also done the same idea in this app: training app

It's just the rail263 app with different models. It's along the same lines. I use rail263 to develop the idea to be used in the training app.

You can try out the training app here: demo
- Log in with user = reg and password = a2a2
- click the add tr_training_employee button to see it working.

enter image description here


3.

If this idea is of interest to you, I can work with you to edit this post to include a more complete answer and explain how it works for anyone else who may read this thread later. Let me know.


Related: cocoon issue

David Gleba
  • 517
  • 1
  • 5
  • 21
  • In this example, each time a new field is added select input for new users. The app suggests a lot of users, so it is not logical to introduce all of them using select input. Unfortunately, this is not quite what I need. In my application need one search box in the main form, through which the users search. Once in the field with autocomplete, the user is found, you have to press enter or select it from the drop-down list. Then, this user should appear in the list of users in the team **(without the choice as the picture in question)**. – Nikolay Lipovtsev Aug 15 '17 at 02:55
  • I think I need AJAX, but I don't know how to save `@team` in a `new` action in `TeamsController` to build `team_user` `@team.team_users.build(user_id: params[:user_id])` in another AJAX. – Nikolay Lipovtsev Aug 15 '17 at 03:05
  • OK, now I see the difference. I hope someone else will answer to help you with that. – David Gleba Aug 15 '17 at 11:00
1

I solved a very similar problem using cocoon for nested attributes and AngularJS with jQuery (Angular UI Typeahead). There are some important points:

Use Angular/jQuery to update data-association-insertion-template and update related data() every time something is selected. Due to browser's caching nature of data elements, changes in this attribute don't reflect automatically. So I bound on click event of the button:

$(this).data('association-insertion-template', $(this).attr('data-association-insertion-template'));

Write your nested fields template for cocoon accordingly so you can populate it with selected values. As I used Angular, mine was like (Slim template syntax):

tr.nested-fields
  td
    = f.object.persisted? ? f.object.full_name : '{{angularModel.full_name}}'

That frontend prototype was working almost fine, but on every parent object's update all existing nested fields doubled. So I overrode my nested attributes method in the parent model (see this thread):

def items_attributes=(attributes)
  self.items = attributes.values.map { |item| Item.find(item['id']) }
  super(attributes)
end

The last issue was that I was able to add nested record many times. To restrict this behaviour I just drop all already used ids in typeahead AJAX queries.

Hope this helps.

Alex Svetkin
  • 1,339
  • 14
  • 17
0

If you have similar projects you can solve this problem with this gem dynamic_nested_forms

Nikolay Lipovtsev
  • 657
  • 1
  • 6
  • 15