Rails - dynamically build nested object inside nested object partials (Cocoon) - ruby-on-rails

Currently, I'm building a Medium-like blog service which users can add article parts(text, image, youtube...etc) that composite the article body.
Article model has many ArticlePart model and to create nested form dynamically, I'm using cocoon gem.
Here's my current code.
_form.html.erb
<%= link_to_add_association("Add New Article Part", f, :article_parts,{:data => {"association-insertion-node" => "#article" }}) %>
<ul class="sortable">
<div id="article">
<%= f.fields_for :article_parts %>
</div>
</ul>
_article_part_fields.html.erb
<li class="nested-fields">
<h2>Text Part</h2>
<%= f.hidden_field :position %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= link_to_remove_association("Delete part", f) %>
<h2>Insert New Article Part Here</h2>
</li>
Above works fine but what I want to do is to display "link_to_add_association" where "Insert New Article Part Here" blocks in _article_part_fields.html.erb so that users can add new article part anywhere they want to.
I tried to do it as below by passing parent object form to nested object form, but this code causes stack level too deep error.
<li class="nested-fields">
<h2>Text Parts</h2>
<%= f.hidden_field :position, :class => "position" %>
<%= f.label :title %>
<%= f.text_field :title, :class => "fld-name required" %>
<%= link_to_remove_association("Delete part", f) %>
<h2>Insert New Part Here</h2>
<%= link_to_add_association("商品", parent_form, :article_parts,{:render_options => {:locals => {:parent_form => parent_form}}, :data => {"association-insertion-node" => "#article" }}, :class => 'btn btn-ctrl btn-lg') %>
</li>
Any help appreciated to get this working, or suggest another way to achieve this.
Thanks in advance!

Related

Nested Elements within Simple Form

