I'm looking to pass a JavaScript variable into a Rails Controller. The interesting part is that the variable is generated inside Canman, and I cannot use it (yet) outside of it.
This is probably just JavaScript and not necessarily related with Canman. But I'm just not sure what it is happening here.
The approach I'm following (but completely open if there is a better way) is to populate a hidden field with jQuery, just to access the data via params from the controller.
If possible (and if this is a good practice) I will like to avoid the form, and just call some JavaScript on click and then pass that variable to the controller.
View
= form_for #post do |form|
= form.hidden_field :base64
= form.submit
JavaScript
$('form').submit(function(event){
Caman('#canvas', img, function() {
var imageBase64 = this.toBase64();
alert(imageBase64); // works fine
$('#post_base64').val(imageBase64);
});
alert(imageBase64); // nothing
});
PostsController
def update
#post = Post.find(params[:id])
raise '¯\_(ツ)_/¯'
...
end
post_params
=> {"base64"=>""}
Also, I read that an option could be to make an AJAX request. However, I'm not sure how to proceed with that, yet.
At some point, I tried with a text_area instead of a hidden_field. The text_area got populated with the right data. However, params never got the data. If I got back via the browser button, the data was in the text_area, and clicking on submit one more time, populates the params as expected.
Thanks in advance!
Short answer: Ajax.
The goal was to send the value of a variable (a base64 image) to my rails controller, and once there, keep going just with Ruby.
At the end, I created a simple Ajax function to send data from my client (Image from browser) to my server (Rails Controller) via params
save_canvas.js
$(document).on('click', '.save_canvas', function() {
event.preventDefault()
var base64Data = canvas.toDataURL('png')
$.ajax({
type: "POST",
url: "http://localhost:3000/pictures/",
data: { image: base64Data },
success: function(post){ console.log('success') },
error: function(post){ console.log(this) }
})
})
pictures_controller.rb
def create
#picture = Picture.new(image: params[:image])
#picture.save
redirect_to #picture
end
I got support to achieve this here
Related
Within my dashboard page in my application I want to display a map legend that populates dynamically depending on the values given to the HTML that displays this legend.
This is what the legend should look like:
I am populating this legend using javascript; My JavaSCript function returns an array of nested hashes that would look something like this:
[
{title: 'Downtown Loft', color:'#ade8ad'},
{title: 'Midtown Mansion', color:'#bd95ff'}
]
The Problem
In order to accomplish this, I need to be able to send the array above from my pages.js.erb file to my pages_controller.rb file, specifically to the dashboard action. Within this controller method, I will assign the data from my JavaScript to an instance variable called #calendar_legend
To verify this works, I will call <%= #calendar_legend %> in my dashboard.html.erb view and expect for it to show the array... unfortunately this does not work.
I've tried render json: #calendar_legend in my controller and I always get a null value when viewing that page.
Here's how I've tried sending the data to my controller:
function calendarLegend(){
const legendarray = [
{title: 'Downtown Loft', color:'#ade8ad'},
{title: 'Midtown Mansion', color:'#bd95ff'}
]
$.ajax({
type:'POST',
url: 'pages/dashboard',
data: JSON.stringify(legendarray),
contentType: 'application/json'
});
};
$(document).ready(function(){
calendarLegend();
});
and here's how I've tried assigning it in my controller
def dashboard
#calendar_legend = params[:legendarray]
render json: #calendar_legend
end
and in my view
<%= #calendar_legend %>
This hasn't worked, among with any other solution that I found on Stackoverflow and various other sites.
The issue is that you are rendering the variable #calendar_legend that has not yet been set. Also the Ajax function does nothing to render the result on the page after it has been called.
I'm not exactly sure what you are trying to accomplish here, but here are some suggestions.
First, you could just set the #calendar_legend variable in the controller action that loads the dashboard.html.erb page (instead of calling another action once the page has loaded using Ajax). If you do this, you could just remove the Ajax function altogether.
Second, if you are set on trying to load data to change the legend after the page has loaded, you need to handle the response from the server. There are 2 ways I can think of to easily do this:
Add a response handler into the AJAX method (example from the jQuery Ajax docs - https://api.jquery.com/jquery.ajax/) - in this example, the 'data' variable in the .done function would contain the value of #calendar_legend that you are rendering as json from the controller action, so you need to add it to the page:
$.ajax({
type: 'POST',
url: "pages/dashboard",
data: JSON.stringify(legendarray),
contentType: 'application/json'
}).done(function(data) {
$('.div-with-calendar-legend-data-inside').html( data );
});
You could also do this with Rails using a js.erb template with the same name as your controller action (instead of adding the response handler to your Ajax function). First you would modify your controller action to just set the variable and not render anything (and instead let Rails render the js.erb template):
def dashboard
#calendar_legend = params[:legendarray]
end
Then create the template app/views/pages/dashboard.js.erb, and add something like this to it:
$('.div-with-calendar-legend-data-inside').html( <%= #calendar_legend %> );
Both of these options would require that you add a div (with a class or ID) in your view to add the #calendar_legend data into:
<div class="div-with-calendar-legend-data-inside">
<%= #calendar_legend %>
</div>
Typically, I would only try to load data with Ajax if you are trying to reload the legend when the user makes a change on the page (ie. they select something from a dropdown or some other form input) - then you can send the data with Ajax to the controller and let it update the legend without refreshing the page. However, when doing that I would just use a form_tag and set remote: true on it, then use jquery to submit the form when the inputs change and handle the response with the js.erb template - I think this is cleaner than using the Ajax function.
Hopefully this helps
I am looking for ways to update the cart in my toy e-commerce application without having to reload the page and I was following this pen.
For example the code that is updating a product's quantity is the following:
$('.product-quantity input').change( function() {
updateQuantity(this);
});
It works nicely but the database is not updating of course at this point.
I was wondering what is the best way to update both the front-end and the database with products' quantities or similar operations? I am probably looking for AJAX but not sure what the latest best practices are (ideally with as less JS as possible).
Your updateQuantity() function has to make an ajax call to a method in your controller that handles the change in the database and responds to either json or js to manipulate the dom.
function updateCart(e){
$.ajax({
type: "patch",
url: 'your_route_to_method', //point to the route that updates the item
data: {
your_resource_scope: { quantity: e.value } //sanitize input in strong params
},
success: function (response) {
//response is the json you get back from your controller
//handle the logic of whatever happens in the dom after you update the quantity
}
});
}
I'd suggest attaching the id of the product you want to update to the input's parent so you can pass it to your route and remember to pass the value under the required scope so you can sanitize the input in your controller via strong_params.
In your controller:
def update
respond_to do |format|
if #your_resource.update(your_resource_params)
format.json { render json: { key: value } } #build the json you want to return
else
#handle failiure
end
end
If you decide to respond in js instead of json, you need to create a view with the same name as your method with a .js or .js.erb extension (instead of .html/.html.erb) and handle the successful response in js. In this view you have access to all the instance variables declared in your method. For example:
# => update.js.erb or your_custom_method_name.js.erb
$('#resource_#{ #your_resource.id }_price').replaceWith('<p>#{ #your_resource.quantity * #your_resource.price }</p>');
If you go this route, remember to delete the success part of your ajax call.
I rely on a variable defined by a JavaScript object to update (re-render) a Ruby on Rails partial inside a view, but it is not working. From all I read it tells me the only possible way for this to work is to use an Ajax call, however I'm new to this and couldn't quite grasp why (given the JavaScript variable is available before the Rails command is defined), nor exactly how I should do it.
My Rails view has this HTML bit I'm looking to
<div id="myevent">
<% if #pocket.events.any? %>
<%= #event = #pocket.events.first %>
<%= render #event %>
<% end %>
</div>
On that same view I implement a JavaScript object made up of various clickable nodes, each node denoting an event with a unique id. I want to re-render the #myevent section above each time someone clicks on a different node.
As someone new to front-end programming, I've tried this:
timeline.on('click', function (properties) {
logEvent('click', properties);
var item = properties["item"];
$('#myevent').html("<%= escape_javascript render (#pocket.events.find_by id:" + item + ") %>");
The JavaScript variable 'item' contains the event id from the clicked node. As you may know, the last line above doesn't work, Rails raises an ArgumentError with the message:
'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
It does works if I set a hardcoded id, so the rationale seems to be right:
$('#myevent').html("<%= escape_javascript render (#pocket.events.find_by id: 5) %>");
I have tried using the partial format as well:
$('#event').html("<%= escape_javascript render :partial=>'shared/event', :locals=> {event_id: " + item + "} %>")
And then using the following method to recover the right event in the 'shared/event' partial:
<%= #event = Event.find(event_id) %>
however, it fails with a ActiveRecord::RecordNotFound error because it doesn't really replace the string "+ item +" by the value of that JavaScript variable:
Couldn't find Event with 'id'=+item+
The "pain" is that, no matter how I try I can't find a way to use the JavaScript defined variable on that Rails calls. It looks like the solution would be to use an Ajax call but I reckon after many tries I don't get to work this out by myself. I would really appreciate a hand here.
I think that what happens is that the .erb portion of the code is only parsed at render time, so when the page first renders, there's really no item to append to that. In turn, when you hardcode it, it's able to parse that ruby code and render correctly.
So indeed you need to contact the server.
What I would do would be:
timeline.on('click', function (properties) {
var item = properties["item"];
$.ajax({
type: "POST",
url: '/route_to_your_controller',
data: { event_id: item },
dataType: 'json',
success: function(data) {
if (data['success']) {
$('#myevent').html(data['html']);
}
else {
alert('oopsss take care of this');
}
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log(errorThrown);
}
});
});
routes.rb
post '/route_to_your_controller', to: 'controller#spew_html'
then, saying you have a route to this action on a controller:
def spew_html
# you should take care of failed finds, errors that might occur, have a before_action to check legality of ajax call, and even possibly accept only xhr, return false unless request.xhr? etc
event_id = params[:event_id]
html_partial = render_to_string 'shared/event', locals: { event_id: event_id }
respond_to do |format|
format.json { render json: { html: html_partial, success: true } }
end
end
As a bonus, one pattern I like to implement when possible is just having the same route for GET be the POST for whatever ajax, so that then I can simply do:
$.ajax({
type: "POST",
url: window.location.pathname,
//...
});
And let the controller know how to deal with it.
I'm having a lot of trouble trying to do something that I imagine would be fairly simple.
I have a list of items, let's say, todos. At the bottom of that list I have a text field where I add new items to that list. I want to make it so that the new items are added to the bottom of that list dynamically, without a full page refresh, like in a chat window.
I made the submit form remote: true and it successfully submits without reloading the page, but I can't get the new item to appear at the bottom of the list at the same time. I have to refresh the page to see the changes.
I tried a few different approaches I found on SO (there's no shortage of similar questions here) and the web, and even a gem called Sync, but each of them had errors and problems of their own and I couldn't get any to work properly. Each of them could be its own SO question. So instead I ask: Is there a "recipe" that is sure to successfully implement this in Rails 4?
let's say, now you have a user form to submit,
<%=form_for #user,remote: true%><%end%>
And you also have a controller,
UsersController
In your controller, you have a function,
def create
#something
end
which is for the form.
the only thing you need is to modify the function like
def create
#something
respond_to do |format|
format.js
format.html
end
end
then in your view side, under directory of view/users/ , create a create.js file, in the file, you can do the js action, like get the new record, and append the new record to the users list.
reference:
http://edgeguides.rubyonrails.org/working_with_javascript_in_rails.html#form-for
There are various ways to do what you are asking. My approach would be:
Create an AJAX call to the controller that passes the parameters of the form
Inside the controller, you save/update things and then return a JSON object
On the success callback of the AJAX function, you append a list item/table row, using the object values
The code could be something like this:
model.js
$(function() {
$("#submit_button").on("click", function(event) {
event.preventDefault();
$.ajax({
type: "POST",
url: "your_controller_url",
data: "your_form_data"
success: function(result) {
// Append the result to a table or list, $("list").append(result)
},
});
});
});
controller.rb
def your_action
# Do your stuff
# return JSON to the ajax call
end
Well, this is just a skeleton. I prefer doing things this way. (Because i hate the js.erb approach)
Here is rails 5, hope it will help someone ( it still works on rails 4 ):
Try this ajax example:
In 'routes.rb':
# set the route that ajax can find the path to what controller in backend
get '/admin/some_great_flow', to: 'great_control#great_flow'
In 'great_control_controller.rb' controller:
# this function in controller will response for ajax's call
def great_flow
# We can find some user or getting some data, model here.
# 'params[:id]' is passed by ajax that we can use it to find something we want.
#user = User.find(params[:id])
# print whole data on terminal to check it correct.
puts YAML::dump(#user.id)
# transform what you want to json and pass it back.
render json: {staff_info: #user }
end
In 'app/views/great_control/index.html.erb' view:
<div>
<label>Staffs</label>
<%=select_tag(:staff, options_from_collection_for_select(#staffs, :id, :name), id:"staff_id", required: true)%>
</div>
<script>
//every time if option change it will call ajax once to get the backend data.
$("#staff_id").change(function(event) {
let staff_id = $("#staff_id").val()
$.ajax({
// If you want to find url can try this 'localhost:prot/rails/info/routes'
url: '/admin/some_great_flow',
type: 'GET',
dataType: 'script',
data: { id: staff_id },
// we get the controller pass here
success: function(result) {
var result = JSON.parse(result);
console.log(result['staff_info']);
// use the data from backend for your great javascript.
},
});
});
</script>
I write it for myself.
You can see the changes using javascript.
For eg lets consider a controller Mycontroller with action index and you are submitting form on index.
Then create a file in views my_controller/index.js.erb
To reflect changes use javascript in this template.
Definately remote sends the ajax call, so to see the changes you need some manipulation using javascript.
Thanks
I have this Javascript view in my Rails 3 project:
app/views/expenses/new_daily.js.erb
var i = parseInt($('#daily').attr('data-num')) + 1;
//$('#daily').append('agrego fila ' + i + ' <br />');
$('#daily').append('<%= escape_javascript(render(partial: 'new_expense', locals: { i: i })) %>');
$('#daily').attr('data-num', i);
I want to pass my 'i' javascript variable to a ruby partial through locals, How I can accomplish this?
As far as i know there is no way to do it directly and the reason is fairly simple too, html is executed at the server side and javascript is a client side language which means its executed in your local browser, thats why if you even try to pass a variable between the two you'll have to make a request to the server,
However this problem is tackled by calling an AJAX request, this AJAX request does the same thing as sending a new request to the server however it does that without refreshing or reloading the page to it gives the users the illusion that no request was made.
a guy asks a similar question Here
and you can learn more about AJAX Here on MDN:
Yes you can pass the value by using jquery;
<%=f.text_field :email ,:id=>"email_field" %>
<script type="text/javascript">
var my_email= "my#email.com"
$(document).ready(function(){
$("#email_field").val(my_email);
});
</script>
Simple answer is you can't. Partials are expanded at server side, and JavaScript variables are set later at client side. You could make i (as a variable name) a parameter of the partial and use it there.
render :partial => 'xx', :locals => { :variable => 'i' }
And in partial
alert(<%= variable %>);
Check out the gon gem. https://github.com/gazay/gon
It gives you a simple object you can pass variables to that will be available to your scripts via window.gon
Also referenced here
http://railscasts.com/episodes/324-passing-data-to-javascript
1) You may create a js tag with global variable in you erb template, after that you will be able to access that variable from any js file
<%= javascript_tag do %>
window.productsURL = '<%= j products_url %>';
<% end %>
2) You can pass data to data-attribute in erb template and access it by js on client side that way $('#products').data('products')
<%= content_tag "div", id: "products", data: {products: Product.limit(10)} do %>
Loading products...
<% end %>
3) You can use gon, to use your Rails variables in your js
There is a good article, read it and fine solution for your specific case
http://railscasts.com/episodes/324-passing-data-to-javascript,
more comments are here http://railscasts.com/episodes/324-passing-data-to-javascript?view=asciicast
Here's a few different options on how to do it:
http://jing.io/t/pass-javascript-variables-to-rails-controller.html
The best other answers here are right that this can't be done by passing the javascript variable into an erb partial, since it is rendered on the server, not the client.
But since anyone looking for this is probably interested in a work-around solution, which I don't see here, I will post this example that works well with Rails UJS and Turbolinks.
First, you set up your controller to return a partial as HTML:
format.html { render partial: "new_expense" }
Next, write a javascript AJAX function in app/views/expenses/new_daily.js.erb:
var i = parseInt($('#daily').attr('data-num')) + 1;
$.ajax({
url: '/daily',
type: 'GET',
dataType: 'html',
contentType: "application/html",
success: function(response) {
$('#daily').replaceWith(response)
$('#daily').attr('data-num', i);
}
});
This is going to get your Rails partial as an html fragment that you can use to replace that part of your rendered page. You can use jQuery to get your data-num attribute value, do some math on it, replace the partial in your view, and then set the attribute value again.
You may ask why go to all the trouble of getting the Rails partial and replace it on the page, instead of just getting the data attribute, doing math on it, and setting that? The answer is that this is the best, and perhaps the only way of doing something which is really essential when rendering a Rails partial using UJS while handling an asynchronous response to an action.
If you are handling an asynchronous response from your server in a create.js.erb template, then your variables (#daily, for example) are not going to reflect the work done after the request has completed (for example, if there has been processing on a background server like Sidekiq). In that case you don't have up-to-date action response variables to pass into your Rails partial in the js.erb file, but you also can't pass the javascript data response into your partial, as pointed out in this question.
As far as I know, this approach is the only way to get a fully up-to-date partial after receiving a response to an asynchronous response (not shown). This get you the up-to-date partial, allows you to get your javascript into it, and is flexible enough to work in pretty much any use case.
Let's make shure we understand each other. Your erb template (new_daily.js.erb) will be processed on the server side, ruby code will be evaluated (within <% %>), substitution made, and then resulting javascript will be sent to browser. On the client side the browser will then evaluate this javascript code and variable i will be assigned a value.
Now when do you want to pass this variable and to what partial?