I have an ajax call that works in a .js file, using:
...
update: function(){
$.ajax({
url: '/groups/order_links',
...
but I would rather use the route path
I made the file extension .js.erb and I tried adding:
...
update: function(){
$.ajax({
url: "#{order_links_groups_path}",
...
or
...
url: "#{order_links_groups_url}",
...
but I am getting a 404 in either case - [HTTP/1.1 404 Not Found 76ms]
From a POST http://localhost:3000/groups/49
rake routes shows my routes include:
...
PUT /groups/:group_id/links/:id(.:format) links#update
DELETE /groups/:group_id/links/:id(.:format) links#destroy
order_links_groups POST /groups/order_links(.:format) groups#order_links
groups GET /groups(.:format) groups#index
POST /groups(.:format) groups#create
new_group GET /groups/new(.:format) groups#new
edit_group GET /groups/:id/edit(.:format) groups#edit
which are defined with:
resources :groups do
resources :links
collection do
post 'order_links'
end
end
groups_controller has
class GroupsController < ApplicationController
...
def order_links
params[:link].each_with_index do |id, index|
Link.where(id: id).update_all(['position = ?',index+1])
end
render :nothing => true
end
...
Rails 4.1
"#{}" is used for string interpolation in Coffeescript so I am assuming that's an error. I assume the url where this ajax request is being made from is http://localhost:3000/groups/49 because if you don't pass in a proper url then it will use the current path.
"<%= order_links_groups_path %>" would look for a variable in ruby. This would work but JavaScript files in the assets directory are being compiled without using your apps context. Meaning order_links_groups_path will be undefined.
The answer here should help: Route helpers in asset pipeline
<% url = MyRailsApp::Application.routes.url_helpers %>
url: "<%= url.order_links_groups_url %>"
Firstly, let me explain some things for you:
--
Mime Types
Rails processes mime-types at controller-level (not middleware).
This means that if you're looking to request a resource through ajax's js mime type, you'll have to define its handling in the controller, not the routes structure.
You'll be able to read more about how Rails processes the mime types here:
If the client wants HTML, we just redirect them back to the person
list. If they want JavaScript, then it is an Ajax request and we
render the JavaScript template associated with this action. Lastly, if
the client wants XML, we render the created person as XML, but with a
twist: we also include the person's company in the rendered XML, so
you get something like ...
This means that if you're looking to process a JS response, you'll be able to do the following:
#app/controllers/groups_controller.rb
class GroupsController < ApplicationController
def order_links
...
respond_to do |format|
format.js
format.html
end
end
end
This allows you to create / call the various responses you want, depending on the mime type you send through to the controller.
--
Ajax
In regards to the Ajax call, you need to be aware that you shouldn't use any dynamic linking in your asset pipeline. I know the Rails documentation recommends otherwise, but the fact is if you serve static assets (as is recommended in production), you'll lose the ability to call those routes.
Of course, as Ahmed suggested, you can rely on the coffeescript or erb preprocessing to allow you to use the route as you wish:
#app/assets/javascripts/application.js.coffee
update: function(){
$.ajax({
url: <%= order_links_groups_path %>,
...
This will route your javascript request, allowing you to process the mime type in the controller as you need.
Related
I am doing an initial test of my Rails API by performing a fetch to my backend and using console.log() to view the results in my browser console. My error appears when I make the request to my namespaced url to see a list of programs.
I have checked the controller show method, as well as my routes and model. I know that it is returning html instead of JSON and that is why the fetch is breaking in part.
App.js
componentDidMount(){
fetch('http://localhost:3000/api/v1/programs')
.then(res => res.json())
.then(json => console.log(json))
programs_controller.rb
class Api::V1::ProgramsController < ApplicationController
def index
#programs = Program.all
render json: #programs
end
def show
#program = Program.find(params[:id])
render json: #program
end
private
def program_params
params.require(:program).permit(:url, :name, :network, :image)
end
end
I expected to see a list of 18 programs I placed in my seeds.rb file but I get the error from ActiveRecord that says 'Couldn't find Program without an ID'. I did try passing :id into the .permit() in my program_params method but had no luck there either.
I think your api/v1 url will be miss in routes.rb or something wrong. Can you show me your routes.rb file?
additionally
i have checked your source code in github
after you write comment to this answer
and I find wrong word
you have coding resource in routes.rb and it's wrong word
you have to change resource to resources in config/routes.rb
2.5 Singular Resources
Sometimes, you have a resource that clients always look up without referencing an ID. For example, you would like /profile to always show the profile of the currently logged in user. In this case, you can use a singular resource to map /profile (rather than /profile/:id) to the show action:
https://guides.rubyonrails.org/routing.html#singular-resources
I recommend you have to read rails guides
https://guides.rubyonrails.org/
I am following the https://devcenter.heroku.com/articles/direct-to-s3-image-uploads-in-rails#jquery-file-upload-callbacks Tutorial to use s3_direct_upload on local development environment. There's some javascripts which set the fileupload function for each file fields. It all works great if I simply include the codes right below the form, but it just doesn't work if I put the code in a js.erb file under app/assets/javascripts/. The error is:
ActionView::Template::Error (undefined method `url' for nil:NilClass
(in /s3_direct_upload_gem_test/app/assets/javascripts/s3_direct_upload.js.erb)):
18:
19: <%= #s3_direct_post.url %>
20:
21: <%= javascript_include_tag :s3_direct_upload %>
app/assets/javascripts/s3_direct_upload.js.erb:14:in `block in singleton class'
As you may see, line 19 from the above code is used to print out the #s3_direct_post.url. It was correctly printed when I include all the javascript in the file. If I trace the line 14 in the s3_direct_upload.js.erb, it is the url line:
fileInput.fileupload({
fileInput: fileInput,
url: '<%= #s3_direct_post.url %>',
type: 'POST',
autoUpload: true,
formData: <%= #s3_direct_post.fields.to_json.html_safe %>,
It seems like that for some reason, the javascript file under the assets folder (in the asset pipeline) is compiled separately and so #s3_direct_post, which is set in the Controller is not set here.
Of course I can always put those in <script> at the viewer file but it is not quite elegant. Is there any way to separate page-specific js coding and solve the problem above?
All JS files within /assets/javascript are part of the asset pipeline. In production the asset pipeline is compiled beforehand when your Rails app is deployed (e.g not on each request). This is why #s3_direct_post.url is nil.
I agree that injecting the whole JS code in the view is less than ideal and not very elegant. In the past I have come up with approaches that take inspiration from JS frameworks like Google Analytics, where only 2-3 lines of JS are placed in the HTML:
/* /assets/javascripts/s3_direct_upload.js */
window.MyApp = {};
window.MyApp.config = {};
window.MyApp.config.getS3Url = function() {
if(typeof(window.MyApp.config._s3Url) == ‘undefined’) {
throw “No S3 URL configured”;
}
return window.MyApp.config._s3Url;
};
window.MyApp.config.setS3Url = function(url) {
window.MyApp.config._s3Url = url;
}
// ...
$('#upload-button').on('click', function(){
fileInput.fileupload({
fileInput: fileInput,
url: window.MyApp.config.getS3Url(),
type: 'POST',
autoUpload: true
})
});
Then the view only needs to reference the Config API you've created:
<script type=“text/javascript”>
window.MyApp.config.setS3Url('<%= #s3_direct_post.url %>');
</script>
However if you're really determined not to have any JS in the views. You could load the configs via a dynamic JSON request:
class JSConfigsController < ApplicationController
def index
configs = {
's3URl' => #s3_direct_post.url
# etc
}
respond_to do |f|
f.json do
render json: {config: configs} # => {"config": {"s3Url": "http://the_url"}}
end
end
end
end
Then you can load all the configs via ajax by requesting /js_configs.json. However this approach requires a bit more care due to the asynchronous nature of Ajax. E.g you have to be careful to not call JS functions that rely on configs until the Ajax request has finished being retrieved.
finally I work around this problem by using another s3 related gem: s3_file_field and it just works well even without heavy tweaking. unfortunately I can't find all related information through my study on solving this problem.
I hope this help!
In rails, this is an api response I'm currently generating as a response from /charts_json/south_carolina.json (for example)
[{"d_s":0,"name":"summerville"},{"d_s":1,"name":"hilton head island"},{"d_s":2,"name":"north myrtle beach"},{"d_s":1,"name":"spartanburg"},{"d_s":12,"name":"greenville, sc"},{"d_s":0,"name":"aiken"},{"d_s":6,"name":"columbia"},{"d_s":4,"name":"myrtle beach"},{"d_s":1,"name":"simpsonville"},{"d_s":1,"name":"lancaster, sc"},{"d_s":0,"name":"north augusta"},{"d_s":0,"name":"sumter"},{"d_s":0,"name":"rock hill"},{"d_s":1,"name":"beaufort, sc"},{"d_s":1,"name":"mount pleasant"},{"d_s":21,"name":"charleston"},{"d_s":1,"name":"clemson"},{"d_s":1,"name":"anderson, sc"}]
Now what I need to do is render the above like this, as a json document
[['0', 'summerville'], ['1', 'hilton head island'], ...etc... ]
For the benefit of the SO community and the clarification of the reader I'll include all the code I'm going to be using to make this work if and when I get this last thing handled
In addition to my charts_controller, I generated a charts_json_controller for responding to json requests--- an example of a controller method in that controller (this is a bit clunky but its ok for now as long as I get functionality)
def south_carolina
#locations = Location.find(1687).descendants #used acts_as_tree gem
respond_to do |format|
format.json { render json: #location.as_json(only: [:d_s, :name])}
end
In the view (cross section)
function drawMarkersMap() {
var data = google.visualization.arrayToDataTable([
['Startups', 'Location'],
$.ajax({url: '/charts_json/south_carolina', dataType: 'json'})
]);
Not sure if I'm understanding correctly, but here's a way to get the json as an array instead of a hash.
define this method for Location
class Location
def self.as_json_array
as_json(only: [:d_s, :name]).collect { |l| [l[:d_s], l[:name]] }
end
end
You could make this more general-purpose if you necessary, but I want to make sure I'm understanding your requirements first.
Then just use that method instead of as_json in your render line.
Also, it sounds like you know this, but you really should just use the same controller and put any custom code for different formats in your respond_to block.
Lately i have run into a few applications that are using js.erb and i am not really sure how to use it ...here is the code below. Can someone help me understand how this works?
in the routes.rb file
map.resources :player_emails
my controller player_emails_controller.rb in the create action
def create
#player_email = PlayerEmail.create(params[:player_email])
if #player_email.save
#response_txt = "The player has been emailed."
PlayerEmailsMailer.deliver_pattern_email(#something, #player_email, request.host_with_port)
#error = false
else
#error = true
#response_txt = "Please make sure you entered your name and a valid email address."
end
end
then i have the file player_emails/create.js.erb
$('#player_email_ind').hide();
$('#player_email_submit').show();
$('#player_response_msg').html("<%= escape_javascript #response_txt %>").fadeIn();
<% unless #error %>
$('#player_email_form')[0].reset();
<% end %>
i know what the jquery is going but i dont know how this is doing the ajax call. Does it just automatically do an ajax call when there is a js.erb...can someone explain the way this works and why i dont need a respond_to in the controller action telling it this is format.js
If a js (ajax) request is made it will respond by rendering the js.erb file and viceversa.
This is the default behaviour that is being performed:
respond_to do |format|
format.js{
render :template => 'create.js.erb'
}
format.html{
render :template => 'create.html.erb'
}
end
When the form is submitted, it does a POST to /player_emails. The resource declaration in routes.rb ensures the request is handled by PlayerEmailsController#create.
The controller is responsible for handling each format it receives. In the case of an AJAX call, the format is 'js', and is set by explicitly adding the format string to the end of the URL (/player_emails.js) or (more likely) by deducing the format from the request header.
In your case, the create action does not expect anything other than AJAX, so it takes a shortcut and omits the respond_to and format blocks. The controller has already figured out that the format is 'js', so when create is complete it takes the default action of rendering the appropriate template for the format (create.js.erb).
Does your form submit button have a :remote => true on it? If so, there might be some JavaScript in rails.js or application.js that automatically submits via AJAX. Bottom line is, there has to be some JavaScript somewhere that is making an AJAX call and asking for a js or JSON response, otherwise it would be an html request.
As for why you don't need a respond_to block, I'm not entirely sure. Maybe since the call is always being made by AJAX and there is a js.erb template available, it just does its thing without complaining. Is there an html.erb template at all? If not, try doing a regular form submit and see if it complains.
I'm using Backbone.js to create a pure JS front-end for my rails app using a JSON api.
The backbone.js model URL looks like this:
window.MedicationCollection = Backbone.Collection.extend({
model: Medication,
url: 'http://localhost:3000/medication_options'
});
CRUD works fine with this. It's sending requests to my server with an "application/JSON" content type.
But I needed to add authentication for the API and my rails app is using Authlogic so I added this:
before_filter :require_user
def require_http_auth_user
return true if current_user
authenticate_or_request_with_http_basic do |email, password|
email = email.gsub("_at_", "#") if email.include?("_at_")
if #current_user = User.find_by_email(email)
#current_user.valid_password?(password)
else
false
end
end
end
And a typical CRUD method in rails looks like this:
def update
#medication_option = MedicationOption.find(params[:id])
if #medication_option.update_attributes(params[:medication_option])
flash[:notice] = "Successfully updated medication."
end
respond_with #medication_option
end
So in my Backbone.js model I added "username:password" to the url:
window.MedicationCollection = Backbone.Collection.extend({
model: Medication,
url: 'http://username:password#localhost:3000/medication_options'
});
The authentication works fine with the appropriate credentials, but for some reason all of the Backbone.js requests are interpreted by the Rails server as HTML instead of JSON.
For some reason during an Basic HTTP authentication, the JSON header gets lost in the mix.
So if I explicitly add ".json" to the end of the URL like this:
window.MedicationCollection = Backbone.Collection.extend({
model: Medication,
url: 'http://username:password#localhost:3000/medication_options.json'
});
I can get basic GET requests to work.
But when I do CRUD it doesn't work because it appends the ID to the end of the URL, so it errors:
ActionController::RoutingError (No route matches "/medication_options.json/41516")
So I'm not sure where to go from here.
Your .json should always be at the end of the url to be interpreted as json:
/medication_options/41516.json
You can edit the Model url as well as the collection URL if needed to put the json at the right place.
The root problem is why it doesn't get processed as json in the first place. If you are using rails 3 do you have:
respond_to :js
or
respond_to :json
to match your respond_with and make sure your controller accepts this kind of format (otherwise rails would return 406 error by default)?
I've never used Document.js, but in looking through the docs it seems you can override url and return the result of a function instead of giving it a literal string value. Could you add the .json to the end of the url string that way?
Try something like this:
_.extend(Medication,
{extension:'.json',
url:function() {
var base = getUrl(this.collection);
if (this.isNew()) return base + this.extension;
return (base + (base.charAt(base.length - 1) == '/' ? '' : '/')
+ this.id + this.extension);
}
});