I am trying to use Simple Form with my Rails 4 app.
I have models called projects.rb and programs.rb. Each of those 2 models has some common components. I handle these with another model called scope.rb. Each of projects and programs has a scope.
Within scopes, I have models called data.rb and materials.rb.
In my projects view, I ask several threshold questions. One of them is do you want data? (the answers true or false) The answer to that question is stored in my scope model. If the answer is true, then I want to ask further questions about the data (the answers are stored in my data model).
So, the relationships are that each of programs & projects has one scope. Each of data and materials belongs to scope.
For the moment, all of those data model questions are set out in my projects view.
The projects view when everything is set out in the projects view is as follows:
This is the scope question to find out whether further detail is required about data or materials:
<div class="col-md-3 col-md-offset-1">
<%= f.label 'What sort resources are involved in this project?', :class => 'question-project' %>
</div>
<div class="col-md-7">
<div class="response-project" style="margin-left:5%">
<%= f.simple_fields_for :scope do |s_all| %>
<%= s_all.input :if_datum, :as => :boolean, :label => false, inline_label: 'Data' %>
<%= s_all.input :if_material, :as => :boolean, :label => false, inline_label: 'Equipment or materials' %>
<% end %>
</div>
</div>
</div>
If the answer to data is true, then I ask:
<div class="col-md-3 col-md-offset-1">
</div>
<div class="col-md-7">
<%= f.simple_fields_for :scope do |data_s| %>
<%= data_s.simple_fields_for :datum do |d| %>
<%= d.label 'Is this data confidential or commercially sensitive?', :class => 'response-project' %> <br>
<%= d.collection_radio_buttons :confidential, [[true, ' Yes '] ,[false, ' No']], :first, :last, {:item_wrapper_class => 'inline'}, {:class => "radio-inline response-project" } %>
<%= d.input :prim_sec, label: 'What sort of data do you want?', label_html: {class: 'response-project'}, collection: ["Primary", "Secondary", "Both" ], prompt: "Choose one", item_wrapper_class: 'inline' %>
<% end %>
<% end %>
</div>
</div>
</div>
This all works fine, except that i want to use the same forms in my programs model. That's why I'm trying to extract them into partial views in my data model so that I can use them in both the projects and programs models.
When I start with the data/_form view, with this extracted:
<div class="row">
<div class="col-md-3 col-md-offset-1">
</div>
<div class="col-md-7">
<%= f.simple_fields_for :scope do |data_s| %>
<%= data_s.simple_fields_for :datum do |d| %>
<%= d.label 'Is this data confidential or commercially sensitive?', :class => 'response-project' %> <br>
<%= d.collection_radio_buttons :confidential, [[true, ' Yes '] ,[false, ' No']], :first, :last, {:item_wrapper_class => 'inline'}, {:class => "radio-inline response-project" } %>
<%= d.input :prim_sec, label: 'What sort of data do you want?', label_html: {class: 'response-project'}, collection: ["Primary", "Secondary", "Both" ], prompt: "Choose one", item_wrapper_class: 'inline' %>
<% end %>
<% end %>
</div>
</div>
</div>
And replace that code in my project view to render "data/form".
My problem is that when I move the data code to a data/_form to ask the data specific questions, I get the errors set out below. My view, when I try to do this is:
undefined local variable or method `f' for #<#:0x00000107ff3a90>
This is a reference to the line in the data/_form view that says:
<%= f.simple_fields_for :scope do |data_s| %>
Does anyone know what I've done wrong?
Thank you
I assume that you are doing something like this:
<%= simple_form_for project do |f| %>
<%= render 'data/form' %>
<% end %>
When rendering a partial, local variables are not passed down. So if you need to access f within your data/_form partial, you need to provide your partial with a corresponding local variable. Try this:
<%= simple_form_for project do |f| %>
<%= render 'data/form', f: f %>
<% end %>
Or, in a more verbose fashion:
<%= simple_form_for project do |f| %>
<%= render partial: 'data/form', locals: {f: f} %>
<% end %>
HTH!

Rails 4 - Pre-Populate Form From Link

I have a form that allows users to manually input movies to review. Additionally, I am pulling movies from an external API (TMDB). When the user searches, it shows all results from the database as well as another area where data is pulled from the API. I want them to be able to click on a thumbnail from the external API and pre-populate my "new" form with that data. This code currently goes to the "new" form, but the title and description are not populated like I want them to be (a blank new form is rendered).
<% if #movies_api %>
<div class="row">
<% #movies_api.each do |movie| %>
<% if movie.poster_path %>
<div class="col-sm-6 col-md-3">
<div class="thumbnail">
<%= link_to(image_tag('https://image.tmdb.org/t/p/w396' + movie.poster_path, class: 'image', size:"400x600"), {:action => 'new', :controller => 'movies'}, :title => movie.title, :description => movie.overview ) if movie.poster_path %>
</div>
</div>
<% end %>
<% end %>
<% end %>
How do I accomplish pre-populating the new form with data I gather from the API?
I figured out a way to do this. In my form, I can do:
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title, :value => params[:title] %>
</div>
Not sure if there is a better way, but this works :-)

Rails 4 & cocoon gem: Cannot add 3rd level child to dynamically added 2nd level child

I am currently using the cocoon gem to handle my nested form along with bootstrap/simple_form. My models are laid out like this:
A Contact has_many goals
A Goal has_many tasks
A Task has_one reminder (not yet implemented, this is next once I resolve this issue)
My form is laid out with the Goal fields being built correctly, and I can add/remove tasks to my first Goal without a problem. However, when I dynamically add another Goal (regardless if it's in the new or edit action), I cannot add/remove tasks to it (if I click 'add task', it only gets added to my first Goal). Below are my forms:
_form.html.erb
<%= simple_form_for(#contact) do |f| %>
<% if #contact.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#contact.errors.count, "error") %> prohibited this contact from being saved:</h2>
<ul>
<% #contact.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.input :name %>
<%= f.input :title %>
<%= f.input :company %>
<%= f.input :email %>
<%= f.input :notes %>
<h3>Goals:</h3>
<div id="goals">
<%= f.simple_fields_for(:goals) do |goal| %>
<%= render 'goal_fields', :f => goal %>
<div class="links">
<%= link_to_add_association 'add goal', f, :goals, :render_options => {:wrapper => 'bootstrap' } %>
</div>
<% end %>
</div>
<%= f.submit :submit %>
<% end %>
_goal_fields.html.erb
<div class="nested-fields">
<%= f.input :title %>
<%= f.input :due_date %>
<%= f.input :notes %>
<%= link_to_remove_association "remove goal", f %>
<h4>Tasks:</h4>
<div id="tasks">
<%= f.simple_fields_for(:tasks) do |task| %>
<%= render 'task_fields', :f => task %>
<div class="links">
<%= link_to_add_association 'add task', f, :tasks, :render_options => {:wrapper => 'bootstrap' } %>
</div>
<% end %>
</div>
</div>
task_fields.html.erb
<div class="nested-fields">
<%= f.input :task_type %>
<%= f.input :date_of_task %>
<%= f.input :complete, as: :boolean, checked_value: true, unchecked_value: false %>
<%= link_to_remove_association "remove task", f %>
</div>
I have gone through my code to make sure that I am using proper div classes and ids, but I'm sure there is a problem with my form somewhere. I also added the necessary wrapper option for simple_form/bootstrap. Thank you in advance for taking the time to helping me out.
This sounds like a html/javascript problem. I just had a similar issue posted on cocoon issues, so it sounds like using an id instead of a class for a repeating thing.
Not sure if that causes the problem, but I see you have a <div id='tasks'>.
Although the behaviour I would expect in that case, would be that in the form it apparently works, but it will only get saved in the first one. Not exactly what you describe.
Secondly: is it intentional to have a "add task" or "add goal" for each child?
I would write those loops as follows:
<%= f.simple_fields_for(:tasks) do |task| %>
<%= render 'task_fields', :f => task %>
<% end %>
<div class="links">
<%= link_to_add_association 'add task', f, :tasks, :render_options => {:wrapper => 'bootstrap' } %>
<% end %>
[rant]
Should I try to promote haml again? I see people making so many errors when using erb, it just gets messy (your indentation is very confusing), I sometimes honestly do not understand why more people use haml or slim. I find it makes my views way more readable.
[/rant]

Form not updating values in the database if the class of a framework is used

I m using the ink framework for styling my web application. And for styling form i am using this http://ink.sapo.pt/index.php/forms.
<form class= "ink-form inline">
<%= form_for(#document, :remote => true) do |f| %>
<fieldset>
<div class="control required validation error">
<%= f.label :title, :class => "short" %>
<%= f.text_field :title, :class => "short" %>
</div>
</fieldset>
<% end %>
</form>
In the above form ive put the class= ink-form inline to style the form. Doing this styles my form but it does nt update the values in the database. When i use this code
<%= form_for(#document, :remote => true, :class => "ink-form inline") do |f| %>
then it updates the values in the database but it doesnt style the form. I am not getting where the problem is. Please help.!!
Try this!
<%= form_for(#document, :remote => true, :html => {:class => "ink-form inline"}) do |f| %>
<fieldset>
<div class="control required validation error">
<%= f.label :title, :class => "short" %>
<%= f.text_field :title, :class => "short" %>
</div>
</fieldset>
<% end %>
If you had inspected your earlier code in the browser, you would see two form tags (one within the other). The reason is form_for generates its own form and that is where you would want to provide the options.

Work after user creation

I create a new user with basic fields as user name and password. Then I redirect to another page where the user is allowed to complete his profile. The page looks like this.
Ok, I created the user first. Then I redirected the user to complete his profile. The profile page looks like this
<h1>User successfully created</h1>
<h3>Would you like to complete your profile now?</h3>
<%= render #profile %>
<%= render #address %>
<%= button_to 'Save',{
:controller => "users",
:action => "update",
:id => #user.id,
:profile => #profile,
:address => #address
}, :method => :put %>
The code above executes the update action in the controller. However, I am getting nil for the #profile and #address objects. I guess the button_to is having no effect on the profile and address partials.
The address partial looks as follows
<%= form_for(#address) do |f| %>
<% if #address.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#address.errors.count, "error") %> prohibited this address from being saved:</h2>
<ul>
<% #address.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<fieldset>
<legend>Addresss</legend>
<div class="field">
<%= f.label :street_address %>
<%= f.text_field :street_address, :size => 40 %>
</div>
<div class="field">
<%= f.label :street_address_2 %>
<%= f.text_field :street_address_2, :size => 40 %>
</div>
<div class="field">
<%= f.label :city %>
<%= f.text_field :city, :size => 40 %>
</div>
<div class="field">
<%= f.label :state %>
<%= f.text_field :state, :size => 40 %>
</div>
<div class="field">
<%= f.label :zip_code %>
<%= f.text_field :zip_code, :size => 40 %>
</div>
<% end %>
I would appreciate if anyone would be able to help me on this.
The button_to method only creates a button which an be used to call an controller/ go to another page.
If you want to allow the user to modify fields, you need to use a form. Please read this guide on forms for more information, especially part 2 is of interest for your situation I guess.
Edit: You need a submit button in your form to submit it, not a button_to outside the form. If you want to have both partials submitted at the same time, you need 1 big form and a controller which is able to process the data of both models.

Resources