Pass javascript selection variable to rails controller - javascript

I'm working on a rails (v.5.2.3) app that is supposed to provide data filter abilities for spatial data (model "measurements") based on (1) different variables and (2) a lasso selection on a map. Both of these filter options should be available in the same view ("welcome/index").
The relevant part of the controller for preparing and filtering the data:
# app/controllers/welcome_controller.rb
def index
#selected_measurements = Measurement.where(
"measurements_id IN (?) OR name = ?",
params[:spatial_lasso_selection],
params[:query_site_name]
).all
gon.selected_measurements = #selected_measurements.to_json
end
In the view I use form_tag for the filter variable definition (1):
# app/views/welcome/index.html.erb
<%= form_tag({controller: "welcome", action: "index"}, method: "get") do %>
<%= label_tag(:query_site_name, "Site name:") %>
<%= text_field_tag(:query_site_name, (params[:query_site_name] or "")) %>
<%= submit_tag("Search") %>
<% end %>
In the assets directory I have a javascript file that constructs a leaflet map (via leaflet-rails), adds the point data as markers and enables the leaflet-lasso plugin for (2) lasso selection on the map. This code is embedded in the welcome view to display the map there.
The lasso selection gives me a javascript array of measurement_ids. I want to send this array to my rails controller to filter the data and update the view (params[:spatial_lasso_selection]). I learned that I should do this with an ajax request, but I have difficulties implementing it. I don't understand how form_tag and raw javascript can be used together to make both (1) and (2) work seamlessly.
I figure the ajax call has to look like this:
$.ajax({
type: "get",
url: '/welcome/index',
data: { spatial_lasso_selection: JSON.stringify(lasso_selected_measurements) },
});

I have problem also with ajax request. Instead I use input text value property to pass my javascript variable to the controller. Firstly just make simple form with hidden input property that have specific id in your index.html. Next create button that will send the variable to
<form id "myForm" action="/welcome/index">
<input type="hidden" id="myVar" name="varParams">
</form>
<button id="btnSend">SEND</button>
Then, create javascript function to set the value of the input text with our required variable when the button is clicked. Just put below code in your index.html.
<script>
document.getElementById('btnSend').addEventListener('click', function () {
document.getElementById("myVar").value = "myValue";
document.getElementById("myForm").submit();
});
</script>
Next, just retrieve the myVar params in your welcome_controller.rb.
def index
myVar = params[:varParams]
# Do something with myVar variable
end

Related

How do I send a JavaScript value to Rails Controller using AJAX?

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

Rails efficient way to store JavaScript object to Model?

