Rails 3 Passing a variable from link_to to javascript - javascript

If a user is on a form page, I am trying to throw a confirm message if they navigate away without clicking the update button.
The coffeescript code I am using is as follows
# used to detect if a user is navigating off a page
if form_click == true
window.onbeforeunload = ->
"You have unsaved data! do you really want to exit?"
If I use if 1 ==1 or 1 ==2 as a test case, it works perfectly fine. However, I am having diffculty in sending a variable from the link_to code to set form_click.
I have tried the following
<%= f.button :button, :class => 'btn-primary',
data: {form_click: true},
remote: true %>
But I am not able to pass the variable to the coffeescript code. I am definitely not proficient with javascript and coffeescript, as this probably shows, and would be grateful of any advice on how to resolve this would be much appreciated

Have a look at the generated HTML. I suspect that the link_to is creating something similar to:
<button class="btn-primary" data-form_click="true" data-remote="true"></button>
If so then the coffeescript you need to have would be something like:
$('data-form_click').click( ->
if $(this).attr('data-form_click') == 'true'
window.onbeforeunload = ->
confirm("You have unsaved data! Do you really want to exit?")
)
The code above is off the cuff, but the main point is that the attribute on the button is a string, not a boolean, so you need to adjust accordingly.
Use the JS debugging tools to place a breakpoint so you can inspect the value of form_click is also another way to see what you should be comparing to.

Related

Updating instance variables through AJAX call in Rails to display in form

Here's what I'm trying to do:
The user pastes a URL.
The input box that the user pastes in has an :onpaste that triggers urlPasted() function.
urlPasted() function submits the form that input box is in, which does an AJAX call to a custom function named lookup_profile.
In the controller, lookup_profile function does some web requests, and then updates some instance variables.
Once those variables are updated (takes ~5 seconds), the view has a function that waits 20 seconds and updates textboxes on the modal with the results of those instance variables.
Here's what I have thus far in the view:
<%= form_tag url_for(:controller => 'users', :action => 'lookup_profile'), id: "profileLookupForm", :method => 'post', :remote => true, :authenticity_token => true do %>
<div class="form-group row">
<div class="col-sm-12">
<%= text_field_tag "paste_data", nil, onpaste: "profileURLPasted();", class: "form-control"%>
</div>
</div>
<% end %>
<script type="text/javascript">
function profileURLPasted() {
// Once the user pastes data, this is going to submit a POST request to the controller.
setTimeout(function () {
document.getElementById("profileLookupForm").submit();
}, 100);
setTimeout(function () {
prefillForm();
}, 20000);
};
function prefillForm() {
// Replace company details.
$('#companyNameTextBox').val("<%= #company_name %>");
};
</script>
Here's what the controller looks like:
def lookup_profile
# bunch of code here
#company_name = "Random"
end
Now here's the problem I have. When the user pastes the data, it submits perfectly to the custom_action lookupProfile. However, after lookupProfile runs its code, rails doesn't know what to do afterwards. By that, I mean it gives me this error:
Users#lookup_profile is missing a template for this request format and
variant. request.formats: ["text/html"] request.variant: []
When in fact, I actually have a file at views/users/lookup_profile.js.erb. For some reason, it's trying to render the HTML version. I don't know why.
Secondly, I've tried putting this in the controller towards the end:
respond_to do |format|
format.js { render 'users/lookup_profile'}
end
but that results in this error:
ActionController::UnknownFormat
Any help would be greatly appreciated. I just want the custom function to run, update the instance variables, and let me update the current form with that data.
Here's another stackoverflow reference of something similar I'm trying to do: Rails submitting a form through ajax and updating the view but this method doesn't work (getting the actioncontroller error)
* EDIT 1 *
Ok, so I fixed the ActionController error by replacing my form_tag with:
<%= form_tag(lookup_profile_users_path(format: :js), method: :post, :authenticity_token => true, id: 'profileLookupForm', remote: true) do %>
But now it's actually rendering the actual javascript into the view, and I don't want that. I simply want to be able to access the instance variables that were updated in the lookup_profile action, not display the view.
* EDIT 2 *
So I think my problem comes down to this: Placing a button in the form and submitting from IT is different than my javascript code that submits the form. If I can figure out what's up with that, then I think I may be in good shape.
You are mixing a few things there. First of all, instead of doing document.getElementById("profileLookupForm").submit() you should do an ajax request, I guess the submit() method ignores the remote: true directive from rails.
So, change the submission to:
form = getElementById("profileLookupForm");
$.post(form.action, {paste_data: this.value}, 'script')
// form.action is the url, `this` is the input field, 'script' tells rails it should render a js script
That way the request is done async and the response does not replace the current page.
Now, what I think you are mixing is that #company_name won't change with that ajax request. When you render the form and everything else, #company_name is replaced with the actual value IN THAT MOMENT and will not change after your post request since the reference is lost. So this line:
$('#companyNameTextBox').val("<%= #company_name %>");
will be
$('#companyNameTextBox').val("");
al the time.
What you want is to respond with a script that updates the field with the value that you set to #company_name (also, waiting arbitrarilly X seconds is a really bad practice).
So, instead of responding with:
format.js { render 'users/lookup_profile'}
create a view lookup_profile.js with the code that you want to execute
$('#companyNameTextBox').val("<%= #company_name %>");
here, #company_name will actually be the value obtained with those requests you told before, the script is generated at the moment and excecuted as a response of the request.

