Devise with multiple models, bootstrap modal with ajax login - ajax

I have 2 kinds of users - nurses and patients, and want to setup login such that there are two buttons on the welcome/landing page which open up a bootstrap modal with the login form for each type of user. I'd like the login form to send an AJAX request so that if there are errors, they are displayed in the modal itself.
I'm using Devise for authentication, and have setup 2 models for nurses and patients. Initially, I setup the modal to load the url for the new_nurse_session_path on clicking the button, modified the default devise login form to send AJAX requests, used a custom SessionsController to handle new sessions and send JSON replies back, and then have JS code which catches the reply. This worked (though with some issues as below) was pretty slow since it was loading the entire page from nurses/sessions/new.html.erb along with the header, navbar, etc.
Questions
While the JS code was catching the AJAX request when the entire sign in page was opened in the modal (as per code below), if I opened the page directly, the AJAX request wasn't processed even though the server was sending the correct JSON back - is there something I'm doing wrong here that might be causing this problem?
To avoid the entire new session page from being loaded in the modal, I followed the instructions here https://github.com/plataformatec/devise/wiki/How-To:-Display-a-custom-sign_in-form-anywhere-in-your-app to put the form in the modal. The issue I'm facing here is how to create the helper so that it defines the resource and Devise mappings according to whether it's a nurse or patient trying to login. If I create separate custom forms and modals for each one, I face the same issue with handling AJAX requests as in Q1. Would really appreciate help on what I might be doing wrong here.
If I'm going down a rabbit hole here, would really appreciate suggestions on implementing the above functionality in a better way! Thanks!
Code used
Login form, nurses/sessions/new.html.erb:
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name),
:html => {:id => "sign_in_nurse"}, :format => :json,
:remote => true) do |f| %>
<div><%= f.label :email %><br />
<%= f.email_field :email, :autofocus => true %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<% if devise_mapping.rememberable? -%>
<div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
<% end -%>
<div><%= f.submit "Sign in" %></div>
<% end %>
<%= render "nurses/shared/links" %>
Coffeescript to handle AJAX reply, nurses.js.coffee:
$("form#sign_in_nurse").bind "ajax:success", (e, data, status, xhr) ->
if data.success
window.location.replace(data.redirect)
else
alert('failure!')
SessionsController:
class SessionsController < Devise::SessionsController
def create
resource = warden.authenticate!(:scope => resource_name, :recall => "sessions#failure")
return sign_in_and_redirect(resource_name, resource)
end
def sign_in_and_redirect(resource_or_scope, resource=nil)
scope = Devise::Mapping.find_scope!(resource_or_scope)
resource ||= resource_or_scope
sign_in(scope, resource) unless warden.user(scope) == resource
return render :json => {:success => true, :redirect => stored_location_for(scope) || after_sign_in_path_for(resource)}
end
def failure
return render :json => {:success => false, :errors => ["Login failed."]}
end
end
Code to generate modal:
<li><%= link_to image_tag('i_am_nurse.png', :size => "130x130"),
new_nurse_session_path , :data => { :target => "#ajax-modal", :toggle => "modal"} %></li>
Bootstrap modal div:
<div id="ajax-modal" class="modal hide fade" tabindex="-1">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body">
<div class="modal-body-content"></div>
<div class="ajax-loader"></div>
</div>
<div class='modal-footer'>
<button type="button" data-dismiss="modal" class="btn">Close</button>
</div>
</div>​

Related

Rails sending multiple params through dropdown box