I am using the FileStack API and the file picker gem (https://github.com/Ink/filepicker-rails). I have an Attachment model that has a :title as a string. When a file is uploaded, the URL from the FilePicker API is stored as the :title. But the gem has a onchange method that returns an event variable as a JSON object that contains attributes of the file. I use JavaScript to access those attributes but I want to find a way in Rails to store those attributes, accessed via JavaScript, in a Model so that I can access it through the rest of the Rails app.
<%= filepicker_js_include_tag %>
<%= simple_form_for(#attachment) do |f| %>
<%= f.filepicker_field :title, onchange: 'onUpload(event)' %>
<%= f.submit %>
<% end %>
<script>
function onUpload(event) {
console.log(event);
var name = event.fpfile.filename;
console.log(name);
}
</script>
Update:
So after looking into your solution and googling around I am using ajax to send the data via routes to the controller. Below is my updated Javascript as well as the route and controller. When I render and inspect the #foo instance variable it is nil. So my data isn't getting passed properly. Furthermore, this whole process from the firing of the Javascript function to displaying the index view is now very very slow. I think I have the right idea after viewing your solution and doing more digging but I'm missing something and/or overcomplicating this. Any advice would be much appreciated.
<script>
function onUpload(event) {
var name = event.fpfile.filename;
jQuery.ajax({
data : { data_value: event },
type: 'post',
url: "/attachment/index"
});
}
</script>
Route
post 'attachments/' => 'attachment#index'
Controller
def index
#attachments = Attachment.all
#foo = params[:data_value]
end
View (returns nil)
<%= raise #foo.inspect %>
If you're using Postgres 9.3 or above you should consider using the hstore module and creating a JSON column. In a migration you can do:
add_column :your_model, :your_attribute, :json
And then you can just update YourModel.your_attribute => {'your': 'JSON here'}
Docs here: http://edgeguides.rubyonrails.org/active_record_postgresql.html#json
If you're using MySQL it's tricky, but doable. You have to create a text column and save the JSON as a string, and parse it every time you interact with it. Postgres is definitely better at handling JSON. I realize that this answer relies on an assumption, so if you're not using one of the two data stores mentioned, let me know and I'll pull it down.

Change second select list based on first select list value in rails

Updated with answer code at bottom
For second select box, show select options for only those staff members associated to the selected team.
Form:
Example case: A user selects another team. The staff member select options updates to display only those staff members associated with that selected team.
I know the solution is with javascript, but I'm having trouble applying it within a rails environment. I am aware of this question but am still having trouble applying it to my rails app.
Code
#app/controllers/task_notes_controller.rb
...
def new
#task_note = TaskNote.new
#teams = Team.all.includes(:staff_members)
end
...
#app/views/task_notes/_form.html.erb
...
<%= select_tag('team', options_from_collection_for_select(#teams, :id, :name ) ) %>
<div class="field">
<%= f.label :staff_member_id %><br>
# Currently displays all staff members on every team, no matter what team is selected.
<%= f.collection_select :staff_member_id, StaffMember.all, :id, :first_name %>
</div>
...
#app/assets/javascripts/task_notes.js
$(document).ready(function(){
$("#team").on('change', function(){
alert("The new team id is: " + $(this).val() );
# The value (the id of the team) does update on selection option change.
# Now I need to specify that I want the select options for the staff members
# select box to update and show only staff members with this team_id
});
});
First you fire an ajax call to your controller. (Keep in mind that this url from the ajax call must exist in your routes).
$(document).ready(function() {
$("#team").on('change', function(){
$ajax({
url: "populate_other_list",
type: "GET",
data: {team_id: $(this).val()},
// Callbacks that will be explained
})
});
Next you make your action inside your controller.
def populate_other_list
team_id = params[:team_id]
#staff = Staff.find_by team_id: team_id
respond_to do |format|
format.json { render json: #staff }
end
end
With this, on your success callback of your ajax call, you get a JSON object with your list. So you need to clear the staff select and create the options and append to it.
// Ajax call
success: function(data) {
$("#staff_member_id").children().remove();
// Create options and append to the list
}
// Rest of Ajax call
As you can see, i didn't put the code that create the options and put them inside the list, but a simple search will have plenty of results about this. The idea is simple though.

Rails- Pass information from view to Javascript

I want to start using websockets. I read all the docs and understand everything, but it leaves out something I need: How to pass information from view to JS?
I need to pass data from javascript to my controller. What I do not understand is, how do I get dynamically generated data in my view to the javascript to be sent?
Right now my view receives an instance variable on every HTTP request, it loops over every instance variable and makes a button which submits a hash with information extracted from that instance variable. I do not understand how to do the same thing with Javascript because Javascript will not understand Ruby classes.
This is what my code looks like now:
View/dashboards/_other_characters.html.erb
<% other_characters.each do |other_character| %>
<p><%= other_character.name %> is standing here (<%= other_character.power_level %>)</p>
<%= button_to "punch #{other_character.name}",
attacks_path(
target_type: other_character.class,
attack_type: :punch,
target_id: other_character,
target_name: other_character.name
) %>
<% end %>
This is what I would like to be able to do using JS
var task = {
name: 'Start taking advantage of WebSockets',
completed: false
}
var dispatcher = new WebSocketRails('localhost:3000/websocket');
dispatcher.trigger('tasks.create', task);
Try
<%= button_to "punch #{other_character.name}",
attacks_path(
target_type: other_character.class,
attack_type: :punch,
target_id: other_character,
target_name: other_character.name
), {id: '***', data: {name: '***', other_key: 'other_value'} } %>
Then you can access the data via jQuery data api.
When you need get dynamic data from view by client js, add data-attrs in your view dom then read it from dom API or other 3rd party js API.
In order to pass information from ruby to javascript you can use this gem: Gon, basically it transforms ruby variables and make them available to javascript on each view, take a look:
http://railscasts.com/episodes/324-passing-data-to-javascript
for any other alternative to achieve your purpose visit: https://www.ruby-toolbox.com/categories/javascript_tools#paloma

How to submit form and update element on the page without refresh, in Rails 4

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

Categories