Summernote editor not working with nested forms - javascript

I'm trying to use cocoon-gem and summernote-gem in my ruby-on-rails app.
This is my model:
class Dog < ApplicationRecord
has_many :pictures, as: :imageable, dependent: :destroy
accepts_nested_attributes_for :pictures, reject_if: :all_blank, allow_destroy: true
end
At first look new or edit page seems alright. First summary field render with summernote correctly. And if I already have several pictures saved to my dog model, they all look perfectly fine on editing page.
But when I click on "add picture" it creates new picture field with summary field not rendered properly with summernote. In html page it looks like this:
<div class="field">
<label class="string optional" for="dog_pictures_attributes_1544609860934_summary">Summary</label>
<textarea data-provider="summernote" name="dog[pictures_attributes][1544609860934][summary]" id="dog_pictures_attributes_1544609860934_summary"></textarea>
</div>
Instead of this:
<div class="field">
<label class="string optional" for="dog_pictures_attributes_1_summary">Summary</label>
<textarea data-provider="summernote" name="dog[pictures_attributes][1][summary]" id="dog_pictures_attributes_1_summary" style="display: none;"></textarea>
<div class="note-editor note-frame">...</div>
</div>
This is my _form.erb: (if relevant)
<%= simple_form_for #dog, defaults: { required: false } do |f| %>
<div class="field">
<%= f.label 'Note' %>
<%= f.text_area :note, 'data-provider': :summernote %>
</div>
<h2> Add pictures </h2>
<%= f.fields_for :pictures do |picture| %>
<%= render 'picture_fields', :f => picture %>
<% end %>
<div class='links'>
<%= link_to_add_association 'add picture', f, :pictures %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And this is my _picture_fields.erb:
<div class='nested-fields'>
<div class="field">
<%= f.file_field :image %>
</div>
<div class="field">
<%= f.label :author %>
<%= f.text_field :author %>
</div>
<div class="field">
<%= f.label :summary %>
<%= f.text_area :summary, 'data-provider': :summernote %>
</div>
<%= link_to_remove_association "remove picture", f %>
</div>
I also tried to build nested form from scratch with drifting ruby. But it cause the same problem

When dynamically inserting items into the page, you will have to initialize the summernote editor on those items as well. Cocoon has callbacks to allow you to do this.
When loading the page, to initialize summernote, you would do something like
$('[data-provider="summernote"]').each ->
$(this).summernote
(copied from the summernote-rails gem documentation)
But this only works for the "summernote"'s already on the page, it cannot initialize notes that are not yet there.
So, after inserting a nested-items we have to use the same code to initialise the inserted notes. Something like this should work:
$('form').on('cocoon:after-insert', function(e, insertedItem) {
insertedItem.find('[data-provider="summernote"]').each(function() {
$(this).summernote();
})
});
This will look for summernote items in the freshly nested-item and initialise those.
[EDIT: improve javascript to extract it from view]
Javascript files are generally grouped in app/assets/javascripts, if you are editing Dogs let's add a new file dogs.js and enter the following code:
$(document).on('cocoon:after-insert', 'form', function(e, insertedItem) {
insertedItem.find('[data-provider="summernote"]').each(function() {
$(this).summernote();
})
});
Then add this file to your application.js using require dogs.
This code registers an event handler on the document, it will listen to any cocoon:after-insert event triggered on a form. If you have multiple forms using cocoon, you might need a better/more precise css selector (e.g. add a class to the form and add that as well).

Related

Unresponsive UI elements become responsive after resize