I have two dropdown boxes. One is for campaign size(small, medium, large) and the other one is for the theme selection.
I want to be able to send params dynamically through onchange event handler
current code looks like this
<div class="col-lg-12 col-padding-helper">
<%= form_tag product_builder_path, method: :get do %>
<div class="col-lg-5 col-padding-helper">
<%= label_tag 'Campaign Size' %></br>
<%= select_tag 'type', options_for_select(['Small (4 Products)', 'Medium (7 Products)', 'Large (10 Products)' ]), multiple: false, :include_blank => true, class: "form-control", data: { remote: true }, onchange: "this.form.submit();" %>
</div>
<% end %>
<%= form_tag(product_builder_path, method: :get, remote: true) do %>
<div class="col-lg-7 col-padding-helper">
<%= label_tag 'Theme' %></br>
<%= select_tag 'theme', options_for_select(['Default BWX Theme', 'Black Friday Theme']), multiple: false, :include_blank => true, class: "form-control", onchange: "this.form.submit();" %>
</div>
<% end %>
</div>
The workflow that I am thinking in my head is that once user selects the campaign size it will submit a form through ajax and have something like this as a url. which then I can display display right type of html template on the webpage.
/product_builder?type=small
and after when they select the theme is it possible to do something like this? and this will change the theme of the type that I have chosen above
/product_builder?type=small&theme=black
so basically it's adding params to current URL
I don't even know if this is possible, any help or guidance would be awesome
Thank you

Else part is not working in Rails ajax form

I have a Rails ajax form to submit.When user will give the correct data it will search the database and do the operation accordingly.If user is giving the Wrong data it should display user to alert message.But it is not happening like that.
I am explaining my codes below.
home.html.erb
<% if current_admin %>
<p class="flash-message"><%=flash[:notice]%></p>
<div class="col-md-6" style="float:none; margin:auto;">
<%= form_for :sdf ,:url => {:action => "scan_report" },remote: true do |f| %>
<% if params[:receipt_no] %>
<div class="input-group bmargindiv1 col-md-12"> <span class="input-group-addon text-left">Receipt No. Scan :</span>
<%= f.text_field :Receipt_No,:class => "form-control", :value => params[:receipt_no] %><%= f.submit "Scan Report" %>
</div>
<% else %>
<div class="input-group bmargindiv1 col-md-12"> <span class="input-group-addon text-left">Receipt No. Scan :</span>
<%= f.text_field :Receipt_No,:class => "form-control",placeholder:"Receipt No. scan" %><%= f.submit "Scan Report" %>
</div>
<% end %>
<% end %>
<% end %>
controller/homes_contorller.rb
class HomesController < ApplicationController
def home
#sdf=TSdf.new
respond_to do |format|
format.html
format.js
end
end
def scan_report
if #sdf=TSdf.find_by_Receipt_No(params[:sdf][:Receipt_No])
if #sdf && #sdf.HCSY_Status=='YES'
#hcsy=THcsy.find_by_Sdp_Id(#sdf.Sdp_Id)
#hcsy_deatils=THcsyDetails.find_by_HCSY_ID(#hcsy.id)
#woods=THcsyFundTypeMaster.find_by_Fund_Type_Code(1)
#burn=THcsyFundTypeMaster.find_by_Fund_Type_Code(2)
#good=THcsyFundTypeMaster.find_by_Fund_Type_Code(3)
#swd=THcsyFundTypeMaster.find_by_Fund_Type_Code(5)
#photo=THcsyFundTypeMaster.find_by_Fund_Type_Code(6)
end
else
flash[:notice]="Not Found"
end
end
end
In this form its only taking the correct input but if user is trying to give the wrong input the else part is not executing.Please help me to resolve this error and let me to know how can i make this ajax call in below format.
$('form').submit(function() {
var valuesToSubmit = $(this).serialize();
$.ajax({
type: "POST",
url: $(this).attr('action'), //sumbits it to the given url of the form
data: valuesToSubmit,
dataType: "JSON" // you want a difference between normal and ajax-calls, and json is standard
}).success(function(json){
console.log("success", json);
});
return false; // prevents normal behaviour
});
Please help me.I am using Rails version 3.2.19.
Here you send AJAX POST request to scan_report, and if it is successful it should render template scan_report.js.erb, and rendered template will appear in json variable in success callback.
But if params are not valid, the same template is rendered, but all instance variables are missing. And probably empty json is returned or an error appears in log.
Anyway, if you'd like to change flash in remote form, you should put custom code in fail callback. Something like
$("#flash").html('<%= j render flash[:notice] %>');

