how to end email using Rails and js and html button? - javascript

Im new to using rails , and i am trying to figure out a solution to sending an email using an html button, javascript function and rails.
HTML and JS code:
<%= form_tag root_path, class: "send-email" do %>
<%= submit_tag "Click to Send Email", style: "margin: 10px; padding: 10px" %>
<% end %>
<script>
document.querySelector(".send-email").onsubmit = function(e) {
e.preventDefault()
fetch(e.target.action, {
method: "POST",
headers: {
"X-CSRF-Token": document.querySelector('meta[name="csrf-token"]').content
}
}).then(function(response) {
if (response.ok) {
window.open('/mail', '_blank')
} else {
console.error(response)
}
});
}
</script>
ContactMailer.rb
class ContactMailer < ApplicationMailer
def contact
mail(to: 'abdelmoulabilel2000#gmail.com', subject:'testing for fly')
end
end
ContactMailerPreview.rb
# Preview all emails at http://localhost:3000/rails/mailers/contact_mailer
class ContactMailerPreview < ActionMailer::Preview
def contact
ContactMailer.contact()
end
end
contact.html.erb
<h1>Hello this is for testing the email function</h1>
And i couldn't figure out how to connect everything together.

Related

Dynamic dropdown in rails simple_form

I have a simple has_many and belongs_to relationship in my rails app. I'm using simple_form and want to dynamically change the dropdown options based on the value chosen by the user.
Models
class Processor < ApplicationRecord
has_many :processor_bank_accounts
end
class ProcessorBankAccount < ApplicationRecord
belongs_to :processor
end
Form inputs
<%= simple_form_for [#customer, #transaction] do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :status, :collection => ["payment request"], include_blank: false %>
<%= f.input :processor, collection: #processors ,label_method: :name,value_method: :id,label: "Processor" , include_blank: false %>
<%= f.input :processor_bank_account, collection: #bank_accounts , label_method: :bank_name, value_method: :id, label: "Processor Bank Account" , include_blank: true %>
<%= f.input :tcurrency, collection: #currencies, include_blank: false, label: 'currency' %>
<%= f.input :amount, as: :decimal, label: 'amount' %>
</div>
<div class="form-actions text-center">
<%= f.button :submit, "Add transaction", class: "form-button"%>
</div>
<% end %>
So essentially, I need the processor_bank_account dropdown to populate based on the processor chosen by the user. In the console, this would just be: ProcessorBankAccount.where(processor: processor).
Need to load options using JS and think I need to use JSON but not sure where to go from here
One way to do this would be to use jQuery to make an AJAX call to a controller action and then let Rails handle the rest through an erb template.
So on your page, with the form, invoke the action via AJAX using something like:
<script>
$(document).ready(function() {
$('#processor_id').on('change', function() {
$.ajax({
url: '/transactions/get_processor_bank_accounts',
type: 'GET',
data: {
processor_id: this.value
},
dataType: 'script',
error: function() {
alert('An error occurred retrieving bank accounts for the selected processor.');
}
});
});
});
</script>
NB, #processor_id is the id for your dropdown control.
Next, instantiate the bank accounts within your action in your controller:
def get_processor_bank_accounts
#processor_bank_accounts = ProcessorBankAccount.where(processor_id: params[:processor_id])
end
And finally simply create a view that will be responsible for repopulating your dropdown:
$select_list = $('#processor_id');
$select_list.empty();
<% #processor_bank_accounts.each do |pba| %>
$select_list.append($('<option value="<%= pba.id %>"><%= pba.name %></option>'));
<% end %>
I came up with the following solution:
1) Add a new method to my processors controller to render the inputs for the second (dynamic) dropdown in JSON format:
def processor_bank_accounts
render json: #processor.processor_bank_accounts.each do |bap|
{ id: bap.id, name: bap.name }
end
end
2) Assign this new function to a new route in config/routes:
get 'api/bankaccounts', to: 'processors#processor_bank_accounts', as: 'bankaccounts'
3) Create a JS function to access the route with the id of the processor selected in the first dropdown and populate the second dropdown with items from the JSON array:
// select first dropdown
const processor = document.getElementById("transaction_processor");
// select second dropdown
const bapSelect = document.getElementById("transaction_processor_bank_account");
function update_baps(processor_id) {
const url = `INSERTWEBURLHERE/api/bankaccounts?id=${processor_id}`;
fetch(url)
.then(response => response.json())
.then((data) => {
bapSelect.innerHTML = ""; // clear second dropdown
data.forEach((bap) => { // go through all the BAPs
const elem = `<option value="${bap.id}">${bap.bank_name}</option>`; // create option elements to insert into the second dropdown, bank_name is the chosen label method in the form
bapSelect.insertAdjacentHTML("beforeend", elem); // insert options into the dropdown
});
});
}
4) Trigger the JS when the first dropdown field is changed:
processor.addEventListener('change', function () {
update_baps(parseInt(processor.value));
});
You should add an id to the selects so you can identify them form the script.
$('select#processor').on('change', function() {
var processor_id = this.value;
var processor_bank_account = $('select#processor_bank_account')
$.ajax({
type: "GET",
url: <%= your_path %> ,
data: { processor_id: processor_id },
success: function(data, textStatus, jqXHR){
processor_bank_account.empty();
var option = new Option(data.bank_name, data.id, false, false);
processor_bank_account.append(option);
},
error: function(jqXHR, textStatus, errorThrown){...}
})
});

