Relative URLs in javascript/jquery & rails 3 - javascript

I have a URL like this for each record:
http://localhost:3000/items/3/stuff.json
http://localhost:3000/items/1/stuff.json
http://localhost:3000/items/4/stuff.json
http://localhost:3000/items/9/stuff.json
when on a page such as http://localhost:3000/items/3/, the linked JavaScript file has code like this:
$.getJSON(document.URL+'/stuff.json');
this allows me to grab the JSON file without worrying about the record ID number.
The problem arises on a URLs such as:
http://localhost:3000/items/3/news
http://localhost:3000/items/3/photos
as this:
$.getJSON(document.URL+'/stuff.json');
will be looking for:
http://localhost:3000/items/3/news/stuff.json
which of course, does not exist.
Any ideas for how I should solve this?

You can use
$.getJSON("../stuff.json", function(data){
});
if current url is "http://localhost:3000/items/3/anything"
EDIT:
$.getJSON(document.URL.match(/\/items\/\d+\//) + "stuff.json", function(data){
});
If all the urls are of the form http://host/items/id/anything

If this JS is rendered at all by Rails (i.e. it's not something that comes from the Asset Pipeline) then you can use the routing helpers, like this:
<script>$.getJSON("<%= item_news_stuff_path(3, :format => :json) %>")</script>
This would generate a relative-to-root path to the resource, building the route up correctly.

Related

how to change url for javascript in opencart

i have been working on the opencart frontend. And i want to make a frontend structure such that products uploaded by a particular vendor is shown. For that i use the url 'user/vendor_name'. And i have made changes in htaccess file for such url and after that i have changed the link function in url.php file for such cases. So now if a user clicks anywhere in the website the url will show 'user/vendor_name/index.php......'. But the url in javascript doesnt use $this->link function and those changes of url.php file doesnot take effect and thus it redirects to the original url.
Please help me out on this.
You would have to modify the controllers for each template containing such URLs and make sure You are setting a vendor name to a PHP variable accessible by the template:
$this->data['vendor'] = $vendor_information['name'];
supposing the vendor name is stored in a variable $vendor_name under the index name. This is only an example. Now in each template identify such URL with the JS part:
$.ajax({
url: 'index.php?route=checkout/cart/add' // + ...
// ...
});
and change it to:
$.ajax({
url: 'vendor/<?php echo $vendor; ?>/index.php?route=checkout/cart/add' // + ...
// ...
});
This should solve your problem.

Is it possible to parse Ruby instance variables in coffee script asset files?

I am attempting to pass in instance variable from my rails app to the associated coffeescript file, but it currently does not seem to be parsing. What am I missing?
locations.js.coffee.erb
$.ajax
url: "/map_groups/<%= #id %>.json"
type: "get"
dataType: "json"
async: false
success: (response) ->
exports.response = response
locations_controller.rb
def index
#id = (params[:id]) ? params[:id] : 1
#locations = Location.all
end
But this is the error showing up in the console:
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:3000/map_groups/.json
Is there something I can do to get the instance variable to parse?
note:
I know the variable exists, because it is being passed to the view.
edit: What I am trying to do
Most of my data is being sent via JSON, and I have created a custom route in order to let the coffeescript know what json data to pull:
get "/locations/map-group/:id", controller: :locations, action: :index, as: :map_group
If you refer back to my controller - you will see that if a user visits plain old /locations the ID defaults to 1. Otherwise, the id is whatever gets specified in the URL. The coffeescript file needs to pull data relevant to that ID through an AJAX call. How can I tell the coffeescript what that ID is?
What I would do instead
I would strongly recommend not using Ruby instance variables to generate CoffeeScript, if you can avoid it. I would suggest using a library like this to handle the use case you are considering:
https://github.com/railsware/js-routes
# Configuration above will create a nice javascript file with Routes object that has all the rails routes available:
Routes.users_path() // => "/users"
Routes.user_path(1) // => "/users/1"
Routes.user_path(1, {format: 'json'}) // => "/users/1.json"
Routes.new_user_project_path(1, {format: 'json'}) // => "/users/1/projects/new.json"
Routes.user_project_path(1,2, {q: 'hello', custom: true}) // => "/users/1/projects/2?q=hello&custom=true"
Routes.user_project_path(1,2, {hello: ['world', 'mars']}) // => "/users/1/projects/2?hello%5B%5D=world&hello%5B%5D=mars"
This plus HTML5 data-* tags would help you pass along the identifying information you need in your JavaScript:
http://html5doctor.com/html5-custom-data-attributes/
For example:
<div id="my_awesome_location" data-location-id="<%= #location.id %>">...</div>
Then load your ajax like this:
id = $("#my_awesome_location").data('location-id')
$.ajax
url: Routes.map_group_path(id) #=> "/map_groups/#{id}.json"
type: "get"
dataType: "json"
...
How to do it anyways
However, if you absolutely must use ERB style tags in your CoffeeScript, you can do it by using coffee.erb file extension:
http://guides.rubyonrails.org/asset_pipeline.html#coding-links-to-assets
2.3.3 JavaScript/CoffeeScript and ERB
If you add an erb extension to a JavaScript asset, making it something such as application.js.erb, then you can use the asset_path helper in your JavaScript code:
$('#logo').attr({
src: "<%= asset_path('logo.png') %>"
});
This writes the path to the particular asset being referenced.
Similarly, you can use the asset_path helper in CoffeeScript files with erb extension (e.g., application.js.coffee.erb):
$('#logo').attr src: "<%= asset_path('logo.png') %>"
Why you probably don't want to do it that way
The reason I would suggest using the above library versus straight ERB is the tight coupling that you have between controller/views and assets.
It also means that you have to have your who entire app pre-loaded before you can do asset compilation, so you'll have that bite you in the behind if you try to deploy to a PaaS like Heroku.
https://devcenter.heroku.com/articles/rails-asset-pipeline
The app’s config vars are not available in the environment during the slug compilation process. Because the app must be loaded to run the assets:precompile task, any initialization code that requires existence of config vars should gracefully handle the nil case.
Basically, if you change your controller even a little bit, you risk breaking your assets. It's best to keep them as separate units and to introduce a mediation layer that handles what you are trying to solve.
As Farley Knight says, yes you can, but please don't for the reasons he stated.
What has worked best for me is hidden fields with the data inside my ERB file. Then since you're using JQuery, just use idField.val() in your url for the $.ajax call.
Hope that helps.

Extracting part of current page's URL in javascript

In ASP.NET MVC 3 site, I open the following uri
http://myserver/incidents/supercompany/register
That page needs to make use of ajax JSON retrieval mechanism with the help of jQuery:
$.getJSON('/Incidents/[[COMPANY NAME GOES HERE]]/SearchPeople/' + request.term, function (data)
I am new to javascript. How can I obtain supercompany part of my current url to build the
/Incidents/supercompany/SearchPeople/ string?
Assuming your URLs follow a logical structure, you can do this
var URLparts = window.location.toString().substr(7).split('/');
And you can now access URLparts[1] to get the company name. Obviously, you need to be able to know that that will ALWAYS be there, but it's a solution if you can guarantee that (eg. with htaccess).

how not to hard-code codeigniter link in js file

i wrote the routine below to retrieve city based on selected country for my codeigniter application.
$(document).ready(function() {
$("select#cbo_country").change(function() {
$.post("http://localhost/main/index.php/city/get_data_by_country", {
int_country_id : $(this).val()
},
function(data) {
// some code here
},'json');
})
});
as you can see, i hard-coded the url (http://localhost/main/index.php/city/get_data_by_country) and i know it's a bad practice but i can't help it.
is there a nice clean way to not hard-code the url? i used to use codeigniter's base_url(), but since i move the routine to a js file, i am no longer able to use the function.
Taken (mostly) from my answer on How to get relative path in Javascript?.
You've got two options:
Build a configuration/ preferences object in JavaScript which contains all your environment specific settings:
var config = {
base: "<?php echo base_url(); ?>",
someOtherPref: 4
};
and then prefix the AJAX url with config.base.
You have to place the config object in a place which is parsed by PHP; the standard choice is inside the <head> HTML element. Don't worry its a small config object, whose contents can change on each page, so it's perfectly warrented to stick it in there.
Use the <base /> HTML tag to set the URL prefix for all relative URL's. This affects all relative URL's: image's, links etc.
Personally, I'd go for option 1. You'll most likely find that config object coming in handy elsewhere.
Change "http://localhost/main/index.php/city/get_data_by_country" to "/main/index.php/city/get_data_by_country" and it will work no matter what your base url is.
This works because the / before main/index.php says "start at the root and go the the index.php file in the main folder".
This is unless your document root folder is set to the main folder, if so, take out the main
One thing you can do is add a data-baseurl attribute to an element on the page (the body element works). Then you can grab that from the JavaScript file.
<body data-baseurl="<?=base_url()?>">
Then in JavaScript:
$("select#cbo_country").change(function() {
var baseURL = $('body').data('baseurl')
$.post(baseURL+"city/get_data_by_country", {
int_country_id : $(this).val()
},
function(data) {
// some code here
},'json');
})

Calling Django `reverse` in client-side Javascript

I'm using Django on Appengine. I'm using the django reverse() function everywhere, keeping everything as DRY as possible.
However, I'm having trouble applying this to my client-side javascript. There is a JS class that loads some data depending on a passed-in ID. Is there a standard way to not-hardcode the URL that this data should come from?
var rq = new Request.HTML({
'update':this.element,
}).get('/template/'+template_id+'/preview'); //The part that bothers me.
There is another method, which doesn't require exposing the entire url structure or ajax requests for resolving each url. While it's not really beautiful, it beats the others with simplicity:
var url = '{% url blog_view_post 999 %}'.replace (999, post_id);
(blog_view_post urls must not contain the magic 999 number themselves of course.)
Having just struggled with this, I came up with a slightly different solution.
In my case, I wanted an external JS script to invoke an AJAX call on a button click (after doing some other processing).
In the HTML, I used an HTML-5 custom attribute thus
<button ... id="test-button" data-ajax-target="{% url 'named-url' %}">
Then, in the javascript, simply did
$.post($("#test-button").attr("data-ajax-target"), ... );
Which meant Django's template system did all the reverse() logic for me.
The most reasonable solution seems to be passing a list of URLs in a JavaScript file, and having a JavaScript equivalent of reverse() available on the client. The only objection might be that the entire URL structure is exposed.
Here is such a function (from this question).
Good thing is to assume that all parameters from JavaScript to Django will be passed as request.GET or request.POST. You can do that in most cases, because you don't need nice formatted urls for JavaScript queries.
Then only problem is to pass url from Django to JavaScript. I have published library for that. Example code:
urls.py
def javascript_settings():
return {
'template_preview_url': reverse('template-preview'),
}
javascript
$.ajax({
type: 'POST',
url: configuration['my_rendering_app']['template_preview_url'],
data: { template: 'foo.html' },
});
Similar to Anatoly's answer, but a little more flexible. Put at the top of the page:
<script type="text/javascript">
window.myviewURL = '{% url myview foobar %}';
</script>
Then you can do something like
url = window.myviewURL.replace('foobar','my_id');
or whatever. If your url contains multiple variables just run the replace method multiple times.
I like Anatoly's idea, but I think using a specific integer is dangerous. I typically want to specify an say an object id, which are always required to be positive, so I just use negative integers as placeholders. This means adding -? to the the url definition, like so:
url(r'^events/(?P<event_id>-?\d+)/$', events.views.event_details),
Then I can get the reverse url in a template by writing
{% url 'events.views.event_details' event_id=-1 %}
And use replace in javascript to replace the placeholder -1, so that in the template I would write something like
<script type="text/javascript">
var actual_event_id = 123;
var url = "{% url 'events.views.event_details' event_id=-1 %}".replace('-1', actual_event_id);
</script>
This easily extends to multiple arguments too, and the mapping for a particular argument is visible directly in the template.
I've found a simple trick for this. If your url is a pattern like:
"xyz/(?P<stuff>.*)$"
and you want to reverse in the JS without actually providing stuff (deferring to the JS run time to provide this) - you can do the following:
Alter the view to give the parameter a default value - of none, and handle that by responding with an error if its not set:
views.py
def xzy(stuff=None):
if not stuff:
raise Http404
... < rest of the view code> ...
Alter the URL match to make the parameter optional: "xyz/(?P<stuff>.*)?$"
And in the template js code:
.ajax({
url: "{{ url views.xyz }}" + js_stuff,
... ...
})
The generated template should then have the URL without the parameter in the JS, and in the JS you can simply concatenate on the parameter(s).
Use this package: https://github.com/ierror/django-js-reverse
You'll have an object in your JS with all the urls defined in django. It's the best approach I found so far.
The only thing you need to do is add the generated js in the head of your base template and run a management command to update the generated js everytime you add a url
One of the solutions I came with is to generate urls on backend and pass them to browser somehow.
It may not be suitable in every case, but I have a table (populated with AJAX) and clicking on a row should take the user to the single entry from this table.
(I am using django-restframework and Datatables).
Each entry from AJAX has the url attached:
class MyObjectSerializer(serializers.ModelSerializer):
url = SerializerMethodField()
# other elements
def get_url(self, obj):
return reverse("get_my_object", args=(obj.id,))
on loading ajax each url is attached as data attribute to row:
var table = $('#my-table').DataTable({
createdRow: function ( row, data, index ) {
$(row).data("url", data["url"])
}
});
and on click we use this data attribute for url:
table.on( 'click', 'tbody tr', function () {
window.location.href = $(this).data("url");
} );
I always use strings as opposed to integers in configuring urls, i.e.
instead of something like
... r'^something/(?P<first_integer_parameter>\d+)/something_else/(?P<second_integer_parameter>\d+)/' ...
e.g: something/911/something_else/8/
I would replace 'd' for integers with 'w' for strings like so ...
... r'^something/(?P<first_integer_parameter>\w+)/something_else/(?P<second_integer_parameter>\w+)/' ...
Then, in javascript I can put strings as placeholders and the django template engine will not complain either:
...
var url = `{% url 'myapiname:urlname' 'xxz' 'xxy' %}?first_kwarg=${first_kwarg_value}&second_kwarg=${second_kwarg_value}`.replace('xxz',first_integer_paramater_value).replace('xxy', second_integer_parameter_value);
var x = new L.GeoJSON.AJAX(url, {
style: function(feature){
...
and the url will remain the same, i.e something/911/something_else/8/.
This way you avoid the integer parameters replacement issue as string placeholders (a,b,c,d,...z) are not expected in as parameters

Categories

Resources