I'm busy learning Rails 4 and I would like to display a bootstrap popup modal when a user clicks on a link, when the modal opens it needs to show the information relating to that particular record.
Heres what I have done so far, which just doesn't show the actual modal popup BUT does pass the correct parameters.
index.html.erb page has:
<%= link_to "view", work_path(w), remote: true, :"data-toggle" => 'modal', :"data-target" => '#myModal' %>
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"></div>
I also have a works/_modal.html.erb:
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">× </button>
<h4 class="modal-title" id="myModalLabel"></h4>
</div>
<div class="modal-body">
<%= #work.name %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
also a works controller:
def show
#work = Work.find(params[:id])
if request.xhr?
respond_to do |format|
format.html {render :partial => 'modal'}
format.json {head :ok}
end
end
end
and finally a works/show.js.erb:
$("#myModal").html("<%= escape_javascript(render 'modal') %>");
I hope i'm missing something easy here. in the console I can see the following message so i know it is returning the correct information, but unfortunately it is not showing the modal.:
Started GET "/works/8" for 127.0.0.1 at 2014-03-03 09:31:12 +0000
Processing by WorksController#show as JS
Parameters: {"id"=>"8"}
Work Load (0.2ms) SELECT "works".* FROM "works" WHERE "works"."id" = ? LIMIT 1 [["id", 8]]
Rendered works/_modal.html.erb (4.9ms)
Completed 200 OK in 8ms (Views: 6.3ms | ActiveRecord: 0.2ms)
Any help on this is greatly appreciated.
Have a try with another way. In this way, we are going to show the modal popup using explicit javascript command.
In index.html.erb
<%= link_to "view", work_path(w), remote: true, class: 'static-popup-link'%>
<div class="modal hide fade" id="myModal" tabindex="-1" role="dialog" data-backdrop="static" data-keyboard="false">Loading...</div>
In application.js
$(document).ready(function() {
var clickOnPopupLink = function(){
$('body').on('click', '.static-popup-link', function(){
$('#myModal').modal('show');
});
}
clickOnPopupLink();
});
In controller
def show
#work = Work.find(params[:id])
end
In show.js.erb
$('#myModal').html("<%= j render("/works/modal")%>")
You also need to show the modal, add $('#myModal').modal('show') in show.js.erb
$("#myModal").html("<%= escape_javascript(render 'modal') %>");
$('#myModal').modal('show')
Related
I'm trying to display bootstrap modal with dynamically, so every time user click on record it shows modal with that record information. I don't want to show modal for all record every time I reload the page.
I got this error in browser console.
SyntaxError: Unexpected token '==='
Controller
events_controller.rb
def pay
#event = Event.find(params[:id])
respond_to do |format|
format.js{}
end
end
View
index.html.erb
<%= link_to pay_path(id: event.id), remote: true, class: "", method: :patch do %>
Paid <i class="fe fe-dollar-sign"></i>
<% end %>
Pay view
_pay.html.erb
<!-- Modal: pay invoice -->
<div class="modal fade show" id="pay_invoice" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-vertical" role="document">
<div class="modal-content">
<div class="modal-body">
<!-- Modal body -->
</div>
</div>
</div>
</div>
js.erb
pay.js.erb
document.querySelector("#pay_invoice").insertAdjacentHTML('afterbegin', "escape_javascript(<%= render partial: 'pay', locals: {event: #event} %>)");
document.querySelector("#pay_invoice").modal('show');
Routes
routes.rb
patch "events/:id/pay", to: "events#pay", as: :pay
any help?
You need to move the ERB tags outwards so that you actually call escape_javascript:
document.querySelector("#pay_invoice").insertAdjacentHTML(
"afterbegin",
"<%= escape_javascript(render partial: 'pay', locals: { event: #event }) %>"
);
The reason you are getting a syntax error is that you're outputting the partial unescaped and it contains at least one double quote which ends the string.
I have a basic modal window on my home page that renders a form to create a new Post.
I have a length validation in the Post model and want the form to display an error if it doesn't meet the minimum length.
This is the Post model:
class Post < ApplicationRecord
belongs_to :user
validates :content, length: { in: 1..400 }
end
And this is my home.html.erb view
<!-- Modal button -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#tweetModal">
New Tweet
</button>
<!-- Tweet Modal -->
<div class="modal fade" id="tweetModal" tabindex="-1" aria-labelledby="tweetModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="tweetModalLabel">Send a new Tweet</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">X</button>
</div>
<div class="modal-body">
<%= simple_form_for #post, remote: true do |f| %>
<div class="form-inputs">
<%= f.input :content %>
</div>
<div class="form-actions">
<%= f.submit "Submit", class: "btn btn-primary"%>
</div>
<% end %>
</div>
</div>
</div>
</div>
Post controller:
class PostsController < ApplicationController
before_action :set_user
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
#post.user = #user
if #post.save
redirect_to root_path
else
render :new
end
end
private
def post_params
params.require(:post).permit(:content)
end
def set_user
#user = current_user
end
end
The modal window won't render the error and the only way I've found to render it is to render :new if the Post could not be saved.
The problem with that is that it will render the new.html.erb view that belongs to Post instead of the modal window that I have in my home page.
Can I make the modal window display the error? Should I move the modal window code to a separate partial file and use JS to render it?
I have a form configured with some javascript. The javascript is loaded on load event handled by Turbolink:
//app/assets/javascripts/init.js
var ready = function () {
App.init(); //I configure some components of _form.html.erb partial
};
$(document).ready(ready);
$(document).on('page:load', ready);
If I render a partial the App.init() code is not execute and my application is not well configured.
//new.js.erb
$("#my-modal").html("<%= j( render 'form') %>");
$("#my-modal").modal("show"); //The partial is inside a modal
Can I execute the code after the partial is rendered?
Something like this:
$(document).on('partial:render', ready); //Concept
In need to execute the js code after the partial is render becouse I need to configure some Jquery plugin to customize the modal form.
The modal is a classic Twitter Bootstrap modal:
//_form.html.erb
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<div class="row">
<div class="col-sm-offset-3 col-sm-9">
<h3 class="modal-title" id="myModalLabel">new</h3>
</div>
</div>
</div>
<%= simple_form_for(...) do |f| %>
<div class="modal-body">
<ul class="errors"></ul>
<%= f.error_notification %>
<%= render "companies/fields", f: f %>
...
...
</div>
<% end %>
</div>
</div>
Try:
$(document).on('page:change', function () {
// Actions to do
});
In your case simpley replacing the 'page:load' line in your js call with:
$(document).on('page:change', ready);
I'm having with populating the bootstrap 4 modal with a show action for a clickable image link. I'm currently using Carrierwave for uploading images. At this point, when I click the image, only the modal overlay displays. The modal container with content inside of it, doesn't show at all.
User/Show.html.erb
<div class= "container">
<div class="username">#user.username</div>
<div class="posts_container_pos">
<hr style="border: 2px solid #515558;">
<%= render 'posts/posts', post_items: #user.posts %>
</div>
</div>
Posts/_Posts.html.erb
<div class"container-fluid">
<%= #post_items.each do |post| %>
<%= link_to image_tag(post.photo.url(:medium), style: 'height: 300px; width: 500px;'), modal_post_path(post), data: {:toggle => 'modal', :target => '#reusable_modal'}, lazy: true %>
<div id="post-modal" class="modal fade"></div>
<!-- Modal -->
<div class="modal fade" id="reusable_modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
</div>
</div>
</div>
<% end %>
<script type= "text/javascript">
$(document).on('page:change', function () {
$(document).on('click', '#reusable_modal [data-dismiss="modal"]',
function (e) {
$(e.target).removeData('bs.modal');
$('#reusable_modal').find('.modal-content').empty();
});
$(document).on('click', '[data-target="#reusable_modal"]', function (e) {
$("#reusable_modal").find(".modal-content").load($(this).attr("href"));
});
});
</script>
</div>
Posts/Modal.html.erb
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<i><%= image_tag(#post.user.avatar_url(:thumb), class: 'round-image-50') %></i>
<h4 class="modal-title" id="myModalLabel" style="margin-left: 60px; margin-top: -42px;"><%= post.user.username %></h4>
</div>
<div class="modal-body">
<% if #post.photo.present? %>
<%= image_tag(#post.photo.url(:large), style: 'max-width: 570px;') %>
<% end %>
<div class="modal_post_pos">
<%= sanitize content_with_emoji(#post.body) %>
</div>
</div>
<div class="modal-footer">
<%= render partial: 'comments/template', locals: {commentable: #post, new_comment: #comment} %>
</div>
Users_Controller.rb
def show
if params[:id].to_s.match(/^[0..9]+$/) && !User.friendly.exists?(params[:id])
render 'public/404.html', status: 404
else
#user = User.friendly.find(params[:id])
#post_items = #user.posts.paginate(page: params[:page])
end
end
Posts_Controller.rb
def modal
render layout: false
end
def show
#post = Post.find(params[:id])
#new_comment = Comment.build_from(#post, current_user.id, "")
respond_to do |format|
format.html
format.js
format.json {render json: #post }
end
end
Routes.rb
resources :posts, except: [:edit, :update] do
member do
get 'like', to: 'posts#like'
get 'dislike', to: 'posts#unlike'
get :modal
end
end
Server Log
Started GET "/posts/13/modal" for 127.0.0.1 at 2016-04-11 11:41:00 -0400
ActiveRecord::SchemaMigration Load (0.5ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by PostsController#modal as HTML
Parameters: {"id"=>"13"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 5]]
Rendered posts/modal.html.erb (47.5ms)
Completed 500 Internal Server Error in 240ms (ActiveRecord: 1.5ms)
NoMethodError - undefined method `user' for nil:NilClass:
app/views/posts/modal.html.erb:5:in `_app_views_posts_modal_html_erb__227267806_63456696'
Since you dont have any instance variable #post defined on _posts partial and still trying to call it within _posts, its throwing up error.
The approach to solve this issue if you want to use Bootstrap Modal without extra js script is to bring _post partial code to _posts partial itself and <% end %> the loop after modal.
<% #post_items.each do |post| %>
<%= link_to image_tag........................
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="myModalLabel">Post Show</h4>
</div>
<div class="modal-body">
# complete partial code goes here
</div>
</div>
</div>
</div>
<% end %>
Obviously above approach is not good for performance as it will render everything without the need for it.
Better approach is to use Ajax using jQuery (my preference). listen for this button event (onClick) and load modal on the fly using ajax get method. consider looking at this approach for better performance reasons.
The second solution i am talking about is well explained here: https://stackoverflow.com/a/22144587/2545197
Maybe something like this?
In your JS
var $modal = $('#ajax-modal');
$.fn.modalmanager.defaults.backdropLimit = 1;
$('.modal-link').on('click', function(){
var modal_url = $(this).data("modal-url");
if(modal_url != null){
// create the backdrop and wait for next modal to be triggered
$('body').modalmanager('loading');
$modal.empty().load(modal_url, '', function(){
$modal.modal();
// Any callback functions such as let's say selectpicker
// $('#ajax-modal .selectpicker').selectpicker('refresh');
});
return false;
}
});
and in ERB
<%= link_to 'Show', post_path(#post), class: 'modal-link', data: { modal_url: post_path(#post) } %>
I solved this problem by simply rendering the file for the modal show view instead of the normal show action.
def show
#post = Post.find(params[:id])
#new_comment = Comment.build_from(#post, current_user.id, "")
respond_to do |format|
format.html {render :file => "posts/modal", layout: false}
format.js {render :file => "posts/modal", layout: false}
format.json {render json: #post }
end
end
I am trying to use modals in place of the traditional 'show' views in Ruby. When a user clicks on a item in the index view, a modal popup shows instead of redirecting the user to a new 'show' page.
How can I accomplish this?
Let's assume that the controller looks something like this:
#list = Items.all
I want the modal to show the characteristics of each item on the #list object. For instance, one modal would show #list[0] and another would be #list[1]. How can I pass the index values to the modal?
An approach I might take would be, assuming that on your index there is a dropdown and a submit button ...
jQuery
$('#submit').click(function(){
$.ajax({
url: "items/get_item",
type: "GET",
data: { item: $('#select').val() },
success: function (data) {
//Populate modal in here with item details
$('#myModal modal-body').html(data)
}
});
});
Item Controller
def get_item
return :json => Item.where({:name => params[:item]}).to_json
end
View
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title" id="myModalLabel">Modal title</h4>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
You need to render a javascript template from the show action
def show
#list = Items.all
respond_to do |format|
format.js { render 'your_js_template' }
end
end
Then in your template you can use the #list instance variable to create the jQuery for the modal