render follow/unfollow button in rails with ajax

I have implemented follow/unfollow functionality and would like to add AJAX call to it, but I am stuck.
My partial _follow_button.html.erb for follow/unfollow which is rendered on Users->index, looks like:
<% if current_user.id != user.id %>
<% if !current_user.following?(user) %>
<%= form_for(current_user.active_relationships.build, remote: true) do |f| %>
<div><%= hidden_field_tag :followed_id, user.id %></div>
<span class="follow"><%= f.submit "Follow User", class: "btn btn-primary btn-sm" %></span>
<% end %>
<% else %>
<%= form_for(current_user.active_relationships.find_by(followed_id: user.id),
html: { method: :delete }, remote: true) do |f| %>
<span class="unfollow"><%= f.submit "Unfollow User", class: "btn btn-secondary btn-sm" %></span>
<% end %>
<% end %>
<% end %>
Then my controller for relationships looks like:
class RelationshipsController < ApplicationController
respond_to :js, :json, :html
def create
user = User.find(params[:followed_id])
#follow = current_user.follow(user)
end
def destroy
user = Relationship.find(params[:id]).followed
#unfollow = current_user.unfollow(user)
end
end
My view on user profile looks like:
<div class="col-5" style="margin-left: -5px;">
<%= render '/components/follow_button', :user => User.find_by_username(params[:id]) %>
</div>
My routes.rb have the following routes defined:
resources :users do
member do
get :following, :followers
end
end
resources :relationships, only: [:create, :destroy]
My Views folder structure has subfolders Users and Relationships. Both of them have separate controllers, and I have tried adding simple alert function 'alert("Works");' to the create.js.erb in both of those subfolders to try and match them with the controller, but none don't seem to work. This is my first Rails project, and I do not quite understand what the issue could be. Any suggestions?
Calling the partial follow/unfollow
<% if current_user.id != user.id %>
<%= render partial: 'follow_links', locals: { user: user }
<% end %>
Partial follow_links.
<% show_follow_link = current_user.following?(user) ? 'hidden' : '' %>
<% show_unfollow_link = current_user.following?(user) ? '' : 'hidden' %>
<!-- links to follow/unfollow have data-attributes that include the path to make the ajax post and the user to follow, that is used to find the link to show after the ajax call. You should use the path to the controller that will create or destroy the relationship -->
<%= link_to 'Follow', '#', { class: 'follow-user btn-success #{show_follow_link}', "data-url": follow_user_path(user.id), "data-followee": user.id } %>
<%= link_to 'Unfollow', '#', { class: 'unfollow-user btn-danger #{show_unfollow_link}', "data-url": unfollow_user_path(user.id), "data-followee": user.id } %>
Javascript for the partial. Ajax post to follow/unfollow
$('.follow-user').on("click",function() {
follow_unfollow($(this), "follow")
});
$('.unfollow-user').on("click",function() {
follow_unfollow($(this), "unfollow")
});
function follow_unfollow(target, what_to_do)
url = target.attr('data-url')
followee = target.attr('data-followee')
if (what_to_do == "follow") {
other_button = $('.unfollow-user[data-followee="'+followee+'"]')
} else {
other_button = $('.follow-user[data-followee="'+followee+'"]')
}
$.ajax( {
url: url,
type: 'post',
success: function() {
// Hide this link
target.addClass('hidden');
// Show the other link
other_button.removeClass('hidden');
},
error: function(ret) {
alert(ret.responseJSON.error);
}
});
};
Changes in your controller.
class RelationshipsController < ApplicationController
def create
user = User.find(params[:followed_id])
#follow = current_user.follow(user)
respond_to do |format|
if #follow.valid?
format.html
format.json: { render json: #follow }
return
else
format.html
format.json: { render json: { :error => 'Follow failed', :status_code :not_found } }
end
end
end
def destroy
user = Relationship.find(params[:id]).followed
#unfollow = current_user.unfollow(user)
respond_to do |format|
if #unfollow.valid?
format.html
format.json: { render json: #unfollow }
else
format.html
format.json: { render json: { :error => 'Unfollow failed', :status_code :not_found } }
end
end
end
end
An advice
An advice, also regarding your last question: I would recommend - instead of posting questions about debugging code on StackOverflow - create a good debugging environment for yourself.
Byebug or Binding pry is a good place to start, but before you can use those properly you need to understand the code you are using. I would recommend reading Working with Javascript in depth! - it really helped me getting the hang of it and understanding the dataflow of Rails and ajax.
This would, i think, break the unbreakable Stackoverflow-loop, that i myself were tied to for a long time:
loop do
puts "Try code"
sleep 1000
puts "Arrhh! an error!"
sleep 1000
puts "Posting on Stackoverflow"
sleep 1000
puts "Waiting for answer"
sleep 1000
end
I hope you figure it out!

How to send id from controller to js in Rails app

I need to generate a big report file in background. Here is a simple view to create a OrderReport object.
<%= simple_form_for order_report, remote: true do |f| %>
<%= f.input :start_date, as: :date, html5: true %>
<%= f.input :end_date, as: :date, html5: true %>
<%= f.submit "Generate report", id: "test" %>
<% end %>
And that what is going on in the controller:
def create
order_report = OrderReport.new(order_report_params)
order_report.user = current_user
order_report.save
OrderReportJob.new(order_report).delay.perform
render nothing: true
end
After user click a submit button this action creates a background process to generate report. I wrote endpoint to check the status of this background job. This JS is a onclick function to Submit buttom by id #test
$.ajax({
url: report_url,
success: function(report) {
if(report.status === 'progress') {
$("#spin").show();
$interval = setInterval(checkStatus, 3000);
}
}
});
This is a part of the JS script. It works good, but the final step to send the ID of created OrderReport to this js file. As you can see in the JS script I have a variable report_url - it's already hardcoded and looks like
var report_url = '/order_reports/1'
So the main idea is to catch the ID of created OrderReport, if it's possible, and use it in the JS script. How can I pass it correctly?
Update:
order_report.js
$(function () {
$('#test').click(function() {
var report_url = '/order_reports/39'
$.ajax({
url: report_url,
success: function(report) {
if(report.status === 'progress') {
$interval = setInterval(checkStatus, 3000);
}
}
});
function checkStatus() {
$.ajax({
url: report_url,
success: function(report) {
if(report.status === 'done') {
clearInterval($interval)
}
}
});
}
});
});
A more RESTful solution is to use meaningful response codes to tell the client what happened with the request:
def create
order_report = OrderReport.new(order_report_params)
order_report.user = current_user
respond_to do |format|
if order_report.save
OrderReportJob.new(order_report).delay.perform
format.json { head :created, location: order_report }
else
format.json { head :unprocessable_entity }
end
end
end
head :created, location: order_report returns a 201 - Created response with a location header that contains a url to the created resource.
This lets you listen for the Rails UJS ajax:success and ajax:error events:
<%= simple_form_for order_report, remote: true, html: { class: 'order_report_form', 'data-type' => 'json'} do |f| %>
<%= f.input :start_date, as: :date, html5: true %>
<%= f.input :end_date, as: :date, html5: true %>
<%= f.submit "Generate report", id: "test" %>
<% end %>
$(document).on('ajax:success', '.order_report_form', function(e, data, status, xhr){
function checkStatus(url) {
return $.getJSON(url).then(function(data) {
// some logic here to test if we have desired result
if (!desiredResult) {
// never use setInterval with ajax as it does not care
// if the previous request is done.
// instead use setTimeout with recursion
setTimeout(1000, function(){ checkStatus(url) });
} else {
// do something awesome
}
}
}
checkStatus(xhr.getResponseHeader('location'));
});
$(document).on('ajax:error', '.order_report_form', function(){
alert("Oops");
});

Rails 4 update instance variables without page reload

Is there a way I can reload an instance variable in the view without reloading the page?
I have an AJAX post, and it creates a new record. I want that record to then be added to the existing instance variable.
So I have an action such as action and it handles the view I am in action.html.erb:
def action
#variable = Variable.where().to_a
end
And then I have an AJAX request that executes an action such as action2:
def action2
#new_record = Variable.create(params)
respond_to do |format|
format.html
format.json {render :json => #new_record.to_json}
end
end
Is there a way I can then refresh the #variable instance variable to include the newly created record?
Could I make another AJAX request to action? If so, how would I detect the AJAX request in action?
I have tried this^^ with request.xhr? but it is throwing a weird StandardError after I execute a JS .click()?
I'm not sure if I am going in the right direction, but if I am, here is all the code that I put together...
I am building an instant messaging service, and I admit that I am totally hacking together a prototype. I understand there are cleaner ways to do this that will handle high traffic loads, but I am working on a prototype right now.
The view code looks like this... there is: the HTML chatbox, I make an AJAX request to find new messages every 2 seconds, in the same function I make the AJAX request to what I called action above (actually names something different), after the "refresh" function I have a .click() function to update the users view (the one who just submitted).
<div id="wrapper">
<div id="menu">
<p class="welcome">Welcome, <b></b></p>
<p class="logout"><a id="exit" href="#">Exit Chat</a></p>
<div style="clear:both"></div>
</div>
<div id="chatbox">
<% #messages.each do |message| %>
<% if session[:email] == message.email %>
<!-- post to the right side -->
<div id="right-side">
<p>Email: <%= message.email %></p>
<p>Message: <%= message.message %></p>
</div>
<% else %>
<!-- post to the left side -->
<div id="left-side">
<p>Email: <%= message.email %></p>
<p>Message: <%= message.message %></p>
</div>
<% end %>
<% end %>
</div>
<form id="frm1" action="">
Message: <input id="message_input" type="text" name="fname"><br>
<!-- <input type="submit" value="Submit"> -->
<button name="submitmsg" type="submit" id="submitmsg">Try it</button>
</form>
</div>
<p id="demo"></p>
<script>
function retrieveMessages(){
var message;
$.ajax({
type:"GET",
url:"<%= get_messages_path %>",
dataType:"json",
data: {chat_id: <%= #message_info[:chat_id] %>,
last_message: <%= #messages.last.created_at.to_i %>},
success:function(data){
if (data != null){
console.log(data);
console.log(data.message);
message = data.message;
document.getElementById("chatbox").innerHTML += message + '<br>';
}
}
});
$.ajax({
type:"GET",
url:"<%= new_message_path %>",
dataType:"json",
success:function(data){
console.log("success!");
}
}
});
setTimeout(retrieveMessages, 2000);
}
$(document).ready(function(){
//get messages
setTimeout(retrieveMessages, 2000);
//send messages
$("#submitmsg").on('click', function(e){
e.preventDefault();
var x = document.getElementById("frm1"); //This is the form, and not the value of the textbox
var text = "";
var i;
for (i = 0; i < x.length ;i++) {
text += x.elements[i].value;
}
// document.getElementById("chatbox").innerHTML += text;
text_with_br = text + "<br>"
document.getElementById("chatbox").innerHTML += text_with_br;
document.getElementById("frm1").reset();
// console.log(document.getElementById("right"));
//Actual message is in 'text'
$.ajax({
url: "/messages",
method: "post",
data: { message: text,
chat_id: <%= #message_info[:chat_id] %>,
message_counter: <%= #message_info[:message_counter] + 1 %> },
dataType: "JSON",
success: function(response){
//do something,maybe notify user successfully posted their message
},
error: function(error){
console.log(error);
}
});
});
});
</script>
Then for the action action I have (actually the new action, and what I called #variable is actually #messages:
def new
# debugger
# Message.create(:chat_id => 5)
if session[:email].nil?
#user not logged in
redirect_to new_session_path(:message => "need to login")
else
#user = User.where(:email => session[:email]).first
#message_info = Hash(email: #user.email, message_counter: 0, chat_id: Message.last.chat_id + 1)
#messages = Message.where(:chat_id => #message_info[:chat_id]).to_a
respond_to do |format|
format.html
format.json
end
end
end
Then the action that executes the AJAX request to GET the most recent message (so that viewers from other browsers can view) is as follow...
def get
#new_message = Message.where(["created_at > ?", Time.at(params[:last_message].to_i)]).first
respond_to do |format|
format.html
# format.json {render json: #new_message}
format.json {render :json => #new_message.to_json}
end
end
UPDATE
#eggroll suggested code below, but I'm running into bootstrap issues
Starting from your code, below is my attempt at a solution. I've renamed elements in the hope that it will make this code more self-documenting and therefore easier for you to discern what I've done. It's not perfect, but it is working for me in Chrome, so hopefully it helps. I have assumed the starting chat_id is 1. If it is not, you can adjust it in the get_last_chat_id method in the controller.
(Note: There are a few lines of code that will be superfluous for you, but were necessary for me to be able to run this code inside of one of my existing apps. You should also know that I'm using Postgres, jQuery, Devise, HAML and Bootstrap 4 alpha.)
app/controllers/messages_controller.rb
class MessagesController < ApplicationController
before_action :authenticate_user!
before_action :set_current_user_email, only: [:index, :display_all_messages]
before_action :set_current_messages, only: [:display_all_messages]
def index
end
def get_last_chat_id
last_chat_id = Message.pluck(:chat_id).max
if last_chat_id
puts '*** LAST CHAT ID: ' + last_chat_id.to_s
else
last_chat_id = 0
puts '*** LAST CHAT ID: ' + last_chat_id.to_s
end
respond_to do |format|
format.json { render json: last_chat_id }
end
end
def save_new_message
new_message = Message.new do |msg|
msg.email = params[:email]
msg.message_text = params[:message_text]
msg.chat_id = params[:chat_id]
end
puts '*** NEW MESSAGE EMAIL: ' + new_message.email
puts '*** NEW MESSAGE TEXT: ' + new_message.message_text
puts '*** NEW MESSAGE CHAT ID: ' + new_message.chat_id.to_s
# Source: https://makandracards.com/housetrip-deck/16879-jquery-ajax-success-done-will-not-run-callbacks-if-request-is-json-but-the-response-is-empty-typical-200
respond_to do |format|
if new_message.save
puts '*** NEW MESSAGE WAS SAVED!!!'
format.json { render json: { ok: true }, status: :ok }
else
puts '*** NEW MESSAGE WAS NOT SAVED!!!'
format.json { render json: { ok: false }, status: :unprocessable_entity }
end
end
end
def display_all_messages
respond_to do |format|
if #current_messages
format.js { }
else
puts '*** THERE ARE NO MESSAGES TO DISPLAY!!!'
end
end
end
private
def set_current_messages
#current_messages = Message.by_created_desc
end
def set_current_user_email
#current_user_email = current_user.email
end
def message_params
params.require(:message).
permit(:email, :message_text)
end
end
app/models/message.rb
class Message < ActiveRecord::Base
scope :by_created_desc, -> { order(created_at: :desc) }
end
db/migrate/20160717000100_create_messages.rb
class CreateMessages < ActiveRecord::Migration
def change
create_table :messages do |t|
t.string :email, null: false
t.text :message_text
t.integer :chat_id, null: false
t.timestamps null: false
end
add_index :messages, :chat_id, unique: true
end
end
app/assets/javascripts/messages.js (Updated 2016-07-17)
var messagesRefresher;
$(document).ready(function(){
// AJAX error handling, outputting error messaging to the console
$(document).ajaxError(function (event, jqxhr, settings, thrownError) {
console.log('EVENT: ' + JSON.stringify(event, null, '\t'));
console.log('JQXHR: ' + JSON.stringify(jqxhr));
console.log('SETTINGS: ' + JSON.stringify(settings, null, '\t'));
console.log('THROWN ERROR: ' + thrownError);
});
messagesRefresher = setInterval(refreshMessages, 2000);
$('#new-message-create-btn').on('click', function(e){
e.preventDefault();
$('#new-message-create-btn').addClass('no-display');
$('#new-message-form-wrapper').removeClass('no-display');
});
$('#new-message-cancel-btn').on('click', function(e){
e.preventDefault();
$('#new-message-form-wrapper').addClass('no-display');
$('#new-message-create-btn').removeClass('no-display');
});
$('#new-message-submit-btn').on('click', function(e){
e.preventDefault();
var newMessageEmail = $('#new-message-email').val();
var newMessageText = $('#new-message-text').val();
console.log('*** NEW MESSAGE EMAIL: ' + newMessageEmail);
console.log('*** NEW MESSAGE TEXT:');
console.log(newMessageText);
getLastChatId('/messages/get_last_chat_id').done(function(lastChatId) {
var newMessageChatId = lastChatId + 1;
console.log('*** NEW MESSAGE CHAT ID: ' + newMessageChatId)
saveNewMessage('/messages/save_new_message?email=' + newMessageEmail + '&message_text=' + newMessageText + '&chat_id=' + newMessageChatId).done(function(data) {
console.log('*** MESSAGE # ' + newMessageChatId + ' SAVED!!!')
$('#new-message-form-wrapper').addClass('no-display');
document.getElementById('new-message-form').reset();
$('#new-message-create-btn').removeClass('no-display');
refreshMessages;
});
});
});
});
function refreshMessages() {
displayAllMessages('/messages/display_all_messages').done(function(data) {
console.log('*** MESSAGES REFRESHED!!!');
});
};
function getLastChatId(url) {
return $.ajax({
url: url,
type: 'get',
dataType: 'json'
})
.fail(function() {
alert('AJAX Get Last Chat Id Error');
});
};
function saveNewMessage(url) {
return $.ajax({
url: url,
type: 'get',
dataType: 'json'
})
.fail(function() {
alert('AJAX Save New Message Error');
});
};
function displayAllMessages(url) {
return $.ajax({
url: url,
type: 'get',
dataType: 'script'
})
.fail(function() {
alert('AJAX Display All Messages Error');
});
};
$(window).unload(
function(event) {
clearInterval(messagesRefresher);
}
);
app/views/layouts/application.html.haml (snippet) (Added: 2016-07-18)
.
.
.
%head
%meta{ charset: 'UTF-8' }
%meta{ name: 'viewport', content: 'width=device-width, initial-scale=1, shrink-to-fit=no' }
-# Derived from: http://v4-alpha.getbootstrap.com/getting-started/browsers-devices/
%meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }
= csrf_meta_tags
-# For page-specific meta tags
= content_for?(:meta_tag) ? yield(:meta_tag) : ""
%title Chatbox
= stylesheet_link_tag 'application'
= yield :page_stylesheet_link_tags
= javascript_include_tag 'application'
= yield :page_specific_javascript
.
.
.
app/views/messages/index.html.haml
- content_for :page_specific_javascript do
= javascript_include_tag 'messages.js'
#chatbox
%h1 Chatbox
= link_to 'New Message', 'javascript:;', id: 'new-message-create-btn', class: 'btn btn-sm btn-primary'
#new-message-form-wrapper.no-display
= form_tag messages_path, id: 'new-message-form' do
= hidden_field_tag 'new-message-email', #current_user_email
#new-message-form-label-wrapper
= label_tag 'new-message-text', 'Enter Your Message:'
#new-message-form-text-wrapper
= text_area_tag 'new-message-text', nil, rows: 6, cols: 70
#new-message-form-buttons-wrapper
= submit_tag 'Post Message', id: 'new-message-submit-btn', class: 'btn btn-sm btn-success'
= link_to 'Cancel', 'javascript:;', id: 'new-message-cancel-btn', class: 'btn btn-sm btn-secondary'
#display-messages-wrapper
#messages-column-left.pull-md-left
%h3 Messages From Others
#messages-other-users
#messages-column-right.pull-md-right
%h3 My Messages
#messages-current-user
app/views/messages/display_all_messages.js.haml
$('#messages-other-users').html('');
$('#messages-current-user').html('');
- #current_messages.each do |msg|
- if msg.email == #current_user_email
$('#messages-current-user').append("#{ escape_javascript render(partial: 'message', locals: { email: msg.email, message_created_at: msg.created_at, message_text: msg.message_text }) }");
- else
$('#messages-other-users').append("#{ escape_javascript render(partial: 'message', locals: { email: msg.email, message_created_at: msg.created_at, message_text: msg.message_text }) }");
app/views/messages/_message.html.haml
.message-wrapper
.message-attribution-wrapper
%span.message-attribution-label Posted by:
%span.message-attribution-text=email + ' on ' + message_created_at.strftime('%Y-%m-%d') + ' at ' + message_created_at.strftime('%I:%M:%S %p')
.message-text-wrapper
.message-label Message:
.message-text= message_text
app/assets/stylesheets/messages.scss (Updated 2016-07-17)
.no-display {
display: none !important;
}
#chatbox {
width: 90%;
margin: .5em auto;
}
#new-message-form-wrapper {
width: 48%;
padding: 1em;
border: 1px solid #ccc;
}
#new-message-form-label-wrapper > label {
font-weight: 700;
}
#new-message-form-buttons-wrapper {
margin-top: .5em;
}
#new-message-submit-btn {
margin-right: .3em;
}
#new-message-submit-btn,
#new-message-cancel-btn {
width: 8em;
}
#new-messages-form-wrapper,
#display-messages-wrapper {
margin-top: 1.5em;
}
#messages-column-left,
#messages-column-right {
width: 48%;
}
.message-wrapper {
width: 96%;
margin: 1em auto;
padding: .5em;
border: 1px solid #ccc;
}
.message-attribution-label,
.message-label {
font-weight: 700;
}
config/routes.rb
Rails.application.routes.draw do
resources :messages, only: [:index]
get 'messages/get_last_chat_id', to: 'messages#get_last_chat_id'
get 'messages/save_new_message', to: 'messages#save_new_message'
get 'messages/display_all_messages', to: 'messages#display_all_messages'
end
assests/javascripts/application.js (Added: 2016-07-18)
//= require jquery
//= require jquery_ujs
//= require jquery-ui
//= require bootstrap-sprockets
config/initialization/assets/rb (Added: 2016-07-18)
Rails.application.config.assets.precompile += %w( messages.js )
assets/stylesheets/application.scss (Added: 2016-07-18)
#import 'bootstrap_4a/bs_4a_variable_overrides';
#import 'bootstrap';
#import 'bootstrap_4a/bs_4a_customization';
app/assets/stylesheets/bootstrap_4a/bs_4a_variable_overrides (Added: 2016-07-18)
// http://v4-alpha.getbootstrap.com/getting-started/flexbox/
// Enabling flexbox means reduced browser and device support:
// Internet Explorer 9 and below do not support flexbox.
// Internet Explorer 10 has a few known quirks, requires using a prefix,
// and only supports the syntax from the old 2012 version of the spec.
$enable-flex: true;
app/assets/stylesheets/bootstrap_4a/bs_4a_customization (Added: 2016-07-18)
// http://v4-alpha.getbootstrap.com/getting-started/browsers-devices/
// As of Safari v8.0, fixed-width .containers can cause Safari
// to use an unusually small font size when printing.
// One potential workaround for this is adding the following CSS:
#media print {
.container {
width: auto;
}
}
body {
position: relative;
}
Gemfile (Added: 2016-07-18)
gem 'sass-rails', '~> 5.0'
gem 'jquery-rails'
gem 'jquery-ui-rails', '~> 5.0.5'
gem 'autoprefixer-rails', '~> 6.3.6'
gem 'bootstrap', '~> 4.0.0.alpha3'
UPDATE: .js.erb version of apps/views/messages/display_all_messages.js.haml
$('#messages-other-users').html('');
$('#messages-current-user').html('');
<% #current_messages.each do |msg| %>
<% if msg.email == #current_user_email %>
$('#messages-current-user').append("<%= escape_javascript render(partial: 'message', locals: { email: msg.email, message_created_at: msg.created_at, message_text: msg.message_text }) %>");
<% else %>
$('#messages-other-users').append("<%= escape_javascript render(partial: 'message', locals: { email: msg.email, message_created_at: msg.created_at, message_text: msg.message_text }) %>");
<% end %>
<% end %>
You can use the success callback, and append the div to the $("#chatbox"), following is the sample code for that.
jQuery.ajax({
type: 'GET',
url: loadUrl,
data: dataString,
dataType: 'html',
success: function(response) {
$("#chatbox").append("
<div id="left-side">
<p>Email:"+ response[:email]+"</p>
<p>Message:"+ response[:message]+"</p>
</div>"
}
});

How do I display a JSON status message in a view for a controller action success or failure in Rails?

I send certain data from a view to my controller. The controller action checks to see if the user has enough money, and if so, allows them to buy a tool of a certain price.
Otherwise, it doesnt update their tool.
Either way, I want to send a JSON response back to the view, to display.
How do I display these messages?
Here is my controller:
# Updates dollars and tool id when a user goes shopping in 'store'...
def update_tool
#user = User.find(params[:user_id])
#tool = Tool.find(params[:toolId])
price = #tool.price
# subtract tool price from users dollars
if #user.dollars <= price
respond_to do |format|
msg = { :status => "error", :message => "You do not have enough money!", :html => "<b>NO MONEY!</b>" }
format.json { render :json => msg }
end
else
#user.dollars = #user.dollars - price
# re-assign users tool_id reference ID
#user.tool_id = #tool.id
#store to database
#user.save
#sends a confirmation back to store
respond_to do |format|
msg = { :status => "success", :message => "You purchased a tool!", :html => "..." }
format.json { render :json => msg }
end
end
end
I want to take these status responses and use them to trigger events in my view,
something like this:
success: function(){
window.alert(':message');
},
error: function(){
window.alert(':message');
}
I'm just uncertain how to access the content of the json response message.
UPDATE:
Heres my AJAX request, with my success or failure functions:
function buyTool() {
$.ajax({
type: 'PATCH',
headers: {
'X-CSRF-Token': '<%= form_authenticity_token.to_s %>'
},
url: '<%= update_tool_path %>',
dataType: "JSON",
async: true,
data: {
'user_id' : <%= #user.id %>,
'toolId' : toolId
},
success: function(){
window.alert(":json");
},
error: function(){
window.alert(":json");
}
});
};
Its not working though-- My alert windows just actually displays the text ":json".
Do I need to pass the anon error: function that data?
My preference for this is to use flashes that are triggered by ajax. To do this use the following.
Add the following to your ApplicationController.rb
after_filter :flash_to_headers
#....
private
def flash_to_headers
return unless request.xhr?
response.headers['X-Message'] = flash_message
response.headers["X-Message-Type"] = flash_type.to_s
flash.discard # don't want the flash to appear when you reload page
end
def flash_message
[:error, :warning, :notice, :success].each do |type|
return flash[type] unless flash[type].blank?
end
end
def flash_type
[:error, :warning, :notice, :success].each do |type|
return type unless flash[type].blank?
end
end
And then add a flashes.js.coffee file with the following (This uses bootstrap styled flashes so just change the classes to something with your own styling)
show_ajax_message = (msg, type) ->
if (type == "error")
$("#flash-message").html "<div id='flash-#{type}' class='alert alert-danger'>#{msg}</div>"
else if (type == "success")
$("#flash-message").html "<div id='flash-#{type}' class='alert alert-success'>#{msg}</div>"
else if (type == "notice")
$("#flash-message").html "<div id='flash-#{type}' class='alert alert-info'>#{msg}</div>"
else if (type == "warning")
$("#flash-message").html "<div id='flash-#{type}' class='alert alert-warning'>#{msg}</div>"
$("#flash-#{type}").delay(5000).slideUp 'slow'
$(document).ajaxComplete (event, request) ->
msg = request.getResponseHeader("X-Message")
type = request.getResponseHeader("X-Message-Type")
show_ajax_message msg, type
Finally add somewhere for the flashes to render
# views/shared/_flashes.html.erb
<!-- Id is used for ajax flashes -->
<div id="flash-message">
<% if flash[:notice] %>
<div class="alert alert-success">
<%= flash[:notice] %>
</div>
<% elsif flash[:error] %>
<div class="alert alert-danger">
<%= flash[:error] %>
</div>
<% elsif flash[:alert] %>
<div class="alert alert-info">
<%= flash[:alert] %>
</div>
<% end %>
<% flash.discard %>
</div>
and render it from your layouts/application.html.erb
<%= render 'shared/flashes' %>
After this you can trigger flash messages as you would normally in rails and they will appear.

Categories