Stripe Token not passed with Javascript - javascript

I am using Stripe to:
Create Customer
Create Purchase
To do this, I am using this javascript code in my purchases.js (for my purchases controller):
$('document').ready(function() {
jQuery.externalScript = function(url, options) {
options = $.extend(options || {}, {
dataType: "script",
cache: true,
url: url
});
return jQuery.ajax(options);
};
$.externalScript('https://js.stripe.com/v1/').done(function(script, textStatus) {
Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'));
var purchase = {
setupForm: function() {
return $('.card_form').submit(function() {
$('input[type=submit]').prop('disabled', true);
if ($('#card_number').length) {
purchase.processCard();
return false;
} else {
return true;
}
});
},
processCard: function() {
var card;
card = {
name: $('#name_on_card').val(),
number: $('#card_number').val(),
cvc: $('#card_code').val(),
expMonth: $('#card_month').val(),
expYear: $('#card_year').val()
};
return Stripe.createToken(card, purchase.handleStripeResponse);
},
handleStripeResponse: function(status, response) {
if (status === 200) {
$('#purchase_stripe_token').val(response.id)
$('.card_form')[0].submit()
} else {
$('#stripe_error').text(response.error.message).show();
return $('input[type=submit]').prop('disabled', false);
}
}
};
return purchase.setupForm();
});
});
This works fine.
My issue is, I am trying to create a recipient. To do this, I basically use the same code, swapping out the respective fields for the recipient creation. BUT before I do this, I attempt to submit the form (essentially creating a purchase from a different controller), but in the second controller, it does not pass the stripe_token.
What would be the cause for this -- the code is basically the same? I'm not versed in JS.
UPDATE
THIS IS THE JAVASCRIPT THAT DOES NOT WORK.
$('document').ready(function() {
jQuery.externalScript = function(url, options) {
options = $.extend(options || {}, {
dataType: "script",
cache: true,
url: url
});
return jQuery.ajax(options);
};
$.externalScript('https://js.stripe.com/v1/').done(function(script, textStatus) {
Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'));
var bank = {
setupForm: function() {
return $('.recipient_form').submit(function() {
$('input[type=submit]').prop('disabled', true);
if ($('#account_number').length) {
bank.processBankAccount();
return false;
} else {
return true;
}
});
},
processBankAccount: function() {
var details;
details = {
country: $('#country').val(),
routingNumber: $('#routing_number').val(),
accountNumber: $('#account_number').val()
};
return Stripe.createToken(details, bank.handleStripeResponse);
},
handleStripeResponse: function(status, response) {
if (status === 200) {
$('#bank_stripe_token').val(response.id)
$('.recipient_form')[0].submit()
} else {
$('#stripe_error').text(response.error.message).show();
return $('input[type=submit]').prop('disabled', false);
}
}
};
return bank.setupForm();
});
});
THIS IS THE FORM.
<%= form_for([#user, #bank], role: "form", html:{class: "recipient_form"}) do |f| %>
<%= label_tag :country, "Country"%>
<%= text_field_tag :country, nil, name: nil, required: true %>
<%= label_tag :routing_number, "Routing Number"%>
<%= text_field_tag :routing_number, nil, name: nil, required: true %>
<%= label_tag :account_number, "Accounting Number"%>
<%= text_field_tag :account_number, nil, name: nil, required: true %>
<%= f.hidden_field :stripe_token %>
<%= f.submit value: "ADD" %>
<% end %>
THE CONTROLLER.
class BanksController < ApplicationController
def new
#user = User.find(params[:user_id])
#bank = #user.build_bank
end
def create
#user = User.find(params[:user_id])
#bank = #user.build_bank
stripe_token = params[:bank][:stripe_token]
begin
if !stripe_token.present?
raise "Stripe token not present. Cannot process transaction."
end
recipient = Stripe::Recipient.create(
:name => "Person Name",
:type => "individual",
:bank_account => stripe_token )
end
end
end
FORM DATA:
{"utf8"=>"✓",
"authenticity_token"=>"[FILTERED]",
"bank"=>{"stripe_token"=>""},
"commit"=>"ADD",
"user_id"=>"1680"}

Related

Rails cocoon gem: populate multiple fields

I'm using the cocoon gem on my rails app and I have two nested fields inside a form (categories, subcategories). Initially I'm showing the first one only and the second one is hidden. Each time the first select fields has subcategories the second field gets populated and appears.
The nested fields:
<div class="nested-fields">
<%= form.input :category, collection: #categories, as: :grouped_select, group_method: :children, :label => false, :include_blank => true, input_html: { id: "first_dropdown" } %>
<div class="show_hide">
<%= form.input :subcategories, label: false, :collection => [], :label_method => :name, :value_method => :id, required: true, input_html: { multiple: true, id: "second_dropdown" } %>
</div>
<div class="links">
<%= link_to_remove_association "Remove", form %>
</div>
</div>
This is the code to initially hide the second field
$('#first_dropdown').keyup(function() {
if ($(this).val().length == 0) {
$('.show_hide').hide();
}
}).keyup();
This is the code to show the second select input when the first select input has subcategories:
def select_item
category = Category.find params[:category_id]
render json: category.children
end
$('#first_dropdown').on('change', function() {
$.ajax({
url: 'categories/select_item?category_id=' + $(this).val(),
dataType: 'json',
success: function(data) {
let childElement = $('#second_dropdown');
if( data.length === 0 ) {
$('.show_hide').hide();
} else {
$('.show_hide').show();
}
childElement.html('');
data.forEach(function(v) {
let id = v.id;
let name = v.name;
childElement.append('<option value="' + id + '">' + name + '</option>');
});
}
});
});
Everything works well for the initial opened field. But when I add more fields and select a value inside any of the first select fields it populates all of the second fields according to that value. I think it's because I'm using specific id's for this and when I add more fields, all of them end up having the same id's. How can I set this up so I properly populate the second select field each time I add more nested fields to the form?
I'd give your selects classes instead of ids:
<div class="nested-fields">
<%= form.input :category, collection: #categories, as: :grouped_select, group_method: :children,
label: false, include_blank: true, input_html: { class: "first_dropdown" } %>
<% if f.object.category && f.object.category.sub_categories.length > 0 %>
<div class="show_hide">
<%= form.input :subcategories, label: false, collection: form.object.category.subcategories, label_method: :name,
value_method: :id, required: true, input_html: { multiple: true, class: "second_dropdown" } %>
</div>
<% else %>
<div class="show_hide" style="display: none;">
<%= form.input :subcategories, label: false, collection: [], label_method: :name,
value_method: :id, required: true, input_html: { multiple: true, class: "second_dropdown" } %>
</div>
<% end %>
<div class="links">
<%= link_to_remove_association "Remove", form %>
</div>
</div>
then find the second select relative to the first one adding this to a page specific js file:
$(document).on('turbolinks:load cocoon:after-insert', function(){
$('.first_dropdown').off('change').on('change', function(){
let $this = $(this);
let $second = $this.closest('.nested-fields').find('.second_dropdown');
$second.children().remove();
$second.closest('.show_hide').hide();
if ($this.val() !== '') {
$.ajax({
url: 'categories/select_item?category_id=' + $(this).val(),
dataType: 'json',
success: function(data){
if (data.length > 0) {
$second.append('<option value=""></option>');
data.forEach(function(v) {
let id = v.id;
let name = v.name;
$second.append('<option value="' + id + '">' + name + '</option>');
});
$second.closest('.show_hide').show();
}
}
})
}
});
});

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>"
}
});

