Ruby on rails not loading Javascript - javascript

I had this working, but must have made a minor change somewhere that's stopped it working.
I have a call to Javascript when clicking on a button, but when I click the button nothing loads. I've gone over all parts of the code multiple times but cannot see where the failure is happening and there's no feedback in the logs so I have nothing to go on.
As I said it was working previously, but then stopped.
This is my button:
<%= button_tag t(".change"), class: "btn let-admin-change",
data: { title: t(".confirm_switch"),
cancel: t(".cancel"),
cta: t(".confirm_cta"),
plot: plot.id } %>
And my javascript file:
(function (document, $) {
'use strict'
$(document).on('click', '.let-admin-change', function (event) {
var dataIn = $(this).data()
var $changeContainer = $('.change-admin-letting-form')
$('body').append($changeContainer)
var $form = $('.edit_plot')
$changeContainer.dialog({
show: 'show',
modal: true,
width: 700,
title: dataIn.title,
buttons: [
{
text: dataIn.cancel,
class: 'btn',
click: function () {
$(this).dialog('destroy')
}
},
{
text: dataIn.cta,
class: 'btn-send btn',
id: 'btn_submit',
click: function () {
$.ajax({
url: '/plots/' + dataIn.plot,
data: $form.serialize(),
type: 'PUT'
})
$(this).dialog('destroy')
$changeContainer.hide()
}
}]
}).prev().find('.ui-dialog-titlebar-close').hide() // Hide the standard close button
})
})(document, window.jQuery)
And the form it's calling:
<div class="change-admin-letting-form">
<%= simple_form_for plot, url: phase_lettings_path(#phase), remote: true do |f| %>
<div>
<%= f.hidden_field :letter_type, value: :homeowner %>
<%= f.hidden_field :letable_type, value: :planet_rent %>
</div>
<div>
<h3><%= t(".are_you_sure", plot: plot) %></h3>
<p><%= t(".confirm_admin_swap").html_safe %></p>
</div>
<% end %>
</div>
Can anyone see where this is going wrong to stop the javascript from loading?
EDIT
If I add
<%= render "change_admin_letting_form", plot: plot %>
underneath the button then the form loads (but it loads multiple forms - one form for each instance of the button / each plot on the page), so I assume the issue is that the plot information is not being passed to the form? I'm not sure how to pass this information across.

I've managed to get this working by doing the following to get the correct information to the form:
I've changed the class of the form to take a variable:
<div class="hidden <%= plot_id %>">
And send the variable to the form by rendering the form from inside the block that loads each plot:
<% plots.each do |plot| %>
// some omitted code
<%= button_tag t(".change"), class: "btn let-admin-change",
data: { title: t(".confirm_switch"),
cancel: t(".cancel"),
cta: t(".confirm_cta"),
plot: plot.id } %>
<%= render "change_letter_type_form", plot_id: "change_letting_plot_#{plot.id}", plot: plot %>
<% end %>
And changed the part of the javascript that loads the form to search for the new plot-based class:
var $changeContainer = $('.change_letting_plot_' + dataIn.plot)
So now only the one form relevant to the selected plot loads, and the form works as required.
I have next to zero experience with javascript, so this probably isn't the 'proper' way to get this to work, but it works so I'll take it as it is.

Related

Rails destroy js.erb

Can you tell me please, how I could remove object before destroy method will be end. When I use next pattern, removing object happen when photo was delete, but it take 1 or 3 or more seconds.
_form(edit action)
<% listing.photos.each do |photo|%>
<%= image_tag photo.image.thumb, class: 'thumbnail', id: "test"%>
<%= link_to "remove", photo_path(photo),class: 'btn btn-primary', method: :delete, data: { confirm: 'Are you sure?' }, remote: true %>
destroy.js.erb
$('#test').remove();
How I can use this pattern
_form:
<div id="test_<%= photo.id %>">
<%= image_tag photo.image.thumb, class: 'thumbnail'%>
<%= link_to "remove", photo_path(photo),class: 'btn btn-primary', method: :delete, data: { confirm: 'Are you sure?' }, remote: true %>
Destroy.js.erb:
$('#edit_image_<%= #photo.id %>').remove();
If you want to remove the image from the DOM before its real destroying on server to avoid delay you can apply event.preventDefault() on the 'remove' button click.
This will allow you to rewrite the normal behavior of the 'remove' button.
Take a look at this example about performing some UI actions before the original event and then triggering it.
Also please notice that it's not a good idea to remove something from UI without being sure it's already removed in general. It's not clear enough for the user. So, maybe it would be better to hide the image at first and in case there will be a server error while destroying it you'll show it again and display some instructive message also.
UPD
Considering following markup
<div id="test_<%= photo.id %>">
<%= image_tag photo.image.thumb, class: 'thumbnail' %>
<%= link_to "remove", "#", class: 'remove btn btn-primary', data: { id: photo.id, url: photo_path(photo) } %>
</div>
another option is to rewrite remote: true with separate jQuery.ajax() call.
$('.btn.remove').click(function () {
var $target = $(this).parents('#test_' + $(this).data('id'));
$.ajax({
url: $(this).data('url'),
method: 'DELETE',
beforeSend: function() {
$target.hide() # hiding corresponding image
},
error: function () {
$target.show() # revert hiding on error
console.log("Sorry! Couldn't remove image.") # log message
}
})
})
There is a much cleaner way to do it without a js.erb template:
<div class="photo">
<%= image_tag photo.image.thumb, class: 'thumbnail'%>
<%= link_to "remove", photo_path(photo),class: 'destroy btn btn-primary', method: :delete, data: { remote: true, type: 'json', confirm: 'Are you sure?' } %>
<div>
Now just setup an ajax handler:
// app/assets/javascripts/photos.js
$(document).on('ajax:success', '.photo .destroy.btn', function(){
$(this).parent('.photo').remove();
});
And set your controller to return the correct response code.
class PhotosController
def destroy
#photo = Photo.find(params[:id])
#photo.destroy!
respond_to do |format|
format.json do
head :no_content
end
end
end
end
This keeps your client side logic in app/assets/javascripts where it can be cached and minified instead of spreading it around in a bunch of glorified script tags.

Forms with Select2 are duplicated when clicking back button on browser in Rails 5

_header.html.erb (for forms part)
<%= form_for home_path, class: 'home', role: 'search', method: :get do |f| %>
<div class="form-group" style="display:inline;">
<div class="input-group input-group-md">
<%= text_field_tag :q, params[:q], placeholder: ... ,class: 'form-control hideOverflow', type: "search" %>
<%= select_tag "category", options_from_collection_for_select(...),include_blank: true, class: 'form-control hideOverflow', type: "search" %>
<%if logged_in? %>
<%= select_tag "location", options_for_select([...], ...),class: 'form-control hideOverflow', type: "search" %>
<% else %>
<%= select_tag "location", options_for_select([...], ...),class: 'form-control hideOverflow', include_blank: true, type: "search" %>
<% end %>
<span class="input-group-addon"><%= submit_tag "Search", class: "btn-transparent"%></span>
</div>
</div>
<% end %>
JS codes
<script>
$( document ).on('turbolinks:load', function() {
$('select#category').select2({
width: '60%',
dropdownAutoWidth : true,
placeholder: "Choose a category",
maximumSelectionLength: 3
});
$('select#location').select2({
width: '40%',
dropdownAutoWidth : true,
minimumResultsForSearch: Infinity
});
});
</script>
Glitch or rendering issues (click links to view the images)
normal
after I click back button from the browser
Can someone help me out why? Plus, my search forms are in the navigation bar in header partial file.
If I get rid of $(...).select in the script, everything works fine... I think there is a problem with select.js
Answered here:
https://stackoverflow.com/a/41915129/5758027
I have use this solution in my own code:
$(document).on('turbolinks:before-cache', function() { // this approach corrects the select 2 to be duplicated when clicking the back button.
$('.select-select2').select2('destroy');
$('.select-search-select2').select2('destroy');
} );
and an observer:
$(document).ready( ready ); //... once document ready
$(document).ajaxComplete( ready ); //... once ajax is complete
$(document).on('turbolinks:load', ready ); //... once a link is clicked
function ready() {
$(".select-search-select2").select2({
theme: "bootstrap",
language: 'es',
allowClear: true
});
$(".select-select2").select2({
theme: "bootstrap",
language: 'es',
minimumResultsForSearch: Infinity,
allowClear: true
});
};
isn't clearing the cache all the time makes using turbolinks practically pointless?
how about something like this instead?
$(document).on('turbolinks:before-cache', function(e) {
return $('.form-control.select2').each(function() {
return $(this).select2('destroy');
});
});
I wasn't able to solve this rendering issue (still waiting for the correct answer!) but if anyone has a similar problem like me, try thinking outside of box. Here is my hack: I added a back button in my applicaiton.
Get the full url path
# get the previous url
def save_previous_page
session[:return_to] = request.fullpath
end
Show the back button only if the page is NOT home page or search page
<% if session[:return_to] != request.fullpath%>
<%= link_to session.delete(:return_to) || request.fullpath, class: 'back-button' do%>
<i class="fa fa-arrow-circle-left" aria-hidden="true"></i>
<%end%>
<% end %>
Meanwhile, I am still waiting and trying to fix rendering issue...
FIXED THE ISSUE
Simply add this code in your .js file
Turbolinks.clearCache();
This is most likely to be some asset inconsistency, you should check your app\views\layouts folder for files with duplicated declarations of wither jQuery, jQuery UJS or Turbolinks. Check all <script> tags of your pages, and if you are declaring both on layout folder and in internal views the same scripts. If that is not the case, check for render, yield or build calls
Simple solution, don't run the select2 builder on stuff you would rather it not run on.
$("select#category:not(.select2-container):not(.select2-hidden-accessible)").select2();

Post GeoJSON data with form data to Rails 4 app

I've got a web page that creates an "Activity." Using simple form, the user fills in some basic information about the activity:
#new.html.erb
<%= simple_form_for #activity do |f| %>
<%= f.input :name, label: "Activity name", input_html: { id: "create_activity_name_field" }, hint: "At least 7 characters" %>
<%= f.input :type, label: "Activity type", input_html: { id: "create_activity_type_field" }, as: :select, :include_blank => false, collection: Activity.types.keys.to_a %>
<%= f.input :date_field, input_html: { id: "create_activity_date_field" }, as: :string %>
<%= f.input :time_field, input_html: { id: "create_activity_time_field" }, as: :string %>
<% f.button :submit, :class => "btn btn-primary" -%>
<% end %>
However, I am also using MapBox to display a map. The user will click the points on the map to make a route for the activity. When the user is done drawing the route, I can then extract GeoJSON data for this route.
I would like to use javascript to POST the GeoJSON data with the form data. On the backend, I'll have rails convert the GeoJSON to a KML file, and use Paperclip to upload it to Amazon S3. The rest of the data can be saved, and Paperclip will give me a URL to the KML file which I can associate with the activity.
I'm a Rails noob, and I can't figure out how to do this nor track down anything to get me over this hurdle. I considered using javascript's FormData. I was very attracted to this approach, because implementation looks so simple, but apparently it can only really handle key/value pairs, not complex nested JSON data.
Any suggestions or strategies greatly appreciated. And greatly appreciated++ if anyone can give detailed answers because, like I said, I'm a few weeks fresh to rails and web development.
What you probably want to do here is to prevent the default behavior of your form using in JS :
$('form').submit(function(e) {
e.preventDefault();
var form = $('form');
var form_data_json = JSON.stringify(form.serializeArray());
form_plus_geojson = form_data_json.concat(Geojson);
Grab your data in JS ( this should be easy but you dont really say how do you extract the data from mapbox ).
Then send back the data via AJAX ( don't forget validation on either end )
if (my_data_is_not_good/empty/whatever) {
console.log('nop')
} else {
$.ajax({
type: 'post',
url: Yourcontrollerpostaction,
data: form_plus_geojson,
dataType: "JSON",
Once the data has been sent you want to resume the normal form submission post behavior and submit the form.
complete: function() {
this.off('submit');
this.submit();
}
});
}
});
Then you just need to parse the data in your controler.
def post_controller
if request.xhr?
my_hash = JSON.parse(params[:mydata])
else
normal post behavior
end
end
Everything is quick pseudocode you might have to fix some stuff as i haven't tested it but it should work.
Obviously now that we are doing everything in ajax you don't need the complet function call back and can remove that part.
Kudos to #jDay for pointing me in the right direction -- specifically with concatenating the JSON data. However, I couldn't get his specific solution to work on my app, and I went trying another method which, in my view, was a little more straightforward.
I used the Rails simple_form_for gem to make the form, specifying a remote post call:
#new.html.erb
<%= simple_form_for #activity, :url => '/activities', :method => :post, :remote => true, html: {id: :activity_create_form, "data-type" => :json} do |f| %>
<%= f.input :name, label: "Activity name", input_html: { id: "create_activity_name_field" }, hint: "At least 7 characters" %>
<%= f.input :type, label: "Activity type", input_html: { id: "create_activity_type_field" }, as: :select, :include_blank => false, collection: Activity.types.keys.to_a %>
<%= f.input :date_field, input_html: { id: "create_activity_date_field" }, as: :string %>
<%= f.input :time_field, input_html: { id: "create_activity_time_field" }, as: :string %>
<%= f.button :submit, :class => "btn btn-primary", input_html: {id: "create_activity_submit_btn"} -%>
<% end %>
Then I used jQuery to to hijack the form submit button and append an invisible form element bearing the GeoJSON data. The data, however, had to be stringified so it could be sent:
#activity_new.js
$("form").submit( function (e) {
e.preventDefault();
$('<input />')
.attr('type', 'hidden')
.attr('name', 'routeGeoJson')
.attr('value', JSON.stringify(polyline.toGeoJSON()))
.appendTo(this);
return true;
});
(Here, "polyline" is a Leaflet L.polyline object that I used to draw on the map.)
In the controller, you'll get params like this:
{"utf8"=>"✓", "activity"=>{"name"=>"A jaunt in the woods", "type"=>"walk",
"date_field"=>"2015-09-08", "time_field"=>"07:00"},
"routeGeoJson"=>"{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-118.38855654001236,33.95361274499209],[-118.36624056100845,33.97681937760982]]}}",
"commit"=>"Create Activity", "controller"=>"activities",
"action"=>"create"}
Since the "routeGeoJson" data is still in string form, I reassembled the GeoJSON data by parsing it using the JSON gem (which I believe is included by default in Rails 4):
def create
geojson = JSON.parse(params[:routeGeoJson])
# do something...
end
And you get your GeoJSON hash:
{"type"=>"Feature", "properties"=>{},
"geometry"=>{"type"=>"LineString",
"coordinates"=>[[-118.42014223337173, 33.98407904797006],
[-118.37825685739517, 33.956175751601826]]}}

Sidebar and Main page persistence - Rails

I am using a sidebar that searches and generates a list (within the sidebar) I want the main page to remain unchanged when searching with the sidebar. I believe this requires some JS, but I know nothing about JS.
my navbar is in a div _navbar.html.erb
main page is basically any other page being generated
here is my code: https://github.com/nrkfeller/ratingapplication
You need to use AJAX with rails. Here is how it may work for you:
Add a :remote => true to your form and :'data-update-target' => 'update-container' to specify where you want the search results to go. You might want to avoid using the courses_path, but use form_tag({:controller => courses, :action => 'search'} to directly state where you want the form to be submitted.
<%= form_tag courses_path, method: :get, :remote => true ,class: "navbar- form navbar-right", :'data-update-target' => 'update-container' , role: "search" do %>
<p>
<%= text_field_tag :search, params[:search], class: "form-control" %>
<%= submit_tag "Search",:disable_with => 'Please wait...', name: nil, class: "btn btn-default" %>
</p>
<% end %>
Add this to your sidebar. This is where the partial with the results will come:
<div id="update-container"></div>
Add the javascript`` to put it where it is supposed to be when the request finishes:
<script>
$(function() {
$('form[data-update-target]').on('ajax:success', function(evt, data) {
var target = $(this).data('update-target');
$('#' + target).html(data);
});
});
Add a partial named _search_results.html.erb <- this is where your results go.
<!-- arbitrary code -->
<%= #results.each do |result| %>
<%= result %>
In your controller:
def search
#results= #your search code
render :partial => 'search_results', :content_type => 'text/html'
end
This is will get the functionality you wanted. Beware, this is more of a pesudocode than an exact implementation of what you want to do. You have to fill in the gaps.
I hope I was helpful!

RoR how to submit form with :remote=>true and then execute javascript code

I want to be able to submit a form remotely (so the page does not refresh) and then have some javascript code get executed. Here's what I got so far
<script>
$(function() {
// Subscribe to receive messages!
var client = new Faye.Client('http://localhost:9292/faye');
// Our own private channel
var private_subscription = client.subscribe('<%=request.path + #id1.to_s + #id2.to_s%>',
function(data) {
$('<p></p>').html(data.username + ": " + data.msg).appendTo('#chat_room');
});
// Handle form submission to publish messages.
$('#new_message').submit(function(){ // THIS LINE GIVING TROUBLE
client.publish('<%=request.path + #id1.to_s + #id2.to_s%>', {
username: '<%= #user.fname+' '+#user.lname%>',
msg: $('#message_message').val()
});
// Clear the message box
$('#message_message').val('');
return false;
});
});
</script>
<div class="chat_container">
<div id="chat_room">
<p>Chat with <%= #other.fname+' '+#other.lname%> </p>
</div>
<%= simple_form_for Message.new, :id => "new_message",:controller => "messages", :action => "create", :remote => true do |f| %>
<%= f.text_field :message, :autocomplete => "off" %>
<%= f.hidden_field :sender_id, :value => #user.user_id %>
<%= f.hidden_field :receiver, :value => #other.user_id %>
<%= f.hidden_field :sent_at, :value => Time.now %>
<%= f.submit "Send", class: "btn btn-medium btn-primary" %>
<% end %>
</div>
If I change the name of my javascript function "#new_message" to anything else, the form submits properly into my database, but I lose the chatroom functionality. (The code that transmits the message to all clients in that chat room, via FAYE, and clears the text_box will not get called.)
If I keep the name of my javascript function "#new_message", the message will get transmitted to all clients in the chatroom, and the textbox will get cleared, but nothing will get stored into my database.
What can I do so that I can accomplish both the data storage and the chatroom functionality?
EDIT:Attempted this to no avail
$('#new_message').bind('ajax:success', function() {
client.publish('<%=request.path + #id1.to_s + #id2.to_s%>', {
username: '<%= #user.fname+' '+#user.lname%>',
msg: $('#message_message').val()
});
// Clear the message box
$('#message_message').val('');
return false;
});
});
I think you just need to hook into one of the ajax callbacks for the form submit:
$("#new_message").bind("ajax:success", function(evt, data, status, xhr) {
// Your client code and message clearing code
});

Categories

Resources