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!
I am using Rails ruby 6.1.4 and ruby 2.6.7
I have a form partial that is used for both the new and edit views. There are two select drop-down form elements. The application.js code makes an Ajax call to the controller to get items to populate the 2nd drop-down based on what is selected in the 1st.
For the new view, my code works fine. But, when viewing a record in the edit view, it does not work. It seems to need an id in the path when on the edit view.
When using developer tools in my browser, the console window shows this error when on the edit view:
[XHR] GET http://localhost:3000/fitness/weights/24/exercises?muscle_group=SHOULDERS
As you can see, it wants the id in the path. But I do not need the id to get the exercises for the drop-down. And of course, the 2nd drop-down does not populate.
How can I adjust my route so both the new and edit views work correctly? OR, do I need to change my ajax call? NOTE: when I move the :exercises out of the collection in the routes, then the reverse happens; the new form does not work but the edit form does.
Here is my code:
application.js:
// if edit view is showing
// get the current value of the Muscle Group field
//
var current_muscle_group;
if( $('#fitness_weight_muscle_group') ){
current_muscle_group = $('#fitness_weight_muscle_group').val();
}
if( current_muscle_group != '' && current_muscle_group != 'Select One' && current_muscle_group != null ){
get_exercises(current_muscle_group);
}
// user selects muscle_group from drop-down
// new or edit form
//
$('#fitness_weight_muscle_group').change(function(){
get_exercises($(this).val());
})
// get_exercises
// ajax call to controller#exercises
// to get exercies for the muscle_group
//
function get_exercises(current_muscle_group){
$.ajax({
url: "exercises",
dataType: "json",
data: {muscle_group: current_muscle_group},
success: function(data){
populate_exercise_select(data);
}
});
}
...
Controller
...fitness/weights/controller:
protect_from_forgery except: [:exercises, :past_exercise, :max_weight]
...
def exercises
# run sql to get exercises for muscle group passed in
if params[:muscle_group]
#exercises=Fitness::Weight.select("DISTINCT exercise")
.where(admin_user_id: session[:user_id])
.where(muscle_group: params[:muscle_group].upcase)
return render json: #exercises
end
...
My routes
config/routes:
...
resources :weights do
collection do
get :exercises
...
end
## added this route to satisfy issue with ajax call
## ... to controller action that requires a record id
get :get_edit_exercises
end
...
Solution
I added a new route (see above) to solve the ID in the path issue when viewing the edit form for a record. I added a controller#get_edit_exercises action to match the route. It returns #exercises just like the def exercises does.
I changed application.js to call the new controller#get_edit_exercises action when it was an edit view. If an ID is in the path/url, then it is an edit view.
application.js
// get the exercises
function get_exercises(current_muscle_group){
var url = "exercises";
var current_record_id = get_current_record_id(); // function below
if( current_record_id > 0 ){
// get_exercises is for edit form. it expects an ID
url = "/fitness/weights/" + current_record_id + "/get_edit_exercises";
}
$.ajax({
url: url,
dataType: "json",
data: {muscle_group: current_muscle_group},
success: function(data){
populate_exercise_select(data);
}
})
}
function get_current_record_id() {
var pathname = $(location).attr('pathname');
return pathname.replace(/[^0-9]/g,'');
}
Note: the id was showing twice in the path, but using a full path in url: url solved that. The forward / was also needed.
url = "/fitness/weights/" + current_record_id + "/get_edit_exercises";```
I also added the new ```controller#get_edit_exercises``` to the controller's ```protect_from_forgery except: [...]```
Everything works. I just need to DRY up some code now.
In words, we have a muscle_group that has_many exercises, (and perhaps an exercise has_many muscle_groups), if it's a bidirectional has_many, then it's achieved with muscle_group has_and_belongs_to_many exercises and vice-versa.
So to populate the exercises drop-down, for a given muscle_group, I would suggest an ExerciseController with an index method that can accept a muscle_group id parameter and respond to an ajax request with a list of exercises.
So the routes would be:
resources :muscle_groups do
resources :exercises
end
and the controller:
class ExercisesController < ApplicationController
def index
#exercises = Exercise.joins(exercise_muscles: :muscles)
.where(muscles: { id: params[:muscle_group_id] })
render #exercises # assumes a partial file '_exercise.html.erb' produces the option tag for an exercise dropdown
end
end
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.
I'm not even sure how to ask this question in a way thats understandable.
Basically, I'd like to do some javascript using a js.erb after I save. I'm submitting the form using a regular javascript/coffee-script file (if all fields are filled in correctly, then the form is submitted, else the form does nothing & just displays errors).
Part of my coffee-script:
fieldCorrectResponse: (fields, response) ->
if fields == correct
$('#new_mail')[0].submit()
else
$('#mail_error').text('error while filling out form')
my mail controller:
def create
#mail = Mail.new(mail_params)
if #mail.save
#PERFORM SOME JS USING A JS.ERB
else
render :new
end
END
So I guess what I'm really is asking is, how would you call a js.erb in the controller?
Wrote the solution to my problem below..
You should be able to render js and use create.js.erb.
Please try:
# MailsController
def create
#mail = Mail.new(mail_params)
if #mail.save
respond_to do |format|
format.js
end
else
render :new
end
END
Then, implement your javascript in app/views/mails/create.js.erb.
"do some javascript" isn't terribly descriptive. are you wanting to return a JSON object from the create action, which can then be parsed by the success callback on your jquery? Or do you want to have a template that has javascript in it that gets called as a result of the save action?
vinod covered the second way. make sure you have your routes set up correctly.
if you want to return a parseable JSON object, then write
render json: { some: 'json objects', should: 'go here' }
Also, not knowing what "mails" are, if you're trying to send emails that should be done with action mailer, and probably done as a part of committing the main model to the database (if you're creating a user and also trying to send an email, have a method as part of user creation that sends out the email).
I have the following HTML dropdown box on the index view of the Search controller. I want to update the view based upon the dropbox selection. I need to pass the selection to the controller somehow.
<!-- index.html rendered from Search/Index -->
<select id="search_params" name="search[params]">
<option value="tacos">tacos</option>
<option value="pizza">pizza</option>
</select>
Search Controller code takes params and spits out a message
def index
query = params[:search]
msg = "Your favorite food is #{query}! OMGWTFBBQ!?!?!?!"
respond_to do |format|
format.html
format.js
end
end
This Javascript should somehow post the dropdown box choice to the controller upon selection:
$("#search_params").change(function() {
var state = $('select#search_params :selected').val();
if(state == "") state="0";
//Send the selection to the controller Search controller somehow
// and then render a new view immediately ?
//I AM NOT GOOD WITH COMPUTER
})
return false;
});
I've looked at dozens of other Rails + JQuery examples, jquery API docs, etc. Can't find an answer to the simple question or I'm just overthinking it.
Anyways, thanks in advance for your help!
~Dan
I'm no Ruby/Rails guy but I know jQuery.
I think you want something like
$("#search_params").change(function() {
var state = $('select#search_params :selected').val();
if(state == "") {
state="0";
}
//Call jQuery AJAX
$.ajax({
url:'/path/to/your/controller/action',
type:'POST',
data:'search=' + state,
success: function(data) {
alert(data);
},
error: function() {
alert('Stuff went wrong');
}
});
return false;
});
Hope this helps, and have a read of the jQuery AJAX doc page.
I'm not really sure how you're returning your data from the controller there either? Is there a corresponding view that renders msg?
Checkout jQuery.ajax() documentation: http://api.jquery.com/jQuery.ajax/
You might need to put this at top of application.js (or this might just be a Rails 2.x requirement):
// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults
jQuery.ajaxSetup({
'beforeSend': function(xhr){
xhr.setRequestHeader("Accept", "text/javascript");
var token = $("meta[name='csrf-token']").attr("content");
xhr.setRequestHeader("X-CSRF-Token", token);
}
});
I set jQuery.ajax() to expect raw javascript code in the server response.
I have my normal html template render a partial. Or, all HTML is abstracted to a partial, not a template.
The javascript template will render basically do this:
$("div#some_id").html(
<%= escape_javascript(render(:partial=>'partial_name')) %>
);