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.
Related
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.
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).
On initial page load, all of the current user's highlights are gathered and each is put into a form. These forms are later shown the user once he clicks a certain button.
The scenario I'm trying to sort out, though, is that through the course of the user's interactions, new highlights can be created. And these new highlights also need to be 'prepared' within the same series of forms.
At the moment, I'm trying to use a gigantic .append() method, but it's failing silently in the background.
Any pointers? I suspect it may have something to do with improper quotation management within the method call and/or incorrect usage of the "j" escape_javascript Rails method...
Is there a better way of approaching the scenario? I've tried 'refreshing' the div with a call to render the partial. While that does achieve the end result of having "current" data on display, the problem there is the associated *.js file becomes 'disconnected' from the page, ruining the user interactions.
Thank you in advance for any help or insight you can offer!
_partial.html.erb This is what I'm essentially trying to 'add to'
<% #current_user_highlights.each do |highlight| %>
<div class="slide">
<%= form_for(highlight, remote: true) do |f| %>
<%= f.fields_for :image_tags_attributes do |image_tag_fields| %>
<%= image_tag_fields.fields_for :tag_content_attributes do |tag_content_attributes_fields| %>
<%= image_tag highlight.file_url(:speck), data: { src: highlight.file_url(:medium),
id: highlight.id } %>
<div class="wrapper">
<%= tag_content_attributes_fields.text_field :content, class: 'form-control',
id: "tag-content-field-#{highlight.id}" %>
<%# id: "tag-content-field-#{highlight.parent_image.id}" %1> %>
<div class="actions display-none">
<%= button_tag "", type: 'submit',
class: "btn btn-lg btn-default glyphicon glyphicon-ok above-interaction-barrier",
id: "highlight-confirmation-submit-button-#{highlight.id}" %>
<%= button_tag "", class: "btn btn-lg btn-default glyphicon glyphicon-remove above-interaction-barrier",
id: "highlight-confirmation-remove-button-#{highlight.id}" %>
<%= button_tag "Skip", class: "btn btn-med btn-default above-interaction-barrier display-none skip-button",
id: "highlight-confirmation-skip-button-#{highlight.id}" %>
</div>
</div>
<% end %>
<%= image_tag_fields.fields_for :tag_title_attributes do |tag_title_attributes_fields| %>
<%= tag_title_attributes_fields.hidden_field :title, value: highlight.tag_titles.first.title %>
<% end %>
<% end %>
<% end %>
</div>
<% end %>
update.js.erb
$('#highlight-confirmation-wrapper div.slide:last').append(
"<div= 'slide' <%= j form_for(#image.crops.last, remote: true) do |f| %>
<%= f.fields_for :image_tags_attributes do |image_tag_fields| %>
<%= image_tag_fields.fields_for :tag_content_attributes do |tag_content_attributes_fields| %>
<%= image_tag #image.crops.last.file_url(:speck), data: { src: #image.crops.last.file_url(:medium),
id: #image.crops.last.id } %>
<div class="wrapper">
<%= tag_content_attributes_fields.text_field :content, class: 'form-control',
id: "tag-content-field-#{#image.crops.last.id}" %>
<%# id: "tag-content-field-#{highlight.parent_image.id}" %1> %>
<div class="actions display-none">
<%= button_tag "", type: 'submit',
class: "btn btn-lg btn-default glyphicon glyphicon-ok above-interaction-barrier",
id: "highlight-confirmation-submit-button-#{#image.crops.last.id}" %>
<%= button_tag "", class: "btn btn-lg btn-default glyphicon glyphicon-remove above-interaction-barrier",
id: "highlight-confirmation-remove-button-#{#image.crops.last.id}" %>
<%= button_tag "Skip", class: "btn btn-med btn-default above-interaction-barrier display-none skip-button",
id: "highlight-confirmation-skip-button-#{#image.crops.last.id}" %>
</div>
</div>
<% end %>
<%= image_tag_fields.fields_for :tag_title_attributes do |tag_title_attributes_fields| %>
<%= tag_title_attributes_fields.hidden_field :title, value: #image.crops.last.tag_titles.first.title %>
<% end %>
<% end %>
<% end %>
</div>");
I was able to get this sorted out by changing the approach I was using.
Rather than have the form partial include the call to #current_user_highlights, and thereby proceed to make numerous forms, I instead broke the form itself into a separate partial. This allows me to reuse the form later to 'render' a single instance based off of the highlight that is sent through along with the update.js.erb file.
I also added an additional wrapper around the slide listing, this allowed me to more easily use the .append() jQuery function.
The resulting update.js.erb file looks something like this:
$('#slides-wrapper').append("<div class='slide'> <%= j render 'tasks/highlight_confirmation_slide_form', highlight: #image.crops.last %> </div>");
I have a _user.html.erb partial that I am rendering on my Users#Index action with a form embedded to allow following and unfollowing of each user. I also have accompanying create.js.erb and destroy.js.erb files in my relationships controller to handle the AJAX. Right now I have to refresh the page in order to see the changes made by clicking the "follow/unfollow" button next to a users name except the first user's record behaves correctly.
_user.html.erb
<div id ="follow_form">
<% if current_user.following?(user) %>
<%= form_for(current_user.relationships.find_by(followed_id: user.id),
html: { method: :delete },
remote: true) do |f| %>
<%= f.submit "Unfollow", class: "btn btn-large" %>
<% end %>
<% else %>
<%= form_for(current_user.relationships.build(followed_id: user.id),
remote: true) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>
<% end %>
create.js.erb
$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>")
$("#followers").html('<%= #user.followers.count %>')
destroy.js.erb
$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>")
$("#followers").html('<%= #user.followers.count %>')
here is a link to my users_controller.rb https://gist.github.com/anonymous/9608208 and my relationships_controller.rb https://gist.github.com/anonymous/9608284
I've tried changing the div to something dynamic like
<div id ="follow_form_<%= user.id %>">
But when I make the same change to the javascript like this
$("#follow_form_<%= user.id %>").find.html("<%= escape_javascript(render('users/unfollow')) %>")
it doesn't seem to work.
I've been trying to figure out how to pass the user.id attribute into the javascript but am falling flat.
It turns out the problem was in my _follow_form.html.erb partial.
I had to change
<div id="follow_form">
to
<div id="follow_form_<%= #user.id %>">
I thought I only needed to make the change in the _user.html.erb partial but I was wrong. My current user partial uses the same div id to contain the button in the Users#Index view
<div id ="follow_form_<%= user.id %>">
I'm trying to use tabifier in my rails app so I have:
<%= stylesheet_link_tag "example", "example-print" %>
<%= javascript_include_tag 'tabber.js' %>
<script>
/* Optional: Temporarily hide the "tabber" class so it does not "flash"
on the page as plain HTML. After tabber runs, the class is changed
to "tabberlive" and it will appear. */
document.write('tabber{display:none;}');
</script>
<div class="tabber">
<div class="tabbertab">
<h2>Tab 1</h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :peeruser %><br />
<%= f.select :peeruser, [['Not yet','not yet'],['Beginner','beginner'],['Intermediate','intermediate'],['Expert','expert']] %></div>
<div><%= f.label :discipline %><br />
<%= f.text_field :discipline %></div>
<div><%= f.label :course %><br />
<%= f.text_field :course %></div>
<div><%= f.label :concept %><br />
<%= f.select :concept, [['Yes','yes'],['No','no']] %></div>
<div><%= f.submit "Continue" %></div>
<% end %>
</div>
<div class="tabbertab">
<h2>Tab 2</h2>
<p>Tab 2 content.</p>
</div>
<div class="tabbertab">
<h2>Tab 3</h2>
<p>Tab 3 content.</p>
</div>
</div>
<%= link_to "Back", :back %>
And I can't figure out why it's not loading correctly like the examples:
http://www.barelyfitz.com/projects/tabber/
I put the javascript if under public->javascripts and the css under public->stylesheets - like it just shows the text, none of it is linkable and it displays all the form information.
not exactly a solution, but why not use a more popular and feature-rich library ?
JqueryUI features a nice and simple way to implement tabs.
Once the lib is loaded, all you will have to do is
<script>
$(function() {
$( ".tabber" ).tabs();
});
</script>
then add some links, reclass all your divs and bam ! instant tabs.
Plus, you have all the Jquery Lib, which is pure awesomeness.
Turns out I had not put the css and js in the assets folder which was causing the problem.