Run a Ruby service inside Javascript inside HTML - javascript

I have a view with a simple_form and an input called priority. Before the submit button I have Your request is going to cost $X.
My X cost is going to change accordingly to what the user select in the priority input.
The thing is I need to get the priority input from the user to call a Ruby service, get the result and add it to the Your request is going to cost $X.
What I have in my view is something like:
= f.input :priority
p Your request is going to cost <strong id="selected-priority">0</strong>.
= f.submit "Send",
javascript:
$('#priority').on("change", function(e) {
var priorityInput = $(this).val(); # this is going to return something like "EMERGENCY"
var totalInDollars = #{CalculateAmount.call(priority: ??)};
$('#selected-priority').text("$" + totalInDollars);
}).change();
This is all working but I have no idea how to add the priorityInput js variable to my service call.
Could someone help? Tks!

Related

Running a ruby function based on data in a textbox

So in my ruby on rails application, I have a textbox that, when someone pastes data, it gathers a bunch of data (e.g. does some web requests, etc.). I've gotten everything from the ruby function working properly just using some sample data.
What I'm trying to figure out now is how can I pass the value of a textbox into a ruby function? Is that possible? For example, here's my code:
<script>
function doSomething() {
var searchData = $('#myTextBox').val();
<% helperFunction(<<How can i pass searchData here?>) %>
};
</script>
I'm guessing if this doesn't work, I could always create a small form that posts the textbox input to a controller, and go from there.

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: How to add an object through a bootstrap modal?