Backbone.js is not rendering views properly

I am new at ruby on rails and backbone as well. I have an existing code in backbone and ruby which is working fine which creates some boards and one board have many lists and so on. I will provide code for that one also if needed. Now I am going to add some feature of categories like user can create and delete categories and each categories have many boards. Following data is displayed on browser from Database which is correct. I am not getting it properly why proper views are not displaying on browser.
Following is the code. Kindly suggest me some code changes so that it will work. It will be really appreciated. Thanks in advance.
/app/assets/javascripts/collections/categories.js
Kanban.Collections.Categories = Backbone.Collection.extend({
model: Kanban.Models.Category,
url: "/categories",
});
app/assets/javascripts/models/category.js
Kanban.Models.Category = Backbone.RelationalModel.extend({
urlRoot: "/categories",
relations: [{
type: Backbone.HasMany,
key: "boards",
relatedModel: "Kanban.Models.Board",
collectionType: "Kanban.Collections.Boards",
reverseRelation: {
key: "category"
}
}]
});
app/assets/javascripts/views/categories/categories_index.js
Kanban.Views.CategoriesIndex = Backbone.View.extend({
template: JST['categories/index'],
tagName: "section",
className: "categories-index",
events: {
"submit form.create_category": "createCategory"
},
initialize: function () {
var that = this;
that.collection.on("all", that.render, that);
},
render: function () {
var that = this;
var renderedContent = that.template({
categories: that.collection
});
that.$el.html(renderedContent);
console.log("categories ka render");
return that;
},
createCategory: function (event) {
event.defaultPrevented();
var that = this;
// get form attrs, reset form
var $form = $(event.target);
var attrs = $form.serializeJSON();
$form[0].reset();
var category = new Kanban.Models.Category();
// fail if no category name
if (!attrs.category.name) {
var $createContainer = $("div.create_category");
var $nameInput = that.$el.find("input.category_name");
$nameInput.hide();
$createContainer.effect("shake", {
distance: 9,
times: 2,
complete: function () {
$nameInput.show();
$nameInput.focus();
}
}, 350);
return false;
}
// save list
category.save(attrs.category, {
success: function (data) {
// category.get("users").add(Kanban.currentUser);
that.collection.add(category);
// keep focus on list input
that.$el.find("input.category_name").focus();
}
});
}
});
app/assets/javascripts/routers/main_routers.js
Kanban.Routers.Main = Backbone.Router.extend({
initialize: function (options) {
this.$rootEl = options.$rootEl;
},
routes: {
"": "index",
"/login": "login",
"categories/:id": "showCategoryBoards",
"boards/:id": "showBoardLists"
//"boards/:id": "deleteBoard"
},
index: function () {
var that = this;
var categoriesIndex = new Kanban.Views.CategoriesIndex({
collection: Kanban.categories
});
that.$rootEl.html(categoriesIndex.render().$el);
},
//index: function () {
//var that = this;
//var boardsIndex = new Kanban.Views.BoardsIndex({
//collection: Kanban.boards
//});
//that.$rootEl.html(boardsIndex.render().$el);
//},
showCategoryBoards: function (id) {
var that = this;
var category = Kanban.categories.get(id);
var categoryShow = new Kanban.Views.BoardIndex({
model: category
});
that.$rootEl.html(categoryShow.render().$el);
},
showBoardLists: function (id) {
var that = this;
var board = Kanban.boards.get(id);
var boardShow = new Kanban.Views.BoardShow({
model: board
});
that.$rootEl.html(boardShow.render().$el);
}
});
app/assets/javascripts/kanban.js
window.Kanban = {
Models: {},
Collections: {},
Views: {},
Routers: {},
initialize: function() {
var that = this;
that.$rootEl = $("#content");
Kanban.currentUser = new Kanban.Models.CurrentUser();
Kanban.currentUser.fetch({
success: function (response) {
// console.log("got user");
Kanban.category = new Kanban.Models.Category();
Kanban.category.fetch({
success: function (response) {
Kanban.boards = new Kanban.Collections.Boards();
Kanban.boards.fetch({
success: function (response) {
// console.log("got boards");
new Kanban.Routers.Main({ $rootEl: that.$rootEl });
Backbone.history.start();
}
});
},
error: function (response) {
// console.log("please log in");
}
});
}
});
}
};
$(document).ready(function(){
Kanban.initialize();
});
//BOARD DELETION METHOD... !
$(document).on("click", ".delete-icon", function() {
var board_id = $(this).parent().attr('id');
$.ajax({
url: "/api/boards/"+board_id,
type: 'DELETE',
success: function(result) {
$("#"+board_id).remove();
}
});
});
//LIST DELETION METHOD... !
$(document).on("click", ".list-delete-icon", function() {
var listId = $(this).parent().attr('id').replace(/list_/, '');
// alert(listId);
//var id = $("div").attr('id').replace(/button/, '');
$.ajax({
url: "/api/lists/"+listId,
type: 'DELETE',
success: function(result) {
alert("success!!!");
$("#list_"+listId).remove();
}
});
});
//card DELETION METHOD... !
app/assets/templates/categories/index.jst.ejs
<header class="categories-index">
<span class=""></span>
<h2>My Categories</h2>
</header>
<div>
<ul class="nav pull-left navbar-nav">
<% categories.each(function (category) { %>
<li id="<%= category.id %>" class="boxes">
<a href="/#categories/<%= category.id %>">
<%= category.escape("title") %>
</a>
<div class="delete-icon">
<span class="glyphicon glyphicon-trash">
</div>
</li>
<% }); %>
<li class="boxes">
<p>Create New Category</p>
<form class="create_category" id="myform">
<input type="text"
id="customInput"
class="category_name"
name="category[title]"
placeholder="Category Name ..." />
<input type="text"
class="category_description"
name="category[description]"
placeholder="Category Description ..." />
<input type="submit" value="Create Category" />
</form>
</li>
</ul>
</div>
/app/controllers/categories_controller
class CategoriesController < ApplicationController
# before_action :set_category, only: [:show, :edit, :update, :destroy]
respond_to :json
def index
#categories = Category.all
# respond_with(#categories)
# #categories = current_user.categories.includes(:boards)
end
def show
respond_with(#category)
end
def new
#category = Category.new
respond_with(#category)
end
def edit
end
def create
category = Category.new(params[:category])
if category.save
# board.members << current_user
render json: category, status: :ok
else
render json: category.errors, status: :unprocessable_entity
end
end
def update
#category.update(category_params)
respond_with(#category)
end
def destroy
#category.destroy
respond_with(#category)
end
private
def set_category
#category = Category.find(params[:id])
end
def category_params
params.require(:category).permit(:title, :description)
end
end
app/models
class Category < ActiveRecord::Base
attr_accessible :title, :description
has_many :boards, dependent: :destroy
end
app/views/categories/index.rabl
collection #categories
attributes :id, :title, :description
config/routes.rb
Kanban::Application.routes.draw do
devise_for :users
resources :users
# root to: "categories#index"
root to: "root#index"
resource :root, only: [:index]
resources :categories
# resource :session, only: [:new, :create, :destroy]
#get "login" => "sessions#new"
# get "logout" => "sessions#destroy"
# resources :users, only: [:show]
namespace :api do
resources :users, only: [:show] do
collection do
get :current
end
end
resources :boards, only: [:index, :show, :create, :update, :destroy]
resources :lists , except: [:edit] do
collection do
post :sort
end
end
resources :cards, except: [:edit] do
collection do
post :sort
end
end
resources :card_comments, only: [:index, :create, :destroy]
end
end
you're trying to initialize a "collection" instead of a "model" in your CategoriesIndex file:
initialize: function () {
var that = this;
that.collection.on("all", that.render, that);
which is being called from your Route file calling constructor with collection.
index: function () {
var that = this;
var categoriesIndex = new Kanban.Views.CategoriesIndex({
collection: Kanban.categories,
});
that.$rootEl.html(categoriesIndex.render().$el);
},
so, see your kanban.js file
it should be:
Kanban.categories = new Kanban.Collections.Categories();
Kanban.categories.fetch({

Send checkbox value to rails model field

I would like to send the value of my checkbox (true/false) to my rails model field (:taxable) on the state change of that button.
So far I have this, but I am not sure where to go from here
<script>
$(document).ready(function() {
$('.taxable').on('change', function(event) {
var checkbox = $(event.target);
var isChecked = $(checkbox).is(':checked');
/* */
});
});
</script>
<%= f.input :taxable, :as => :boolean, hint: "Is order taxable?",:id => 'taxable', :class => "taxable", :remote => true %>
Try this :
<script>
$(document).ready(function() {
$('.taxable').on('change', function(event) {
var checkbox = $(event.target);
var isChecked = $(checkbox).is(':checked');
$.ajax({
type: "POST",
url: "/url",
data: { params: { taxable: isChecked } },
success:(data) {
alert(data)
return true;
},
error:(data) {
return false
}
})
/* */
});
});
</script>
<%= f.input :taxable, :as => :boolean, hint: "Is order taxable?",:id => 'taxable', :class => "taxable", :remote => true %>

Categories