Correct way to use coffee script in rails5 - ruby-on-rails

I am developing my first rails application and trying to show/hide a particular web element in my view by clicking an another link in my page. Below is my view,
%p
= link_to 'Show additional details', "#", id: "secondary-link"
%table{:id => "secondary"}
%tr
%th Key
%th Value
where "secondary-link" is the link which shows / hides my table. When the page is loaded first, the table is made to hide by the following line in my application.css.scss
#secondary{
display: none;
}
Also, i have added this following script in my assets/javascripts/application.coffee
#myFunction = (variable) ->
$(document).on "page:change", ->
$('#secondary-link').click ->
$('#secondary').toggle()
But, the table is not shown when i click the link in my page. However, in my console if i enter "myFunction" i am getting the function
ƒ (variable) {
return $(document).on("page:change", function() {
return $('#secondary-link').click(function() {
return $('#secondary').toggle();
});
});
}
and i am getting an error when i enter myFunction()
Uncaught TypeError: Cannot read property 'on' of undefined
at myFunction (application.self-4e74630fd8894bca1e22a8d66c1d7ebfaa39edf4f40e058aae03fc788a9f6d94.js?body=1:3)
at <anonymous>:1:1
Your help is appreciated.

I am not sure why this issue but i fixed it by using jquery directly in my application.js file and deleted the coffee file. My js file has the below coding to handle the event.
$(document).ready(function() {
$('#secondary-link').click(function(event){
return $('#secondary').toggle();
});
});
I have stopped and restarted the server and this worked.

Related

best_in_place retains original values after update

I'm using best_in_place to do in-page editing of a table of data
in a ruby-on-rails app. The in-place editing works, but I have a
corner case that fails. A pair of items in the row (device_name,
generic_name) must be unique. If they are not unique, the server-side
code passes back a set of names with a changed generic_name to make
the pair unique. I use the following coffeescript to update the
display.
jQuery ->
$('.best_in_place[data-bip-object="full_dpoint"]').bind(
"ajax:success", (event, d) ->▫
return if ! d?
data = JSON.parse(d)
if ! data.dpoint?
return
else
item_to_edit = "#best_in_place_full_dpoint_" + data.dpoint.dpoint_id + "_generic_name"
$(item_to_edit).text(data.dpoint.generic_name)
)
This code works (IE it properly updates the page with the server-supplied
new generic name) , but if I then click back in the 'generic_name'
field, (to go into edit mode), the default edit text changes back
to what it was at the very beginning (page download time). I have
experimented with setting many different page elements to the new
generic name, including all of the following:
$(item_to_edit).attr('data-bip-original-content', data.dpoint.generic_name)
$(item_to_edit).attr('data-bip-value', data.dpoint.generic_name)
$(item_to_edit).attr('original-value', data.dpoint.generic_name)
$(item_to_edit).attr('bipValue', data.dpoint.generic_name)
$(item_to_edit).attr('bipvalue', data.dpoint.generic_name)
All to no avail. I have poked around in the dom trying to find where the original
value might be stored, but haven't found anything other than these.
Any ideas?
TIA.
Leonard
Try it
javascript:
$(document).ready(function () {
/* Activating Best In Place */
jQuery(".best_in_place").best_in_place();
var old_value;
$(document).on('change', '.not_abort', function (e) {
console.log(e.target.value);
old_value = e.target.value;
});
$(document).on('ajax:error', '.not_abort', function (e) {
console.log(e);
e.target.innerHTML = old_value;
$(e.target).data('bipOriginalContent', old_value);
});
})
view:
best_in_place #device_name, :email, class: 'not_abort'

JQuery mobile loading display

I created a loading display method like so
function customShowLoading(text){
$.mobile.loading( 'show', {
text: text,
textVisible: true,
theme: 'e',
html: ''
});
}
customShowLoading("Some message");
Every time the loading display shows, it just says 'loading' for its text
instead of the passed in parameter message. Any ideas why?
Have you tried switching the "text" variable to another name? It could be some variable name overlap.

Rendering dynamic scss-files with ajax, rails