I'm experiencing a weird UI bug where when I click on an element, can be a drop-down list or button, it is unresponsive. If I then resize the screen and click the element again, it is responsive. I haven't found a good explanation for why or how this is occurring. And it doesn't affect all elements on the relevant screen.
I have a front end using Bourbon SASS on a rails application. On some pages, I have jquery javascript, but on others, it is just HTML and CSS. The browser can be chrome or explorer.
I'd appreciate any insight into this issue. Let me know if I need to provide more information.
Here is the partial of the form that is affected. The second drop down element is unresponsive unless I maximize the screen.
<div class="send-invite-container">
<%= form_for :send_invite, url: admin_send_invite_url do |f| %>
<div class="field">
<%= f.label "First Name" %>
<%= f.text_field :first_name %>
</div>
<div class="field">
<%= f.label "Last Name" %>
<%= f.text_field :last_name %>
</div>
<div class="field">
<%= f.label "Email" %>
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label "Client" %>
<%=f.collection_select(:requested_organization, #client_collection, :name, :name)%>
</div>
<div class="field">
<%= f.label "Associated Company" %>
<%=f.select(:parent_role, options_for_select([["Company 1", "company_1"], ["Company 2", "company_2"], ["Other", "other"]]))%>
</div>
<div class="field">
<%= f.label "Roles" %>
<%= f.select(:requested_roles, Role::ROLES, {},
{ :multiple => true, :size => 6 }
) %>
</div>
<%= button_tag(type: "submit", class: "btn btn-success") do %>
<i class="icon-leaf"></i> Send Invite
<% end %>
<%end%>
</div>
Figured it out. There was a div element that was floating over the select box. The element was defined in the layout html file.

How I can render new field in jquery/rails?

I have a problem with rendering an element in jquery/rails.
I would like to do something like this:
When a user clicks on the button "+", another field with select ingredient should be rendered below the default field (because a drink can have a lot of ingredients, and I don't want to specify the amount).
I can't figure it out, so I have to ask for help.
views/drinks/new.html.erb
<%= form_for #drink do |f| %>
<div class="form-group">
<%= f.label :name %><br />
<%= f.text_field :name, class:'form-control' %><br />
</div>
<div class="form-group">
<%= f.label :category %><br />
<%= f.select :category_id, Category.all.collect {|x| [x.name, x.id]}, {:include_blank => 'Select One'}, class:'form-control' %><br /><br />
</div>
<div class="form-group", id: "add-ingredients">
<%= f.label :ingredient %><br />
<%= f.select :ingredient_id, Ingredient.all.collect {|x| [x.name, x.id]}, {:include_blank => 'Select Few'}, class:'form-control' %><%= f.button "+", class:'btn btn-primary', id: 'btn-ingredients' %><br />
</div>
<div class="form-group">
<%= f.label :preparation %><br />
<%= f.text_area :preparation, class:'form-control'%><br />
</div>
<%= f.submit "Submit", class:'btn btn-primary' %>
<%= link_to "Cancel", drinks_path, class:'btn btn-default' %>
<% end %>
application.js
$(document).ready(function(){
$('#btn-ingredients').click(function(){
$('#add-ingredients').append('<%= render("form-group") %>');
});
});
I was trying a lot of times and it could be something really easy, but I'm a newbie and haven't got other ideas.
Well, you can do that in some ways, I just wanna say that manipulating directly the DOM many times is an anti-pattern. Nowadays you have frameworks as React/Redux or ClojureScript that handle the global state and re-render the DOM automatically when the global state "ingredients" is changed.
Maybe not now, but keep it in mind. Handle the state in a "Single Page Application" is tricky.
application.js is a static file which is not pre-processed by a ERB-processor, for example. It goes to user's browser as it is, and when executed on client side this code:
$('#add-ingredients').append('<%= render("form-group") %>');
produces a JS error.
To make it work you need to "unwrap" call of render("form-group") into HTML code. For example:
$('#add-ingredients').append('<div class="form-group">...</div>');
Also please note that append actually appends newly created element to the end of element, not AFTER it. So after that call new 'form-group' element will be added to the end of '#add-ingredients' content, which may be undesirable since it has class 'form-group' too.

Rendering a partial to an edit form inside a popover in rails 4

Hi guys I really need help here.
I'm making a todo app and displaying each task through an iteration. Next to each task I have a pencil glyphicon.
When I click on it, I want there to be a popover containing an edit form and edit the task on the spot. I'm having a ton of troubling rendering that form.
***** UPDATE *****
Clark helped me get the popover to work. But The form inside the popover is acting like a link instead of a form so I can't type in the text area.
This is my index.html.erb
<div id='user_tasks'>
<% #tasks.each do |task| %>
<ul>
<li>
<%= task.description %>
<%= link_to edit_task_path(task), :rel => 'popover', method: 'get', remote: 'true' do %>
<span class = 'edit_task_<%=task.id%> glyphicon glyphicon-pencil'> </span>
<% end %>
</li>
</ul>
<% end %>
</div>
This is the edit.js.erb
$(function(){
$('.edit_task_<%=#task.id%>').popover({
html: true,
title: 'edit',
content: <%= escape_javascript render 'edit_form', task:#task %>
}).popover('show'); });
and my controllers look like this:
def index
#new_task= Task.new
#tasks= Task.all
end
def create
#new_task= Task.new(task_params)
#task.save
redirect_to :back
end
def edit
#task= Task.find(params[:id])
end
_edit_form.html.erb looks like this: This isn't the edit form that I plan to use. Its just a copy of the create task form. I plan to fix it once I get the popover working.
<%= form_for task, :html=>{:class=> 'form-inline'} do |f| %>
<div class='form-group'>
<%= f.text_field :description, placeholder: 'create a task' %>
<%= button_tag(type:'submit', class:"btn btn-default btn-xs") do %>
<span class='glyphicon glyphicon-plus'></span>
<% end %>
</div>
<% end %>

Rails showing series of objects with form_for and Ajax

I'm trying to show a list of "Interests" that users can "follow" by clicking on a button associated with the Interest's image. Upon clicking the button to follow an Interest, the Interest (and it's corresponding image) should disappear from the list.
I'm using a hide function in a create.js.erb file called upon by a Relationship controller when the follow button is clicked, but the function will only hide the first Interest in the series of Interests--NOT the Interest the user clicked on that needs to disappear. So there has to be a better way to set this up, but I cannot, for the life of me, figure it out. Here's my current setup:
My Controller
class StaticPagesController < ApplicationController
def home
#user = current_user
#city = request.location.city
#interests = Interest.all
end
The home page
<%= render #interests %>
The partial
<div id="follow_form">
<div class="four columns portfolio-item interests">
<div class="our-work">
<a class="img-overlay">
<%= image_tag interest.image_path %>
<div class="img-overlay-div">
<h4><%= interest.name %></h4>
</br>
<h5>Placeholder
</br>
<%= interest.desc %></h5>
<%= form_for(current_user.relationships.build(followed_id:
interest.id), remote: true) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "I'm interested", class: "b-black" %>
<% end %>
</div>
</a>
<h3><%= interest.name %><span>features info</span></h3>
</div>
</div>
</div>
And the create.js.erb that's called upon by a Relationships controller when you click the button:
$("#follow_form").hide();
UPDATE
Here is the Relationships controller for additional clarification
def create
#interest = Interest.find(params[:relationship][:followed_id])
current_user.follow!(#interest)
respond_to do |format|
format.html { redirect_to(root_url) }
format.js
end
end
You need to provide something to differentiate the different "follow_forms" that you have on the same page. Otherwise, jQuery will just select the first element and only remove that one.
One idea of how to do this would be to append the "interest.id" to the end of the follow_form id
Update your partial to be like this (assuming your partial is called and located in /app/views/interests/_follow_form.html.erb:
<div id="follow_form<%="#{interest.id} %>">
<div class="four columns portfolio-item interests">
<div class="our-work">
<a class="img-overlay">
<%= image_tag interest.image_path %>
<div class="img-overlay-div">
<h4><%= interest.name %></h4>
</br>
<h5>Placeholder
</br>
<%= interest.desc %></h5>
<%= form_for(current_user.relationships.build(followed_id:
interest.id), remote: true) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "I'm interested", class: "b-black" %>
<% end %>
</div>
</a>
<h3><%= interest.name %><span>features info</span></h3>
</div>
</div>
</div>
This way each follow_form will have a unique ID attached to it. Then you can loop through your interests and call a partial on each one like this:
home page:
<% #interests.each do |interest| %>
<%= render 'interests/follow_form', :interest => interest %>
<% end %>
create.js.erb
$("#follow_form_<%= "#{#interest.id}" %>").hide())
Your controller action can stay the same.

Rails 3 coffeescript controller association?

Alright, I'm a JS / JQuery / Coffeescript noob. This is probably easy points for someone.
Having successfully implemented RBate's Nested Form Model railscast, I am attempting to reproduce this in a simpler model: Chapters have many counties.
I have a chapters.js.coffee file with the following code:
jQuery ->
$('form').on 'click', '.remove_fields', (event) ->
$(this).prev('#destroy').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
This code works just fine in the other model. But not here.
_chapters_form.html.erb:
<div class="row span12">
<%= form_for(#chapter) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="span2"><strong>Chapter name:</strong></div>
<div class="span6"><%= f.text_field :name %></div>
<div class="span2"><strong>Chapter Number:</strong></div>
<%= f.number_field :chapter_num, class: "span2" %>
</div>
<div class="row span12">
<div class="span12">
<%= f.fields_for :counties do |builder| %>
<%= render 'county_fields', f: builder %>
<% end %>
</div>
<% if f.object.new_record? then link = 'Add the Chapter' else link = 'Update Chapter' end %>
<%= f.submit "#{link}", class: "btn btn-large btn-primary" %>
<%= link_to "Cancel", chapters_path, class: "btn btn-large btn-primary" %>
<% end %>
</div>
and:
_county_fields.html.erb:
<fieldset>
<div class="well span12">
<div class="row span12">
<div class="span3">County Number: <br /><i>(6-digit FIPS code)</i></div>
<div class="span2"><%= f.number_field :county_num %></div>
<div class="span2">County Name:</div>
<div class="span5"><%= f.text_field :name %></div>
</div>
<div class="row span12"><hr></div>
<div class="row span12">
<div class="span6">Move to new Chapter:</div>
<div class="span6"><%= select(:county, :chapter_id, Chapter.all.collect {|c| [c.name, c.id]}) %></div>
</div>
<div class="row span12">
<div class="pull-right">
<%= f.hidden_field :_destroy, id: "destroy" %>
<%= link_to "remove county", "#", class: "remove_fields" %>
</div>
</div>
</div>
</fieldset>
There are no errors in the JS. Again, noob speaking, but it doesn't seem that the JS is getting called. Clicking <%= link_to "remove county", "#", class: "remove_fields" %> just adds the # to the URI.
What am I doing wrong?
As requested, the HTML in a fiddle which doesn't work either.
Your HTML is broken. You open a div before the form element, then close it before you close the form. If you move the form element up to just inside the container div, it works.
<body>
<div class="container-fluid">
<form accept-charset="UTF-8" action="/chapters/7" class="edit_chapter" id="edit_chapter_7" method="post">
<div class="row-fluid">
...
</div>
</form>
</div>
</body>
You should take more care in the indenting of your HTML to help avoid this sort of simple mistake. Code format matters.
The page on which they appear - regardless of how they are rendered - must be an action of the chapters_controller for chapters.js.coffee to be included. My bet is that the script is not being included at all, as the code looks fine. Check out the pages you are having issues with with this code:
jQuery ->
console.log "included chapters.js.coffee"
$('form').on 'click', '.remove_fields', (event) ->
console.log "clicked .remove_fields"
$(this).prev('#destroy').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
Also, post up the rendered HTML in your question

Categories