Understanding Ajax in Rails 3

I thought I understood Ajax in Rails 3 to a point but think I am confusing myself somewhere or misinterpreted something. My understanding is that if I want to call different content on the same page then i create a js.erb file for that particular action. For example if i have an action of tynewyddnews, I will have an tynewyddnews view and a tynewyddnews.js.erb file.
I then create a partial to render on that page and call that partial in my .js.erb file.
Example
View
<% #tynewyddpost.each do |t| %>
<li>
<% single = t.photos.first %>
<a class="photo" href="#"><%= image_tag(single.avatar.url(:thumbnail_news_images)) %></a>
<p><%= link_to t.title, tynewyddnews_path(:type => 'tynewyddnews'), :post_id => t.id, :remote => true %></p>
<p class="date"><%= date_output(t.published_on) %></p>
</li>
<% end %>
<div class="post-item">
<h2><%= #tynewyddpostlatest.title %></h2>
<div id="work-samples">
<% for photo in #tynewyddpostlatest.photos %>
<%= image_tag(photo.avatar.url(:news_images), :class => 'work-sample') %>
<% end %>
</div>
<p class="post-description"><%= #tynewyddpostlatest.comments.html_safe %></p><a class="post-more" href="#">Continue Reading »</a>
<div class="post-item-panel">
<ul>
<li class="date">
<p><%= date_output(#tynewyddpostlatest.published_on) %></p>
</li>
</ul>
</div>
</div>
So the historical posts go down the left hand side and the latest post goes in the center
.js.erb
<% if params[:type] == 'tynewyddnews' %>
jQuery('.post-item').html('<%= escape_javascript(render partial: 'tynewyddnewspost') %>');
<% end %>
Partial
<div class="post-item">
<h2><%= #tynewyddpost.title %></h2>
<div id="work-samples">
<% for photo in #tynewyddpost.photos %>
<%= image_tag(photo.avatar.url(:news_images), :class => 'work-sample') %>
<% end %>
</div>
<p class="post-description"><%= #tynewyddpost.comments.html_safe %></p>
<div class="post-item-panel">
<ul>
<li class="date">
<p><%= date_output(#tynewyddpost.published_on) %></p>
</li>
</ul>
</div>
</div>
Controller
def tynewyddnews
tynewyddpost = Post.tynewydd_posts.find(params[:post_id])
tynewyddpost.shift
#tynewyddpost = tynewyddpost
#tynewyddpostlatest = Post.tynewydd_posts.first
end
tynewydd_posts is a scope
scope :tynewydd_posts, :include => :department, :conditions => {"departments.name" => "Ty Newydd"}, :order => "posts.published_on DESC"
So my issue at the moment is when i try and render the page i get the error
Couldn't find Post without an ID
Apologies for the long post but if someone could point me in the right direction that would be great
Thanks
It looks to me like you're doing it way wrong, sorry. It doesn't need to be that complicated. I would start over from scratch with a much simpler example, and then build up from there.
I would do something like rails g scaffold post title:string and then do this, just in a browser console:
$.ajax({
url: "/posts/index"
}).done(function() {
console.log("done");
});
You should be able to see the data coming back through your browser's console. On the Rails side, that data is coming from here:
class PostsController < ApplicationController
def index
#posts = Post.all
# there might be more stuff here
end
end
Hopefully you understand what I'm talking about. If you don't, I'd recommend separately learning a little more about Rails and a little more about jQuery/ajax before trying to combine the two.
OK so it seems my issue was the way i was calling the id of a post and I also moved my ajax call into a separate action, setup all be it complicated is correct.
What i changed
<%= link_to t.title, tynewyddnews_path(:type => 'tynewyddnews'), :post_id => t.id, :remote => true %>
was changed to
<%= link_to t.title, my_path_path(:type => 'tynewyddnews', :id => t.id), :remote => true %>
Controller
I added a new action to handle the ajax call, finding a post by id
def my_path
#tynewyddpost = Post.find(params[:id])
end
Then in my partial I could use the #tynewyddpost instance variable
Hope this helps someone else

RoR: How can I get my microposts to show up?

Here is the users show view where they are supposed to show up. ..
<section>
<div id= "purchases">
<%= render 'shared/micropost_form_purchase' %>
</div>
<div id="sales">
<%= render 'shared/micropost_form_sale' %>
</div>
</section>
<%= #sales %> <%# This is just to see if it outputs anything. It doesn't :( %>
<div id="purchases list">
<ol class="microposts">
<%= render #purchases unless #purchases.nil? %>
</ol>
</div>
<div id="sales list">
<ol class="microposts">
<%= render #sales unless #sales.nil? %>
</ol>
</div>
so the forms (partials) are loading fine, but then when I make a post, in either one, neither the purchases list nor the sales list shows up. I checked the database and they are being created along with an entry in the column indicating kind (either sale or purchase).
Here are the forms:
<%= form_for (#micropost) do |f| %>
<div class="field no-indent">
<%= f.text_area :content, placeholder: "What's something else you want to buy?" %>
<%= hidden_field_tag 'micropost[kind]', "purchase" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
and
<%= form_for (#micropost) do |f| %>
<div class="field no-indent">
<%= f.text_area :content, placeholder: "What's something else you want to buy?" %>
<%= hidden_field_tag 'micropost[kind]', "sale" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
also, here is the show part of the users_controller.rb
def show
#user = User.find(params[:id])
#micropost=Micropost.new
#microposts = #user.microposts.paginate(page: params[:page])
end
and here is the show part of the microposts_controller.rb
def show
#micropost = Micropost.find(params[:id])
#microposts = Micropost.where(:user_id => #user.id)
#purchases= #microposts.collect{ |m| m if m.kind == "purchase"}.compact
#sales = #microposts.collect{ |m| m if m.kind == "sale"}.compact
end
additionally, with the help of this post (http://stackoverflow.com/questions/12505845/ruby-error-wrong-number-of-arguments-0-for-1#12505865) the variables #microposts, #purchases, and #sales are all outputting correctly in the console.
can anyone help me out?
edit: using scopes as suggested by the answer given works in the console (it outputs everything correctly, but they still don't show up in the view. Does this mean it is something wrong with my syntax for the users show page?
edit 2:
Here is the view/microposts/_micropost.html.erb code
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
confirm: "You sure?",
title: micropost.content %>
<% end %>
</li>
I'm making some assumptions without seeing more of your code, but it looks like you could
write what you've shown a little differently. I'm assuming your databases are migrating
and have the required columns, e.g., Micropost#kind, Micropost#user_id, etc.
You can use scopes to refine a collection of microposts more expressively. It might be helpful to read
up about ActiveRecord scopes: http://guides.rubyonrails.org/active_record_querying.html#scopes.
class Micropost < ActiveRecord::Base
belongs_to :user
scope :purchases, where(:kind => "purchase")
scope :sales, where(:kind => "sale")
# your code
end
I'm also assuming your user has many microposts:
class User < ActiveRecord::Base
has_many :microposts
# your code
end
For your forms, I'd suggest attaching your hidden field to the form object (f.hidden_field) so
you don't have to specify the name as 'micropost[kind]'.
<%= form_for(#micropost) do |f| %>
<div class="field no-indent">
<%= f.text_area :content, placeholder: "What's something else you want to buy?" %>
<%= f.hidden_field :kind, :value => "sale" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
In MicropostsController#show, you can use your new scopes:
def show
#micropost = Micropost.find(params[:id])
#microposts = #user.microposts
#purchases = #microposts.purchases
#sales = #microposts.sales
end
You should also confirm that your MicropostsController#create action is actually adding
the microposts to the user sending the form (I'm assuming a current user method).
def create
#micropost = current_user.microposts.create(params[:micropost])
# yada
end
You can also confirm expected results on rails console after creating purchases or sales micropost with:
Micropost.purchases
Micropost.sales
Again, I could be missing something without seeing more of the code base.
Check Micropost.count, #purchases.count, #sales.count (by printing them in the controller, or some part of the view) to see if the records actually exist.
Also, if you want to render collections likes #sales and #purchases, you need to make sure that the model partial exists (_micropost.html.erb in your case). That is probably where you need to look for the view errors. For all you know, that file could be empty, thus no errors will show up at all.
The problem might also lie in your microposts#create (or whichever action that you are saving the micropost in), the micropost should be associated with the current_user:
#micropost = current_user.microposts.build(params[:micropost])
Taking this and your previous question into account, I suggest you go through the original code for the RoR tutorial again (and verify that all tests are passing) before taking it apart. You can always add new tests to it for your experiments and they will help in figuring out where you went wrong.

Rails based simple AJAX calculation fxn seems harder than it should be

I feel like there is a large step from where Rails tutorials end and where I can actually use it to do what I need. This is manifesting itself in this relatively simple case:
I want a page that takes inputs and calculates a volume. Ideally, this would do it with AJAX because it always irritates me when a simple page makes me reload it.
I've been doing this in IRB with:
radius = 25; length = 0.15;( radius**2) * Math::PI * length *1e-3
# radius in microns, length in meters, answer in microliters
But the confusion comes in just about here. I understand that I must route a page, I understand that my controller is responsible for directing to the correct response, but where should my calculation occur? How do I capture the responses for my form?
I just don't seem to understand how to integrate all these things...
Here's my view (which renders, but from which I can't seem to feed the information in or out:
<h2>Here I'll calculate the dead volume of a piece of tubing for you.</h2>
<%= form_tag url_for(:method => :get), :remote => true, :id => 'calculation_entry' do %>
<div id="internal_diameter">
<label for="diameter">Enter the internal diameter:</label>
<%= text_field_tag :diameter %>
<label for="diameter_unit"></label>
<%= select_tag :diameter_unit, options_for_select({
"Microns" => 'micrometers',
'inches' => 'inches'
}) %>
</div>
<div id="length">
<label for="length">Enter the length of tubing:</label>
<%= text_field_tag :length %>
<label for="length_unit"></label>
<%= select_tag :length_unit, options_for_select({
"meters" => 'meters',
"inches" => 'inches'
}) %>
</div>
<%= submit_tag "Calculate!" %>
<% end %>
<div class="output">
<span>The volume of the tubing parameters entered is:</span>
<%= #object %>
</div>
And here is the controller I've built:
class PagesController < ApplicationController
def index
end
def dead_volume
#object = Object.new
end
def calculate
#object = params.inspect
end
end
Here is an example of how I implemented autocomplete functionality with jQuery UI & Ajax. It may help you to determine how to do this for your own application's needs.
Note: This is was done under Rails 3.0.X. There are probably other/better ways to do this.
View/Layout:
<div id="search">
<%= form_tag('/<my_controller>/search', :method => :get, :id => "#search-text") do %>
<input type="text" name="search" value="" id="search-text" placeholder="Search">
<input type="submit" value="Go" class="button">
<% end %>
</div>
JavaScript (note: uses jQuery UI):
$(document).ready(function() {
$("#search-text").autocomplete({
source: function(request, response) {
$.getJSON("/<my_controller>/autocomplete", {
search: request.term
},
response);
},
minLength: 2,
select: function(event, ui) {
this.value = ui.item.value;
return false;
},
});
});
Route:
match '/<my_controller>/autocomplete' => '<my_controller>#autocomplete', :via => :get
Controller:
respond_to :json, :only => [:autocomplete]
def autocomplete
#model = Model.autocomplete(params[:search])
render(:json => #model)
end
I hope this helps you get on your way.

Resources