I have an ASP.NET MVC3 application published to a url like this:
http://servername.com/Applications/ApplicationName/
In my code, I am using jquery ajax requests like this:
$.get(('a/b/c'), function (data) {}, "json");
When I run the application locally, the ajax request goes directly to the correct page (being an mvc route) because the local page ends with a "/" (localhost/a/b/c).
However, when I publish to http://servername.com/Applications/ApplicationName/, the trailing "/" is not always present. The url could be http://servername.com/Applications/ApplicationName, which then causes the ajax request to try to load http://servername.com/Applications/ApplicationNamea/b/c, which fails for obvious reasons.
I have already looked into rewriting the url to append a trailing slash, but A) It didn't work, and B) I feel like it's a poor solution to the problem, and that it would be better to configure the javascript urls to work properly regardless of the local folder setup.
I did try "../a/b/c" and "/a/b/c", but neither seemed to work.
Thanks in advance for the help!
Personally I tend to use a global variable of the relative URL of the server in my view like:
var BASE_URL = '#Url.Content("~/")';
Then you can do things like :
$.get(BASE_URL + 'a/b/c'), function (data) {}, "json");
I would like to add that if you want it to be totally global, you could add it to your /Views/Shared/_Layout.cshtml instead.
I ran into the same problem, and ended up creating two JavaScript functions that mirror the functionality of the MVC Url helper methods Url.Action and Url.Content. The functions are defined in the _Layout.cshtml file, so are available on all views, and work regardless of whether the application is in the root of the localhost or in a subfolder of a server.
<script type="text/javascript">
function UrlAction(action, controller) {
var url = ('#Url.Action("--Action--","--Controller--")').replace("--Action--", action).replace("--Controller--", controller);
return url;
}
function UrlContent(url) {
var path = "#Url.Content("~/--file--")";
path = path.replace("--file--", url.replace('~/', ''));
return path;
}
</script>
These can then be called like so:
var url = UrlAction('AvailableAssetClasses', 'Assessment');
var url2 = UrlContent('~/Images/calendar.gif');
Always use Url helpers when generating urls in an ASP.NET MVC application and never hardcode them. So if this script is directly inside the view:
<script type="text/javascript">
var url = '#Url.Action("a", "b")';
$.get(url, function (data) {}, "json");
</script>
And if this script is inside a separate javascript file (as it should be) where you don't have access to server side helpers, you could simply put the url in some related DOM element. For example using HTML5 data-* attributes:
<div data-url="#Url.Action("a", "b")" id="foo">Click me</div>
and then in your javascript file:
$('#foo').click(function() {
var url = $(this).data('url');
$.get(url, function (data) {}, "json");
});
and if you are unobtrusively AJAXifying an anchor or a form, well, you already have the url:
$('a#someAnchor').click(function() {
var url = this.href;
$.get(url, function (data) {}, "json");
return false;
});
Related
I am trying to load an external JS file based dynamically in my JS.
I have created a service to do it
angular.module('myApp').service('testService', function($http) {
var prefix;
//codes to determine what prefix is.
// It could be http://partnerapp.com/test or http://linkapp.com/test/libs/...etc
var url = prefix + '/js/test.js';
return $http({
method: 'GET',
url: url
}); //load JS file
});
in my main controller
angular.module('myApp').controller('myController', function($scope, testService){
//I am not sure how to load those js here.
})
Is there anyway I can load them in my case? Thanks a lot!
I don't have much experience with angular.js, but except eval() I see only one other solution an it requires jQuery (if you haven't done this already). I'm referring to jQuery.getScript(url, callback).
This means you'll have to do something like this:
var url = prefix + '/js/test.js';
$.getScript(url); //load JS file and execute script
Callback is optional and it's executed after the script is loaded and interpreted.
This is something I've used and I can guarantee will work. another solution is to create a script tag with src= and append it to the bottom of the page. I haven't tested it yet so i'm not 100% sure of its success.
Something like :
angular.module('myApp').controller('myController', function($scope, testService){
testService.then(function(data){
//you can console.log(data) and after maybe eval it ?
eval(data);//eval can be harmfull
}, function(error){
});
});
I have an asp.net mvc web application , which is deployed under /sites as follow:-
http://servername/sites/
but if i reference the URL as follow:-
url:'/ControllerName/ActionMethodName'
the result will be http://servername/ControllerName/ActionMethodName and NOT http://servername/ControllerName/sites/ActionMethodName
i usually solve this issue inside my razor view by writting the following:-
url: "#Url.Content("~/ControllerName/ActionMethodName")" . but seems that javascript does snot have the same ability. so can anyone advice?
Thanks
There are many ways to solve this, not so elegant.
I prefer to put a Hidden field in _Layout.cshtml (or any other master page)
#Html.Hidden("HiddenCurrentUrl", Url.Content("~"))
In a common js file:
var baseUrl = "";
$(document).ready(function () {
baseUrl = $("#HiddenCurrentUrl").val();
});
Then just refer Urls like this:
$.ajax({
type: 'POST',
url: baseUrl + 'solicitacoes/obtertipo/' + value
})
I'm not sure yet about this practice, but it worked for me (fixing tons of legacy code)
In your javascript file, use variables for all paths.
var ActionSubmit;
In your View include this:
<script>
ActionSubmit = '#(#Url.Content("~/ControllerName/ActionMethodName")';
</script>
I have an MVC 2 application hosting on an IIS6 server. I have already done all the routing tweaks so that it can browse in the environment. The problem is however, that I have a dynamic partial view creation aspect, where a partial view is loaded each time an add button is clicked. Using Javascript and a controller, I call the partial vie and add it to a table each time.
Javascript Code
<script type="text/javascript">
$(function() {
$("#btnAdd").click(function (e) {
var itemIndex = $("#container input.iHidden").length;
console.debug("itemIndex : "+itemIndex);
e.preventDefault();
var URL = "/WorkOrder/NewItem/" +itemIndex;
$.get(URL,function(data){
$("#container").append(data);
});
});
});
and the controller is
public ActionResult NewItem(int id)
{
var interest = new ItemModel { index = id };
return View("_NewItem", interest);
}
Quite simple really. The funny thing is that it works when in the test localhost environment, but as soon as i deploy it to production, the btnAdd function does nothing. After using the inspect element Network debugging tool, I discovered that the network is returning a 404 error for the partial view.
Do i have to tweak the routing tables more to make them recognize the routing regime i am trying to implement?
Try using Url.Action method instead of just hard coding the URI and pass the data using data parameter.
Example:
var URL = '<%= Url.Action("WorkOrder", "NewItem")%>';
$.get( URL,
{id: itemIndex}
function(data){
$("#container").append(data);
});
In my ASP.net web project, I've written the following Javascript code in a .js file:
function getDeviceTypes() {
var deviceTypes;
$.ajax({
async: false,
type: "POST",
url: "Controls/ModelSelectorWebMethods.aspx/getDeviceTypes",
data: '{ }',
contentType: "application/json;",
dataType: "json",
success: function(response) {
deviceTypes = response.d;
},
error: function(xhr, status) {
debugger;
alert('Error getting device types.');
}
}); // end - $.ajax
return deviceTypes;
}
It was working great until I tried to load this .js file into a page in a subdirectory.
Let's suppose that the name of my project is widget.
When I use this code in the main virtual directory, Javascript interprets Controls/ModelSelectorWebMethods.aspx/getDeviceTypes to mean https://mysite.com/widget/Controls/ModelSelectorWebMethods.aspx/getDeviceTypes and all is well. However, from the page in a subdirectory, Javascript interprets it to mean https://mysite.com/widget/subdirectory/Controls/ModelSelectorWebMethods.aspx/getDeviceTypes and it doesn't work.
How can I write my Javascript code so that the AJAX web method can be called from pages in any directory in my application?
You've got two options:
Build a configuration/ preferences object in JavaScript which contains all your environment specific settings:
var config = {
base: <% /* however the hell you output stuff in ASPX */ %>,
someOtherPref: 4
};
and then prefix the AJAX url with config.base (and change the value for config.base whether you're on a dev/ testing/ deployment server.)
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.
Obviously the config object will have to be included in a part of your site where server-side-code is evaluated; a .js file won't cut it without configuring your server. I always include the config object in the HTML <head>; its a small config object, whose contents can change on each page, so it's perfectly warrented to stick it in there.
As long as you don't care about asp.net virtual directories (which makes it actually impossible to figure out from script, you'll have to pass something from the server) you can look at the URL and parse it:
function baseUrl() {
var href = window.location.href.split('/');
return href[0]+'//'+href[2]+'/';
}
then:
...
url: baseUrl()+"Controls/ModelSelectorWebMethods.aspx/getDeviceTypes",
...
... and now I see from your comments above that virtual directories are a problem. I usually do this.
1) In your masterpage, put code to inject a script somewhere, preferably before anything else (I add it directly to HEAD by adding controls instead of using ScriptManager) to make sure it's run before any other script. c#:
string basePath = Request.ApplicationPath;
// Annoyingly, Request.ApplicationPath is inconsistent about trailing slash
// (if not root path, then there is no trailing slash) so add one to ensure
// consistency if needed
string myLocation = "basePath='" + basePath + basePath=="/"?"":"/" + "';";
// now emit myLocation as script however you want, ideally in head
2) Change baseUrl to include that:
function baseUrl() {
var href = window.location.href.split('/');
return href[0]+'//'+href[2]+basePath;
}
Create an app root variable...
var root = location.protocol + "//" + location.host;
And use an absolute URI (instead of relative) when you are making AJAX requests...
url: root + "/Controls/ModelSelectorWebMethods.aspx/getDeviceTypes"
I think this function will work... it is to get a relative path as "../../../"
so if you invoke this function in each page, this will return a relative path format.
function getPath() {
var path = "";
nodes = window.location. pathname. split('/');
for (var index = 0; index < nodes.length - 3; index++) {
path += "../";
}
return path;
}
You can import the namespace at the beginning: System.Web.Hosting.HostingEnvironment
<%# Master Language="VB" AutoEventWireup="false" CodeFile="Site.master.vb" Inherits="Site" %>
<%# Import namespace="System.Web.Hosting.HostingEnvironment" %>
and on js:
<script type="text/javascript">
var virtualpathh = "<%=ApplicationVirtualPath %>";
</script>
Could you use window.location.pathname?
var pathname = window.location.pathname;
$.ajax({
//...
url: pathname + 'Controls/...', // might need a leading '/'
//...
});
In my project I have a lot of Ajax methods, with external client-side scripts (I don't want to include JavaScript into templates!) and changing URLs is kind of pain for me because I need to change URLs in my Ajax calls manually.
Is there is some way to emulate the behavior of {% url %} templatetag in JavaScript?
For example, print urlpatterns starting with ^ajax and later in scripts replace patterns with their actual values?
That's what on my mind, and my question is - are there any common practices to do things like that? Maybe some reusable applications? Also I will be happy to read any advices and relevant thoughts you have.
Update 1:
I'm talking about computed URLs, not static ones:
url(r'^ajax/delete/(?P<type>image|audio)/(?P<item_id>\d+)/from/set/(?P<set_id>\d+)/$', 'blog.ajax.remove_item_from_set'),
Try creating javascript helper functions (in django template) for generating url string. In simple form they could look like this:
function generete_some_url(id){
return "{% url some_url itemid=112233 %}".replace("112233", id);
}
Maybe this has some other implications but I think it should work.
What's wrong with putting JavaScript in your templates?
You often want to call an initialisation function in your HTML template anyway, so why not pass it an object containing URLs you'll be using?
<script>
MYGLOBAL.mymodule.init({
fancy_ajax_url: '{% url fancy %}',
fancier_ajax_url: '{% url fancier %}'
});
</script>
If you find yourself passing a lot of variable this way, or wanting to use logic in your JavaScript that you do in your HTML templates, then why not render your script through Django's templating engine? Remember, Django templates are not just for HTML documents - often it helps to use templates for plain text, XML, JSON, and yes even JavaScript. Worried about performance? Then cache the result.
I created a mechanism that builds a list of url patterns in your Django project and outputs that in a Javascript file. It is a fork of django-js-utils.
The repo link is here:
https://github.com/Dimitri-Gnidash/django-js-utils
https://github.com/mlouro/django-js-utils
dutils is a small utility library that aims to provide JavaScript/Django developers with a few utilities that will help the development of RIA on top of a Django Backend.
It currently supports the following features:
Reverse method for generating Django urls...
We created a small app called django-js-reverse for this purpose.
For example you can retrieve a named url
urls.py:
url(r'^/betterliving/(?P[-\w]+)/(?P\d+)/$', 'get_house', name='betterliving_get_house'),
in javascript like:
Urls.betterliving_get_house('house', 12)
result:
/betterliving/house/12/
What I usually do is put the URL in either an <input type="hidden" /> element, or in the rel="" attribute.
Then, when writing the JS (using jQuery below) I do:
$('div#show_more').click(function () {
var url = $(this).attr('rel');
// or
var url = $('#more_url').val();
$.get(url, function () { /* ... */ });
});
Nonstandard attributes are well supported by all major browsers and hidden elements don't have to be in forms.
First, you should name your url:
url(r'^blog/(?P<item_id>\d+)/$', 'blog.ajax.remove_item', name='blog-item'),
Then you could pass urls as variables to your module:
<script src="{{ STATIC_URL }}js/my-module.js"></script>
<script>
$(function(){
MyModule.init('{% url blog-item item.id %}');
});
</script>
// js/my-module.js
var MyModule = {
init: function(url) {
console.log(url);
}
};
You could use tokens in your url:
<script src="{{ STATIC_URL }}js/my-module.js"></script>
<script>
$(function(){
MyModule.init("{% url blog-item item_id='0000' %}");
});
</script>
// js/my-module.js
var MyModule = {
init: function(url) {
var id = 1;
this._url = url;
console.log(this.url(id));
},
url: function(id) {
return this._url.replace('0000', id);
}
};
Notice that your token should match the regex type to resolve successfully (I can't use {item_id} as token because it's defined with \d+).
I was a little bit unsatisfied with this solution and I ended by writing my own application to handle javascript with django: django.js. With this application, I can do:
{% load js %}
{% django_js %}
{% js "js/my-module.js" %}
// js/my-module.js
var MyModule = {
init: function() {
var id = 1;
console.log(Django.url('blog-item', id));
}
};
$(function(){
MyModule.init();
});
You can remove the parameters from the URL, and pass the dynamic parts as query parameters:
$('#add-choice-button').on('click', function () {
var thing_id = $(this).closest('.thing').attr('data-item-id');
$.get('{% url 'addThing' %}?t='+thing_id, function (data) {
...
});
});
I have found this cool django app called Django JS reverse
https://github.com/ierror/django-js-reverse
If you have a url like
url(r'^/betterliving/(?P<category_slug>[-\w]+)/(?P<entry_pk>\d+)/$', 'get_house', name='betterliving_get_house'),
Then you do
Urls.betterliving_get_house('house', 12)
The result is
/betterliving/house/12/