I'm new to rails and I have this web application that allows users to create new print Jobs Using Rails 4
app/models/job.erb
class Job < ActiveRecord::Base
belongs_to :job_type
end
app/models/job_type.erb
class Job < ActiveRecord::Base
has_many :jobs
end
In the new job creation form user must choose a job type for his new job from a list, Which I managed to get it throw the following code.
app/views/jobs/_form.html.erb
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#job) do |f| %>
.....
<div class="field">
<%= f.label :job_type_id %><br>
<%= f.collection_select :job_type_id, JobType.all,:id,:name %>
</div>
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Modal title</h4>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
</div>
</div>
</div>
</div>
<% end %>
</div>
</div>
And through some coffee script I managed to add "Other" Option to the Job Types menu, Which fires the modal #myModal to add a new JobType to the database if its not exist...
app/assets/javascript/jobs.coffee
$('#job_job_type_id').append("<option>Other</option>")
$('#job_job_type_id').change ->
jobType = $('#job_job_type_id :selected').text()
if jobType == "Other"
$('#myModal').modal('show')
else
$('#myModal').modal('hide')
The Code is working good and fires the bootstrap modal. But that's it I don't know what to do next?
I've tried a lot of code and reviewed a lot of questions, but I didn't get to make this modal able to add a new JobType and update the list, I've figured that needs some modification to controllers and fancy AJAX code beyond my knowledge...
I have some questions here
1) What should I put in the modal code to be able to add a new JobType to the database, Then return to the new Job creation form and the newly created JobType selected
2) Which controllers need to be modified? and How? What AJAX Code need?
3) How to re-factor the Job _form ? Can I put the modal code in a new Partial? If yes how to implement this ?
I hope you can help me, I've been struggling to solve this issue for days.
Thank you
Ok, first, to answer your questions:
1) What should I put in the modal code to be able to add a new JobType to the database, Then return to the new Job creation form and the newly created JobType selected
Your modal will add a form, essentially the form you probably have under app/views/job_types/_form.html.erb, but it will have a button that will submit via AJAX instead of form submit.
2) Which controllers need to be modified? and How? What AJAX Code need?
You will need to add a method to the job_types_controller that can handle the aforementioned AJAX call. It will need to:
Save the new job type
Return a success status and the newly created entity to the caller.
3) How to re-factor the Job _form ? Can I put the modal code in a new Partial? If yes how to implement this ?
You need to have a method that, when called, adds a new option to the listbox. You can put the modal code in a partial, or not, up to you; that decision has no consequence in regard to the functionality of all this.
So, what do you need to do here:
SERVER (CONTROLLER):
1) Create a method in config/routes.rb that can handle an AJAX call.
resources :job_types do
post :append, on: :collection
end
This adds a custom resource route. Because we add it this way, we automatically get the URLHelper function append_job_types_path.
2) Implement this method in controllers/job_types_controller.rb to save a new JobType and return it (and, most importantly, it's ID) to the caller.
def append
job_type = JobType.new
job_type.name = params[:job_type_name]
if job_type.save
render :status => 201, :json => { id: job_type.id, name: job_type.name }
else
err_msg = job_type.errors.empty?? 'Save failed' : job_type.errors.full_messages.join('; ')
render :status => 400, :json => { message: err_msg }
end
end
If save goes well, the ID and name of the new entity will be returned as JSON to the caller. If not, we return an error and any validation messages.
Now, we are ready to utilize these methods...
CLIENT (VIEW):
1) Create a button that can launch the modal
You've already done this!
2) Add a form to the modal that can submit a job type
<%= form_tag(append_job_types_path) do %>
Enter Job Name:<br/>
<input type="text" name="new_job_type_name" id="new_job_type_name" />
<br/>
<input type="button" value="Submit" id="append_job_type_submit" />
<% end %>
And really, it doesn't need to be in a form since we're submitting via AJAX, but you'll probably get some styling help by using one. However, the id attributes here are important for the next steps. Note that I'm using form_tag here instead of form_for. That's because I won't be attaching anything to the form (or submitting it for that matter).
3) On submit (er, on button click), send the name entered by the user to new AJAX method
Here, we'll use unobtrusive javascript to hook a listener method to the submit button. You can put this code at the bottom of the view, or you can move it to coffeescript:
javascript:
$("#append_job_type_submit").click(function() {
var name = $("#new_job_type_name").val();
//TODO: validation on the name, ensure it's not blank, etc
$.ajax({
url: '/job_types/append',
method: 'POST',
data: {job_type_name: name},
success: function(data) {
//TODO: Handle success
},
error: function(err) {
//TODO: Handle error
}
});
});
Here, we are sending an AJAX call to the server method, passing the name the user entered. Note that I left space for simple validation you can do prior to submission
4) Upon response, append the new option to the listbox.
This continues the function from above:
javascript:
$("#append_job_type_submit").click(function() {
var name = $("#new_job_type_name").val();
//TODO: validation on the name, ensure it's not blank, etc
$.ajax({
url: '/job_types/append',
method: 'POST',
data: {job_type_name: name},
success: function(data) {
var sel = $("#job_type_id");
sel.append('<option value="' + data.id + '">' + data.name + '</option>');
$("#myModal").modal('hide');
alert("New job type " + data.name + " created.");
//TODO: probably be nice to auto-select this option; I'll leave that exercise to the alert reader
},
error: function(err) {
alert(err.responseJSON.message);
}
});
});
Now, the user can pick the option from the select box, and the new option has been saved to the database (regardless of whether or not they create the new job).
Disclaimer: I have not tested this code, or even checked to see if it compiles. But it should work, and regardless, this is the pattern you want to follow, so if nothing else, at least you have the direction now.
Now, all that said, I would still recommend the other approach without AJAX that I suggested, as it cuts out most of these steps, but that's just me. Feel free to accept whichever answer helps YOU get YOUR task done the way YOU want to do it.
Note: I decided to post this in addition to aldrien's answer as mine has a few differences that I found significant enough to warrant it:
A success and failure response should return success/failure status code. Having it always return success is a bit misleading. I think status codes are an important part of any REST design, even something as small as this.
I'd only recommend using match routes as a last resort. Resourceful routes are cleaner and clearer, and they give you more out the box as well. Also, I like the route under /job_types and not just floating at the root.
Also more of a standards thing, should be a POST request instead of a GET; we are creating a new entity after all.
Bug: The append method should yield data.id for the option value, not data.title (which should be data.name according to the original code). Otherwise, the value will not be the ID, and attempted save will fail.
If I may offer an alternative...
Your approach is going to require some advanced coding methods and, if you are uncomfortable with AJAX, this may be a difficult road.
As an alternative, I would suggest that instead of showing a modal, just show/hide a textbox within the existing form to house the Other name:
<input type="text" name="other_job_type" />
Then, when you submit the form, as you know it will go to JobController#create (or #update for existing jobs). In there, you can get this field:
def create
save_successful = false
#job = Job.new params[:job]
other_job_type = params[:other_job_type]
if other_job_type
new_job_type = JobType.new
new_job_type.name = other_job_type
save_successful = new_job_type.save
#job.job_type = new_job_type
end
if save_successful && #job.save
# redirect to success page
else
# render new/edit with error messages
end
end
By doing it this way, you only create a new job type if the user actually submits the job form, which is nice.
Use the save_successful pattern if you want to enforce validations on the JobType, such as a unique name. Then the save will fail if the user attempts to save with an existing job type. You could also just select the existing one for them, but I'll leave that to you if you choose to do that.
Again, all this follows the same pattern you are using now, just a new text field and a little more processing in the controller.
If you want, I can detail the answer to your question in terms of using AJAX methods to get this to work, as the steps, though much lengthier are pretty deterministic, but it's probably overkill for your use case. That said, I hate not actually answering the given question, regardless of my personal opinion of the approach, so just let me know.
Firstly, set up the button or form in your modal for triggering submit function.
Secondly, set up/check Routes/URL to be used for AJAX method.
As well as setting Controller function for saving new job type.
example in controller:
def create
job_type = JobType.new
job_type.title = params[:job_type]
if job_type.save
render :json => job_type
else
render :json => "some error here."
end
end
Finally, make AJAX function for sending data.
$("#add_new_job_type").click(function(){
$.ajax({
url: '/add_new_job_type',
type: 'GET', // or POST
data: {job_type: $("#field_contains_new_value").val()}
}).done(function(data){
// Do some validation for checking error response (like if statement)
// Append the new data (job type to select option tag)
new_job_type = "<option value="'+data.title+'">" + data.title + "</option>"
$('#job_job_type_id').append(new_job_type);
....
$("#myModal").modal('hide');
});
});
Notes: add_new_job_type used in AJAX url is the custom routes.
In config/routes.rb (custom routes):
match 'add_new_job_type' => 'job_types#add_new_job_type', :via => :post #or :get
In your JobTypes Controller, you must have:
def add_new_job_type
job_type = JobType.new
job_type.title = params[:job_type]
if job_type.save
render :json => job_type
else
render :json => "some error here."
end
end
Modify the code as you wish.