Rails 4: Passing variables to javascript

I've tried numerous methods and followed Ryan Bates' guide but no matter what I do I still get undefined.
application.html.erb
<body>
<%= content_tag :div, id: 'trackers', data: {trackers: User.count} do %>
<% end %>
</body
application.js.erb
var datadump = ($('#trackers').data('trackers'))
console.log(datadump)
//returns undefined in console
Viewing page source I can see the variable
<div data-trackers="2" id="trackers">
I'm passing User.count for now just to keep it simple but I'll need to be passing #trackers_count which is instantiated in a before_action in the application controller. I should be able to sort that out though once I figure out the problem here. Any suggestions?
UPDATE - I've simplified all variables down to just trackers, instead of trackers_count to prevent any errors from syntax and updated code here to reflect that.
ANSWER UPDATE - I selected the correct answer because if you want to pass any variables ASIDE FROM CURRENT_USER those methods worked perfectly. However, you can't access anything with current_user in JS because it loads before the window, so it can't know who the current_user is. It's a real pain but I just did it the long way and accessed the info I need through passing in json and an ajax requests.
I used to do:
var my_var = <%= User.count %>
console.log(my_var)
If it is an integer this works just fine.
If, however, you want to pass objects, then use:
var my_var = JSON.parse(('<%= (#Users.count) == 0 ? "[]" : Users.first(10).to_json %>')
console.log(JSON.stringify(my_var))
You forgot about document ready. Try:
$(function(){
var datadump = ($('#trackers').data('trackers'));
console.log(datadump)
});
Or for provide data from Rails to JS use Gon gem https://github.com/gazay/gon
Stumbled here from google. In my case, I was trying to pass an array of objects to a js.erb, which triggered an event a front end component listened to. For anyone from google, I solved this issue by doing the following:
In my controller.rb:
#payload = []
array.each do |a|
temp = {
foo: a.id,
bar: a.relation.relation
}
#payload << temp
end
in my js.erb:
$(document).trigger('event', { data: <%= #payload.to_json.html_safe %> })
in my component.js:
$(document).on('event', (e, data) => {
console.log(data) //should be array of objects in proper format
})
Hope this helps someone!
This could help you.
For me, I wanted to turn this array
#dates = ["2018-12-24", "2018-12-25", "2018-12-26", "2018-12-27", "2018-12-28"]
into a variable in Javascript. I tried the basic:
var dates = <%= #dates.join(", ") %>
alert(my_var)
This alerted this exact text:
1995
I have no clue why.
Since it wasn't working I tried Guru's solutions up there by copying and pasting, but it was worse now I had an error coming up in my code. Finally I got Guru's solution to work like this:
(the print embedded ruby open and close worked outside the JSON.parse() and there was a missing ")" )
var dates = <%= JSON.parse(#dates.join(", ").to_json)%>
alert(dates)
But even though it didn't crash, want to guess what it alerted?
Yeap, you guessed it:
1995
So I gave up and I used a work around:
In my view edit.html.erb
<div class="dates"><%= #dates.join(", ") %></div>
<script>
var dates = $(".dates").text()
alert(dates)
$(".datepicker").val(dates)
</script>
That allowed me to feed the variable, therefore alert the correct data and set the Datepicker's input with the correct information. I'm still bugged for not being able to solve the hole 1995 problem. If someone could be so kind as to explain, I would really appreciate it. Hope this helps and have a great day!

How to make jQuery remember state in a Rails app?

I'd be very grateful for a code snippet showing off jQuery functionality remembering state, e.g. .slideToggle().
I've read about local storage, session storage, ajax among others - what technique is preferred 2012 and how could an implementation look like in Ruby on Rails 3.2?
This episode on RC gave me nearly all information on how to solve it: http://railscasts.com/episodes/136-jquery-ajax-revised
I created an ajaxed link with remote: true in the view. This link renders as well a dynamic class retrieved from a boolean value in the session.
<%= link_to "Click here", {action: 'retain_widget_state'}, id: "switcher", class: "#{session[:switch]}", remote: true %>
I added an empty action in the controller. And then in a retain_widget_state.js.erb i have the code below. In it I toggle the state in a session and then run jQuery code showing or hiding the element via a click on the link above.
<% session[:switch] = true if session[:switch] == nil %>
<% if session[:switch] == false %>
<% session[:switch] = true %>
$('.company_view').slideDown(400);
<% elsif session[:switch] == true %>
<% session[:switch] = false %>
$('.company_view').slideUp(400);
<% end %>
Wrapped in a $(document).ready(function() {}); I put the conditional code that "remembers" the state of the toggle upon a refresh of the page:
if ($('#switcher.true').length) {
$('.company_view').display();
}
else if ($('#switcher.false').length) {
$('.company_view').hide();
};
There's more than one way to do this. To store a variable in the url as a parameter (accessible from the params[] hash in rails), in your javascript you can write something like (example taken from this question )
$.ajax({
data:{"toggle":state}, # This stores the toggle variable in the
# url like so: http://localhost:3000/?toggle=1
# Other stuff });
If you want something a bit more durable, try a cookie. There is a cookies[] hash in Rails3 (not sure about rails2). The session[] hash is stored as a cookie by default as well. You can check out this railscast on making a "remember me" login function which used the cookies[] hash. Also see the docs on the Cookie class in Rails3, which gives a pretty good explanation of the various options and methods you have available.
With jQuery, you will apparently need a plugin as the functionality to read/write cookies with jQuery is not there automatically (I was surprised to find that). Here's one on github that seems to be recommened and has some good documentation, though I haven't used it: https://github.com/carhartl/jquery-cookie
If you want to be able to retrieve the state at anytime, I would create records for the cases you want to save. Name, value and possibly page. Then, you can retrieve those values on page creation and use those values instead of hard-coded values.
HTTP is stateless in itself, meaning you've got to store the data somewhere (jQuery won't "remember" anything). Depending on your intended use, the state of the toggle field could be stored in a backend database, or stashed in a cookie on the user's browser.

Unable to bind ajax:success to form created with form_for .... :remote => true

I'm using Rails 3.1.1
I have the following in a haml view:
= form_for booking.notes.build, :remote => true do |f|
= f.text_area(:content)
= f.hidden_field(:noteable_id)
= f.hidden_field(:noteable_type)
= f.submit('Add note')
Which creates new notes on submission. Also the response from my controller is appearing correctly in a Chrome console (Network tab). But I cannot seem to grab the response.
I want to update the note list on the page after submission. I've been trying to bind to the ajax response so I can grab the response, but I am failing. For example, I think this should work but does not:
$('#new_note').bind('ajax:success', function() {
alert('Hi');
});
But no alert is triggered. Which I think explains why this also doesn't work.
$('#new_note').bind("ajax:success", function(evt, data, status, xhr){
// Insert response partial into page below the form.
$(this).parent.append(xhr.responseText);
})
Can you please point me as to what might be going wrong?
Did you try 'ajax:complete'?
Other things that can go wrong here:
Status code was not really "successful". This triggers success in jQuery
if ( status >= 200 && status < 300 || status === 304 ) {
Or the event handler was evaluated before the form was rendered. Try event delegation:
$("body").on('ajax:success', '#new_note', function(){...})
(careful, this is the new jQuery syntax. If using old jQuery, adjust accordingly)
if you want, you can put your javascript in create.js.erb (code in this file will be executed after response will come to your browser)
And in this file you can use if statement, like
<% if #ok %>
//your code
<% end %>
if in your controller's action set #ok to false the response will be empty!
This is generally done by doing a create.js.erb file in your controller's view section (if haven't done so already) in which you have access to whatever variables come out of the create action, and there you can render your html
in your create.js.erb file you could write something like
$("#new_note").html('<%= escape_javascript(render partial: "path/to/partial") %>');
read more in this post I wrote some time ago, it pretty much explains the whole flow

Want to evaluate an if statement every time an AJAX call for creating comment_titles is triggered

I have this if statement in my video show view:
<% if #video.user == current_user && current_user != nil && #video.comment_titles.count < 3 %>
<%= link_to "Add Comment Title", "#", :id => "comment_title_link", :class => "edit" %>
<%= simple_form_for #video, :remote => true do |f| %>
<%= f.input :comment_title_names, :label => false, :placeholder => "Add a Comments Title" %>
<%= f.button :submit, :value => 'Add', :id => 'add_comment_title' %>
<div class='hint'>Let your listeners know what comments you want by adding a guiding title for them. Pose a question, ask for feedback, or anything else!</div>
<% end %>
<% elsif #video.comment_titles.count == 3 && #video.user == current_user && current_user != nil %>
<p> You have reached your limit of comment titles. You can always add a new one by deleting one of your old ones. </p>
<% end %>
This if statement essentially evaluates #video.comment_titles.count to determine if the if statement or the elsif statement is true. I let users add comment_titles with ajax and so by the time #video.comment_titles.count == 3 is true, it won't correctly evaluate the if statement since the if statement, which is in my video show view, is only called after a page reload.
I want the if statement to be called dynamically every time the number of comment_titles changes, which is equivalent to saying whenever the AJAX call for updating comment_titles is triggered. However, I'd rather do this on the client side than have to do it in a .js.erb file. How would I trigger this on the client side?
OK so no one has answered, so I'm assuming either I have not provided enough code, I have not been clear in what I am trying to do, or it is impossible. Which is it?
After reading your question again with your comment in mind, I assume that
There is a javascript event handler for the event 'new comment title has been added'.
The provided fragment resides withing a container of sort e.g.<div id="add_comment_title_action">fragment</div>
The fragment should be refreshed every time the event handler is executed
You've seen that is possible to load the fragment by $('#add_comment_title_action').load('fragment_url') but you rather just do it within the client
Let's assume that the event handler looks something like
$('#add_comment_title_form').submit(function(e) {
e.preventDefault();
$.post( 'url',
{ 'title': 'Lorem ispum dolor sit amet'}
);
updateFragment();
});
Then you could have a function for updating the fragment
updateFragment() {
// Let's assume that #video.user == current_user && !current_user.nil?
if($('.comment_title').size() < 3) {
// NOP add something here to handle case when a title is deleted
} else {
$('#add_comment_title_action').html('<p> You have reached your limit of comment titles. You can always add a new one by deleting one of your old ones. </p>');
}
}
How the code can be made better:
There should be some callback to check if the post request has succeeded and proceed accordingly
You could handle the two different states of the fragment by simply hiding and showing the options depending on the state (if you can assume all the users have enabled javascript and css)
If you decide to replace the fragment with .html(...) then the event handlers for the elements within the fragment should be bound with .live(event, handler) so that you can enable adding new comment titles after a delete has been performed
Note that every check done in the client side must be done in the server side also.
See history for completely different answer :)
I kinda get what you mean by now. You should always sync your frontend with your backend so I suggest you do an ajax request when you submit the comment. Your request should return true or false if the user can comment and if he can, post it and reload that partial. I'm not a fan or rjs(which you seem to be using) so I wouldn't know the syntax but if it was jquery I could provide you with a more detailed answer.
anyway, i'll do this in bullet points
user submits form through ajax
$("#add_comment_title").click( function(){
$.ajax({
type: 'POST',
data: $("#formwhatever").serialize(),
complete: function(html) {
$("#divthatcontainsthatwholeifstatement").replaceWith(html);
}
})
})
backend checks if user has enough posts already. if false, return false to browser. if true, add to db and render partial
in your controller:
def create
#pseudo code since im also working lol
if user.comments.size < 3
#save comment watever
end
#comments = updated comments
respond_to do |format|
format.js { render :partial => "partialthatcontainsthatif" }
end
end
???
PROFIT
this "refreshes" the partial no matter what. if the user already had more than 3 comments then it will return and show that error check you have

Categories

Resources