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>"
}
});
Related
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.
I have problem with my HTML sortable update in rails 6. I drag and drop some portfolio images through web page and that is working good but when i want to update the web page with updated position i got NoMethodError (undefined method `each' for nil:NilClass):
I use this script for HTML Sortable
https://github.com/jordanhudgens/devcamp-portfolio/blob/master/app/assets/javascripts/html.sortable.js
portfolio_controller:
class PortfoliosController < ApplicationController
before_action :set_portfolio_item, only: [:edit, :show,:update, :destroy]
layout "portfolio"
access all: [:show, :index, :angular], user: {except: [:destroy, :new, :create, :update, :edit]}, site_admin: :all
def index
#portfolio_items = Portfolio.by_position
end
def sort
params[:order].each do |key, value|
Portfolio.find(value[:id]).update(position: value[:position])
end
head :ok
end
def angular
#angular_portfolio_items = Portfolio.angular
end
def new
#portfolio_item = Portfolio.new
3.times { #portfolio_item.technologies.build }
end
def create
#portfolio_item = Portfolio.new(portfolio_params)
respond_to do |format|
if #portfolio_item.save
format.html { redirect_to portfolios_path, notice: 'Your Portfolio item is now live.' }
else
format.html { render :new }
end
end
end
def edit
end
def update
respond_to do |format|
if #portfolio_item.update(portfolio_params)
format.html { redirect_to portfolios_path, notice: 'The Record successfully updated.' }
else
format.html { render :edit }
end
end
end
def show
end
def destroy
#Destroy/delete the record
#portfolio_item.destroy
#Redirect
respond_to do |format|
format.html { redirect_to portfolios_url, notice: 'Record was removed' }
end
end
private
def set_portfolio_item
#portfolio_item = Portfolio.find(params[:id])
end
def portfolio_params
params.require(:portfolio).permit(:title,
:subtitle,
:body,
technologies_attributes: [:name]
)
end
end
routes.rb:
Rails.application.routes.draw do
devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout', sign_up: 'register'}
resources :portfolios, except: [:show] do
put :sort, on: :collection
end
get 'angular-items', to: 'portfolios#angular'
get 'portfolio/:id', to: 'portfolios#show', as: 'portfolio_show'
get 'about-me', to: 'pages#about'
get 'contact', to: 'pages#contact'
resources :blogs do
member do
get :toggle_status
end
end
root to: 'pages#home'
end
Here is my portfolio.js :
var ready, set_position;
ready = void 0;
set_position = void 0;
set_position = function() {
$(".card").each(function(i) {
$(this).attr('data-pos', i + 1);
});
};
ready = function() {
set_position();
$('#sortable').sortable();
$('#sortable').sortable().bind('sortupdate', function(e, ui) {
var updated_order;
updated_order = [];
set_position();
$(".card").each(function(i) {
updated_order.push;
({
id: $(this).data('id'),
position: i + 1
});
});
$.ajax({
type: 'PUT',
url: '/portfolios/sort',
data: {
order: updated_order
}
});
});
};
$(document).ready(ready);
_portfolio_item.html.erb :
<div class="card" data-id="<%= portfolio_item.id %>">
<%= image_tag portfolio_item.thumb_image unless portfolio_item.thumb_image.nil? %>
<p class="card-text">
<span>
<%= link_to portfolio_item.title, portfolio_show_path(portfolio_item) %>
</span>
<%= portfolio_item.subtitle %>
</p>
</div>
Thank you for your helps!
The only place I see an each statement is in the sort method of your PortfoliosController.
params[:order].each
You need to set your params[:order] or prevent this error from happening by checking if it is set. You could use the save navigation operator:
params[:order]&.each
Or you could check if the order parameter is set with the present? method.
if params[:order].present?
params[:order]&.each
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");
});
I am following section 4 (Server Side Concerns) to set up ajax on a page. I've copied the tutorial text completely (replacing the model names with my own) and it creates and saves my "Participants" record, but does not automatically refresh the ajax partial.
This is the error I get...which looks like it's referrring to my create.js.erb
ActionView::Template::Error ('nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.):
1: $("<%= escape_javascript(render #participant) %>").appendTo("#participants");
2: // $('#participants').html("<%= j (render #participants) %>");
app/views/participants/create.js.erb:2:in `_app_views_participants_create_js_erb___1675277149181037111_70181034249880'
Here's my code
class ParticipantsController < ApplicationController
def new
#participant = Participant.new
#participants = #participants.recently_updated
end
def create
#participant = Participant.new(participant_params)
respond_to do |format|
if #participant.save
format.html { redirect_to #participant, notice: 'Helper Invited!' }
format.js {}
format.json { render json: #participant, status: :created, location: #participant }
else
format.html { render action: "new" }
format.json { render json: #participant.errors, status: :unprocessable_entity }
end
end
end
_form.html.erb
<ul id="participants">
<%= render #participants %>
</ul>
<%= form_for(#participant, remote: true) do |f| %>
<%= f.label :email %><br>
<%= f.email_field :email %>
<%= f.submit 'SUBMIT' %>
<script>
$(document).ready(function() {
return $("#new_participant").on("ajax:success", function(e, data, status, xhr) {
return $("#new_participant").append(xhr.responseText);
}).on("ajax:error", function(e, xhr, status, error) {
return $("#new_participant").append("<p>Oops. Please Try again.</p>");
});
});
</script>
<script>
$(function() {
return $("a[data-remote]").on("ajax:success", function(e, data, status, xhr) {
return alert("The helper has been removed and notified.");
});
});
</script>
_participant.html.erb
<li >
<%= participant.email %> <%= link_to participant, remote: true, method: :delete, data: { confirm: 'Are you sure?' } do %>REMOVE<% end %>
</li>
create.js.erb
$("<%= escape_javascript(render #participant) %>").appendTo("#participants");
destroy.js.erb
$('#participants').html("<%= j (render #participants) %>");
It's on line 2 of your create.js.erb file, it's the missing #participants not the #participant.
You've commented the line out in JS, but the ERB is still going to be processed by Rails, so it's still trying to do the render #participants
Update
For future... it's the last line of that error that's the key:
app/views/participants/create.js.erb:2
See the 2 at the end, that's telling you which line the error happened on, and so that's where you need to focus when looking for the problem.
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.