How to write a conditional statement amidst this jQuery script?

I have a jquery function which targets a rails form and upon the submit event posts the form data to the appropriate action. The object that’s created, a review, is then returned and via a mustache template is appended to a selected element and rendered:
$(document).ready(function(){
$('.new_review').on('submit', function(event){
event.preventDefault();
var reviewList = $(this).siblings('ul’);
$.post($(this).attr('action'), $(this).serialize(), function(review){
var newReview = Mustache.render($('#review_template').html(), review);
reviewList.append(newReview);
});
});
});
I have in my reviews controller written an if else statement in order to restrict a user (a devise model) to only being able to create one review:
def create
#restaurant = Restaurant.find(params[:restaurant_id])
#review = #restaurant.reviews.new(params[:review].permit(:thoughts, :rating))
if #restaurant.reviews.find_by user_id: current_user.id
flash[:notice] = "You already reviewed this restaurant!”
redirect_to '/restaurants’
else
#review.user = current_user
#review.save
redirect_to '/restaurants' unless request.xhr?
end
end
However I now need to insert a conditional into my jquery to ensure that it only attempts to append a new object if a review object is actually returned after the post action. I’m thinking that this needs to be written into the third argument on the post function but am a bit lost as to how to go about it.
Could anyone whether this is indeed the right way to go about what i’m trying to achieve and how exactly I could write this. Thanks in advance from a JS newbie.
The below now works perfectly with one simple if statement in the post function:
$(document).ready(function(){
$('.new_review').on('submit', function(event){
event.preventDefault();
var reviewList = $(this).siblings('ul');
$.post($(this).attr('action'), $(this).serialize(), function(review){
var newReview = Mustache.render($('#review_template').html(), review);
if review
reviewList.append(newReview);
});
});
});

How to insert Javascript code in a Ruby injection

I have 2 text fields. One is for the input of a member's id, and the other is the output of the member's name.
When I type a member's id in the first field and the action onblur is triggered, I would like to show the member's name in the other field.
This is my Javascript function:
function show_name(){
id = document.getElementById('id_member').value;
field_name = document.getElementById("name_member");
field_name.value = "<%= Member.find().nom %>";
}
In the method Member.find() I would like to send the value of the variable id that I've declared at the beginning. Example:
field_name.value = "<%= Member.find(*Javascript "ID" variable value*).nom %>";
How can I insert that value into my Ruby injection?
Thanks.
Javascript is run on the client, ruby is run on the server. You can't pass a javascript variable into the ruby code, unless you make a new request to the server.
Your options are to reload the whole page with a new query, use ajax to make a new query and do something with the result, or to download all possible Members into the page ahead of time and have javascript pull it out of the page somehow.

Categories

Resources