As the title suggests, my main objective is to render a dynamic scss(.erb) file after an ajax call.
assets/javascripts/header.js
// onChange of a checkbox, a database boolean field should be toggled via AJAX
$( document ).ready(function() {
$('input[class=collection_cb]').change(function() {
// get the id of the item
var collection_id = $(this).parent().attr("data-collection-id");
// show a loading animation
$("#coll-loading").removeClass("vhidden");
// AJAX call
$.ajax({
type : 'PUT',
url : "/collections/" + collection_id + "/toggle",
success : function() {
// removal of loading animation, a bit delayed, as it would be too fast otherwise
setTimeout(function() {
$("#coll_loading").addClass("vhidden");
}, 300);
},
});
});
});
controller/collections_controller.rb
def toggle
# safety measure to check if the user changes his collection
if current_user.id == Collection.find(params[:id]).user_id
collection = Collection.find(params[:id])
# toggle the collection
collection.toggle! :auto_add_item
else
# redirect the user to error page, alert page
end
render :nothing => true
end
All worked very smooth when I solely toggled the database object.
Now I wanted to add some extra spices and change the CSS of my 50+ li's accordingly to the currently selected collections of the user.
My desired CSS looks like this, it checks li elements if they belong to the collections and give them a border color if so.
ul#list > li[data-collections~='8'][data-collections~='2']
{
border-color: #ff2900;
}
I added this to my controller to generate the []-conditions:
def toggle
# .
# .
# toggle function
# return the currently selected collection ids in the [data-collections]-format
#active_collections = ""
c_ids = current_user.collections.where(:auto_add_item => true).pluck('collections.id')
if c_ids.size != 0
c_ids.each { |id| #active_collections += "[data-collections~='#{id}']" }
end
# this is what gets retrieved
# #active_collections => [data-collections~='8'][data-collections~='2']
end
now I need a way to put those brackets in a scss file that gets generated dynamically.
I tried adding:
respond_to do |format|
format.css
end
to my controller, having the file views/collections/toggle.css.erb
ul#list<%= raw active_collections %> > li<%= raw active_collections %> {
border-color: #ff2900;
}
It didn't work, another way was rendering the css file from my controller, and then passing it to a view as described by Manuel Meurer
Did I mess up with the file names? Like using css instead of scss? Do you have any ideas how I should proceed?
Thanks for your help!
Why dynamic CSS? - reasoning
I know that this should normally happen by adding classes via JavaScript. My reasoning to why I need a dynamic css is that when the user decides to change the selected collections, he does this very concentrated. Something like 4 calls in 3 seconds, then a 5 minutes pause, then 5 calls in 4 seconds. The JavaScript would simply take too long to loop through the 50+ li's after every call.
UPDATE
As it turns out, JavaScript was very fast at handling my "long" list... Thanks y'all for pointing out the errors in my thinking!
In my opinion, the problem you've got isn't to do with CSS; it's to do with how your system works
CSS is loaded static (from the http request), which means when the page is rendered, it will not update if you change the CSS files on the server
JS is client side and is designed to interact with rendered HTML elements (through the DOM). This means that JS by its nature is dynamic, and is why we can use it with technologies like Ajax to change parts of the page
Here's where I think your problem comes in...
Your JS call is not reloading the page, which means the CSS stays static. There is currently no way to reload the CSS and have them render without refreshing (sending an HTTP request). This means that any updating you do with JS will have to include per-loaded CSS
As per the comments to your OP, you should really look at updating the classes of your list elements. If you use something like this it should work instantaneously:
$('li').addClass('new');
Hope this helps?
If I understood your feature correctly, actually all you need can be realized by JavaScript simply, no need for any hack.
Let me organize your feature at first
Given an user visiting the page
When he checks a checkbox
He will see a loading sign which implies this is an interaction with server
When the loading sign stopped
He will see the row(or 'li") he checked has a border which implies his action has been accepted by server
Then comes the solution. For readability I will simplify your loading sign code into named functions instead of real code.
$(document).ready(function() {
$('input[class=collection_cb]').change(function() {
// Use a variable to store parent of current scope for using later
var $parent = $(this).parent();
// get the id of the item
var collection_id = $parent.attr("data-collection-id");
show_loading_sign();
// AJAX call
$.ajax({
type : 'PUT',
url : "/collections/" + collection_id + "/toggle",
success : function() {
// This is the effect you need.
$parent.addClass('green_color_border');
},
error: function() {
$parent.addClass('red_color_border');
},
complete: function() {
close_loading_sign(); /*Close the sign no matter success or error*/
}
});
});
});
Let me know if my understanding of feature is correct and if this could solve the problem.
What if, when the user toggles a collection selection, you use jquery change one class on the ul and then define static styles based on that?
For example, your original markup might be:
ul#list.no_selection
li.collection8.collection2
li.collection1
And your css would have, statically:
ul.collection1 li.collection1,
ul.collection2 li.collection2,
...
ul.collection8 li.collection8 {
border-color: #ff2900;
}
So by default, there wouldn't be a border. But if the user selects collection 8, your jquery would do:
$('ul#list').addClass('collection8')
and voila, border around the li that's in collection8-- without looping over all the lis in javascript and without loading a stylesheet dynamically.
What do you think, would this work in your case?

Rails 3, bootstrap modal multiple layers of remote calling

I've been racking my head against this for 2 days now. I'm massively frustrated, and I can't seem to find any information on this with searching.
The issue. I'm using a :remote => true link to load some html from a different controller.
$('.managed_locations').bind('ajax:complete', function(evt, xhr, status){
$('#locations_modal').modal('show')
$('#locations_modal').html(xhr.responseText);
});
So it gets the html, dumps it into the bootstrap modal and displays the modal. This is working fine.
But inside of the modal I ALSO have a form which also uses :remote => true. Now to make life harder, when a button is pressed I clone the form and display it. So the user could have many forms.
Now the issue. Whenever the form is submitted it just loads it like a normal page. It's as if the :remote => true is being ignored. But this only in the modal. If I just load the modal controller by itself it works just fine. I also had this developed before using another jquery lightbox where it was working fine. I'm just switching in bootstrap for consistency.
So my initial thoughts are that the jquery_ujs.js isn't finding the new forms. So I added some code to output the form elements.
$("#log_events").click(function () {
$(document).find(".new_stored_physical_location").each(function() {
console.log( $(this).data() );
console.log( $(this).data('events') );
});
return false;
});
Which outputs in the console:
Object { type="html", remote=true}
Object { ajax:complete=[1]}
So I see that the events are being set in jQuery. Each of these forms has :remote => true and has the ajax event for when the request is complete. But it's just not doing an ajax request when I hit submit.
Is there something I'm missing that is required to make sure an ajax request will happen from the form???? The data() looks fine, the data('events') look fine. But is there some other event/binding that I need to look at?
The html that is loaded in from the modal right now is loading a layout. But i've done it both with a layout, without a layout. It's driving me nuts. Thanks for the help guys.
Edit: Some extra weirdness. The modal also loads some additional remote links, all of which are working correctly. It's only the form links which don't seem to work.
I got a solution. The big issue was within jquery_ujs.js Especially this line:
$(document).delegate(rails.formSubmitSelector, 'submit.rails', function(e) {
FYI, rails.formSubmitSelector = 'form'. So this code found all of the forms in the document, overwrote the submit with this function. But the issue was that once you loaded in some ajax, and that ajax contained a it wouldn't add this fancy event to it. You need to re-add it.
So this is what I did.
Inside of jquery_ujs there is a bunch of functions that are accessible outside of it using $.rails. So things like: $.rails.enableElement, $.rails.nonBlankInputs. And the code for the submit event was sitting around all willy nilly. It only executes once when the page is loaded. So I put that in a function addSubmitEvent():
// Add the form submit event
addSubmitEvent: function(element) {
//$(element) was before $(document) but I changed it
$(element).delegate(rails.formSubmitSelector, 'submit.rails', function(e) {
var form = $(this),
remote = form.data('remote') !== undefined,
blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector),
nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
if (!rails.allowAction(form)) return rails.stopEverything(e);
// skip other logic when required values are missing or file upload is present
if (blankRequiredInputs && form.attr("novalidate") == undefined && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
return rails.stopEverything(e);
}
if (remote) {
if (nonBlankFileInputs) {
return rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
}
// If browser does not support submit bubbling, then this live-binding will be called before direct
// bindings. Therefore, we should directly call any direct bindings before remotely submitting form.
if (!$.support.submitBubbles && $().jquery < '1.7' && rails.callFormSubmitBindings(form, e) === false) return rails.stopEverything(e);
rails.handleRemote(form);
return false;
} else {
// slight timeout so that the submit button gets properly serialized
setTimeout(function(){ rails.disableFormElements(form); }, 13);
}
});
}
This is basically the exact same code. But now it's $(element) instead of $(document). This was changed because now I can sniff for when the modal has loaded in the html. Then I can call:
$.rails.addSubmitEvent('#my_modal');
I then had an issue of it adding the event too many times from when I opened/closed the modal multiple times. So I just put a simple true/false if around it to call it once only.

jQuery / jQuery UI - $("a").live("click", ...) not working for tabs

So I have some jQuery UI tabs. The source code is as follows:
HTML:
<div class="tabs">
<ul>
<li>Ranges</li>
<li>Collections</li>
<li>Designs</li>
</ul>
<div id="ranges"></div>
<div id="collections"></div>
<div id="designs"></div>
</div>
jQuery:
$(document).ready(function() {
$(".tabs").tabs();
});
My problem is that I am trying to make each tab load a page into the content panel on click of the relevant link. To start with I am just trying to set the html of all the panels on clicking a link. From the code below, if I use method 1, it works for all links. However if I use method 2 it doesn't - but only for the links in the tabs (i.e. the labels you click to select a tab).
Method 1 (works for all links all the time, but would not be applied to links which are added after this is called):
$("a").click(function () {
$("#ranges, #collections, #designs").html("clicked");
});
Method 2 (works for all links which are not "tabified"):
$("a").live("click", function () {
$("#ranges, #collections, #designs").html("clicked");
});
Does anyone know why it is behaving like this? I would really like to get method 2 working properly as there may well be links which I need to add click events to which are added after the page is originally loaded.
Thanks in advance,
Richard
PS yes the function calls for .live and .click are both in the $(document).ready() function, before anyone says that that may be the problem - otherwise it wouldn't work at all..
Edit:
The solution I came up with involves an extra attribute in the anchors (data-url) and the following code (which outputs a 404 not found error if the page cannot be loaded). I aim to expand this over the next few weeks / months to be a lot more powerful.
$(".tabs").tabs({
select: function (event, ui) {
$(ui.panel).load($(ui.tab).attr("data-url"), function (responseText, textStatus, XMLHttpRequest) {
switch (XMLHttpRequest.status) {
case 200: break;
case 404:
$(ui.panel).html("<p>The requested page (" + $(ui.tab).attr("data-url") + ") could not be found.</p>");
break;
default:
$(ui.panel).html("<p title='Status: " + XMLHttpRequest.status + "; " + XMLHttpRequest.statusText + "'>An unknown error has occurred.</p>");
break;
};
});
}
});
I don't know if I understand what you are going for but basically you want to do something once a tab is clicked?
Here's the docs for setting up a callback function for selecting a tab.
EDIT: Don't know if that link is working correctly, you want to look at select under Events. But basically it is:
$("#tabs").tabs({
select: function(event, ui) { ... }
});
Where ui has information on the tab that was clicked.
jQuery UI tabs has an outstanding issue where return false; is used instead of event.preventDefault();. This effectively prevents event bubbling which live depends on. This is scheduled to be fixed with jQuery UI 1.9 but in the meantime the best approach is use the built in select event as suggested by #rolfwaffle.
Maybe the tabs() plugin that you are using is calling event.preventDefault(); (Reference) once it has created it's tabs.
Then it captures the click event and the bubbling stops, so it doesn't invoke your click-function. In jQuery this is done with
$(element).click(function(){
// Do stuff, then
return false; // Cancels the event
});
You'd have to alter the tabs() plugin code and remove this return false; statement, OR if you are lucky, the plugin might have an option to disable that behavior.
EDIT: Now I see you're using jQuery UI. Then you should check the documentation there, since it is an awesome plugin, it will do anything you want if you do the html right and pass it the right options.

Resources