Related
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 some working JS code which I put on the sections of my create and edit views, and it's working fine.
However, when I attempted to move the code to a separate JS file, the code would no longer call the controller action.
Here the JS code:
<g:javascript>
$(document).ready(function(){
<g:remoteFunction controller="project" action="ajaxGetClient" onSuccess="updateClient(data)"/>
});
function updateClient(data){
var element = $("#project\\.client");
element.empty();
element.val(data.name);
}
</g:javascript>
Here's the controller action:
def ajaxGetClient = {
if(!params.id){
params.id = Project.find("FROM Project ORDER BY id").id
}
def projectInstance = Project.get(params.id)
render projectInstance?.client as JSON
}
And here's the GSP code:
<g:textField name="project.client" id="project.client" maxlength="9" required="" disabled=""/>
<g:select id="project" name="project.id" from="${myPackage.Project.list()}" optionKey="id" required="" value="${productInstance?.project?.id}" class="many-to-one"
onchange="${
remoteFunction(
controller: 'project',
action: 'ajaxGetClient',
onSuccess: 'updateClient(data)',
params: '\'id=\' + this.value'
)}"
/>
I added a resource to ApplicationResources.groovy and changed the above JS code to this:
<g:javascript library="updateclient"/>
I simply copy/pasted the code into a JS file and then got a message:
Uncaught SyntaxError: Unexpected token <
which I understood came from it not recognizing the GSP syntax, so I tried some AJAX, which I'm pretty unexperienced at:
$(document).ready(function(){
$.ajax({
type: 'POST',
url: "${remoteFunction(controller:'project', action:'ajaxGetClient', onSuccess:'updateClient(data)')}"
});
});
Here's what I'm getting from the browser console:
http://localhost:8080/MyApp/product/$%7BremoteFunction(controller:'project',%20action:'ajaxGetClient',%20onSuccess:'updateClient(data)')%7D 404 (Not Found)
Quite frankly, I'm at a loss right now. Any help would be appreciated.
The reason for this is that Javascript (.js) and other non GSP (.gsp) files aren't parsed through the Groovy server pages engine. Thus, tag libraries such as the or ${g.remoteFunction} aren't parsed.
There are several ways to accomplish this however.
One is to keep the code in your GSP and not externalize it into javascript files.
Second is to move your code into javascript files but have configuration values in your GSP file. Here is a very simple example of using the message taglib:
// inside the .js file
function myFunction() {
console.log("I would use this value: "+_VALUE_FROM_GSP);
}
<script type="text/javascript">
// inside the .gsp file
var _VALUE_FROM_GSP = "${message(code: 'just.an.example')";
</script>
Finally, there plugins (listed below) that allows you specify some resources (javascript files in your case) to be parsed through the Groovy Server Pages engine.
GSP-arse plugin and GSP Resources
I'll be answering my own question here, since the bulk of it came from valuable advice from a friend of mine, but Joshua's answer was also very important, so I ended up combining both of them.
This is how I solved it:
On the GSP:
<script type="text/javascript">
var _URL = '${resource(dir: "")}/project/ajaxGetClient';
</script>
<g:javascript library="updateclient"/>
The reason I'm using the <script> tag is because in order for the _URL variable to become usable across different files, it had to be declared before the file using it. At least that's what this other SO answer said:
Global variables in Javascript across multiple files
Separate JS file:
$(document).ready(function(){
getClientAjax(null);
});
function getClientAjax(id) {
$.ajax({
url: _URL,
type: "POST",
data: { id: id },
success: function(data) {
updateClient(data);
}
});
}
function updateClient(data){
var element = $("#project\\.client");
element.empty();
element.val(data.name);
}
And the controller action remained the same.
At the end, there had to be created another JS function, but I gotta say I'm happy with the result.
Thanks for all the help.
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;
});
I use Underscore template. It is possible to attach a external file as template?
In Backbone View I have:
textTemplate: _.template( $('#practice-text-template').html() ),
initialize: function(){
this.words = new WordList;
this.index = 0;
this.render();
},
In my html is:
<script id="practice-text-template" type="text/template">
<h3>something code</h3>
</script>
It works well. But I need external template.
I try:
<script id="practice-text-template" type="text/template" src="templates/tmp.js">
or
textTemplate: _.template( $('#practice-text-template').load('templates/tmp.js') ),
or
$('#practice-text-template').load('templates/tmp.js', function(data){ this.textTemplate = _.template( data ) })
but it did not work.
Here is a simple solution:
var rendered_html = render('mytemplate', {});
function render(tmpl_name, tmpl_data) {
if ( !render.tmpl_cache ) {
render.tmpl_cache = {};
}
if ( ! render.tmpl_cache[tmpl_name] ) {
var tmpl_dir = '/static/templates';
var tmpl_url = tmpl_dir + '/' + tmpl_name + '.html';
var tmpl_string;
$.ajax({
url: tmpl_url,
method: 'GET',
dataType: 'html', //** Must add
async: false,
success: function(data) {
tmpl_string = data;
}
});
render.tmpl_cache[tmpl_name] = _.template(tmpl_string);
}
return render.tmpl_cache[tmpl_name](tmpl_data);
}
Using "async: false" here is not a bad way because in any case you must wait until template will be loaded.
So, "render" function
allows you to store each template in separate html file in static
dir
is very lightweight
compiles and caches templates
abstracts template loading logic. For example, in future you can use preloaded and precompiled templates.
is easy to use
[I am editing the answer instead of leaving a comment because I believe this to be important.]
if templates are not showing up in native app, and you see HIERARCHY_REQUEST_ERROR: DOM Exception 3, look at answer by Dave Robinson to What exactly can cause an "HIERARCHY_REQUEST_ERR: DOM Exception 3"-Error?.
Basically, you must add
dataType: 'html'
to the $.ajax request.
EDIT: This answer is old and outdated. I'd delete it, but it is the "accepted" answer. I'll inject my opinion instead.
I wouldn't advocate doing this anymore. Instead, I would separate all templates into individual HTML files. Some would suggest loading these asynchronously (Require.js or a template cache of sorts). That works well on small projects but on large projects with lots of templates, you find yourself making a ton of small async requests on page load which I really dislike. (ugh... ok, you can get around it with Require.js by pre-compiling your initial dependencies with r.js, but for templates, this still feels wrong to me)
I like using a grunt task (grunt-contrib-jst) to compile all of the HTML templates into a single templates.js file and include that. You get the best of all worlds IMO... templates live in a file, compilation of said templates happen at build time (not runtime), and you don't have one hundred tiny async requests when the page starts up.
Everything below is junk
For me, I prefer the simplicity of including a JS file with my template. So, I might create a file called view_template.js which includes the template as a variable:
app.templates.view = " \
<h3>something code</h3> \
";
Then, it is as simple as including the script file like a normal one and then using it in your view:
template: _.template(app.templates.view)
Taking it a step further, I actually use coffeescript, so my code actually looks more like this and avoid the end-of-line escape characters:
app.templates.view = '''
<h3>something code</h3>
'''
Using this approach avoids brining in require.js where it really isn't necessary.
This mixin allows you to render external template using Underscore in very simple way: _.templateFromUrl(url, [data], [settings]). Method API is almost the same as Underscore's _.template(). Caching included.
_.mixin({templateFromUrl: function (url, data, settings) {
var templateHtml = "";
this.cache = this.cache || {};
if (this.cache[url]) {
templateHtml = this.cache[url];
} else {
$.ajax({
url: url,
method: "GET",
async: false,
success: function(data) {
templateHtml = data;
}
});
this.cache[url] = templateHtml;
}
return _.template(templateHtml, data, settings);
}});
Usage:
var someHtml = _.templateFromUrl("http://example.com/template.html", {"var": "value"});
I didn't want to use require.js for this simple task, so I used modified koorchik's solution.
function require_template(templateName, cb) {
var template = $('#template_' + templateName);
if (template.length === 0) {
var tmpl_dir = './templates';
var tmpl_url = tmpl_dir + '/' + templateName + '.tmpl';
var tmpl_string = '';
$.ajax({
url: tmpl_url,
method: 'GET',
contentType: 'text',
complete: function (data, text) {
tmpl_string = data.responseText;
$('head').append('<script id="template_' + templateName + '" type="text/template">' + tmpl_string + '<\/script>');
if (typeof cb === 'function')
cb('tmpl_added');
}
});
} else {
callback('tmpl_already_exists');
}
}
require_template('a', function(resp) {
if (resp == 'tmpl_added' || 'tmpl_already_exists') {
// init your template 'a' rendering
}
});
require_template('b', function(resp) {
if (resp == 'tmpl_added' || 'tmpl_already_exists') {
// init your template 'b' rendering
}
});
Why to append templates to document, rather than storing them in javascript object? Because in production version I would like to generate html file with all templates already included, so I won't need to make any additional ajax requests. And in the same time I won't need to make any refactoring in my code, as I use
this.template = _.template($('#template_name').html());
in my Backbone views.
This might be slightly off topic, but you could use Grunt (http://gruntjs.com/) - which runs on node.js (http://nodejs.org/, available for all major platforms) to run tasks from the command line. There are a bunch of plugins for this tool, like a template compiler, https://npmjs.org/package/grunt-contrib-jst. See documentation on GitHub, https://github.com/gruntjs/grunt-contrib-jst. (You will also need to understand how to run node package manager, https://npmjs.org/. Don't worry, it's incredibly easy and versatile. )
You can then keep all your templates in separate html files, run the tool to precompile them all using underscore (which I believe is a dependency for the JST plugin, but don't worry, node package manager will auto install dependencies for you).
This compiles all your templates to one script, say
templates.js
Loading the script will set a global - "JST" by default - which is an array of functions, and can be accessed like so:
JST['templates/listView.html']()
which would be similar to
_.template( $('#selector-to-your-script-template'))
if you put the content of that script tag in (templates/)listView.html
However, the real kicker is this: Grunt comes with this task called 'watch', which will basically monitor changes to files that you have defined in your local grunt.js file (which is basically a config file for your Grunt project, in javascript). If you have grunt start this task for you, by typing:
grunt watch
from the command line, Grunt will monitor all changes you make to the files and auto-execute all tasks that you have setup for it in that grunt.js file if it detects changes - like the jst task described above. Edit and then save your files, and all your templates recompile into one js file, even if they are spread out over a number of directories and subdirectories.
Similar tasks can be configured for linting your javascript, running tests, concatenating and minifying / uglifying your script files. And all can be tied to the watch task so changes to your files will automatically trigger a new 'build' of your project.
It takes some time to set things up and understand how to configure the grunt.js file, but it well, well worth the time invested, and I don't think you will ever go back to a pre-grunt way of working
I think this is what might help you. Everything in the solution revolves around require.js library which is a JavaScript file and module loader.
The tutorial at the link above shows very nicely how a backbone project could be organized. A sample implementation is also provided. Hope this helps.
I got interested on javascript templating and now I'm taking the first steps with backbone. This is what i came up with and seems to work pretty well.
window.App = {
get : function(url) {
var data = "<h1> failed to load url : " + url + "</h1>";
$.ajax({
async: false,
url: url,
success: function(response) {
data = response;
}
});
return data;
}
}
App.ChromeView = Backbone.View.extend({
template: _.template( App.get("tpl/chrome.html") ),
render: function () {
$(this.el).html(this.template());
return this;
},
});
App.chromeView = new App.ChromeView({ el : document.body });
App.chromeView.render();
I had to set the data type to "text" to make it work for me:
get : function(url) {
var data = "<h1> failed to load url : " + url + "</h1>";
$.ajax({
async: false,
dataType: "text",
url: url,
success: function(response) {
data = response;
}
});
return data;
}
I found a solution that works for me with using jQuery.
I add the underscore template code, with jQuery.load() method, to the main html file.
Once it's there, I'm using it for generating the templates.
All need to happen synchronously!
The concept is:
I have a underscore map template code:
<!-- MAP TEMPLATE-->
<script type="text/template" id="game-map-template">
<% _.each(rc, function(rowItem, index){ %>
<ul class="map-row" data-row="<%- index %>">
<li class="map-col <%- colItem.areaType ? 'active-area' : '' %>"></li>
...
</script>
And I put that code in a file called map-template.html
After that I create a a wrapper for the template files.
<div id="templatesPool"></div>
Then I include that file in my main html file like so.
In head:
<!-- Template Loader -->
<script>
$(function(){
$("#templatesPool").append($('<div>').load("map-template.html"));
});
</script>
Cheers.
I know this question is really old but it came up as the first result on a google search for underscore ajax templates.
I was tired of not finding a good solution for this so I created my own:
https://github.com/ziad-saab/underscore-async-templates
In addition to loading underscore templates using AJAX, it adds <% include %> functionality. I hope it can be useful to someone.
I was a bit uneasy forcing jQuery to function synchronously, so I modified the previous synchronous example using promises. It's pretty much the same, but runs asynchronously. I'm using hbs templates in this example:
var asyncRenderHbs= function(template_name, template_data) {
if (!asyncRenderHbs.template_cache) {
asyncRenderHbs.template_cache= {};
}
var promise= undefined;
if (!asyncRenderHbs.template_cache[template_name]) {
promise= new Promise(function(resolve, reject) {
var template_url= '/templates/' + template_name;
$.ajax({
url: template_url,
method: 'GET',
success: function(data) {
asyncRenderHbs.template_cache[template_name]= Handlebars.compile(data);
resolve(asyncRenderHbs.template_cache[template_name](template_data));
},
error: function(err, message) {
reject(err);
}
});
});
} else {
promise= Promise.resolve(asyncRenderHbs.template_cache[template_name](template_data));
}
return promise;
};
Then to use the rendered html:
asyncRenderHbs('some_template.hbs', context)
.then(function(html) {
applicationMain.append(html);
// Do other stuff here after html is rendered...
})
.catch(function(err) {
// Handle errors
});
NOTE: As discussed by others, it would be preferable to compile all templates into a single templates.js file and load that in the beginning rather than have many small synchronous AJAX calls to get templates when the webpage loads.
Forward warning - Here be dragons:
I mention the approach shown below simply to help those struggling to make ASP.NET stacks (and similar frameworks) work harmoniously with the ecosystem of js-libs. It goes without saying that this is not a generic solution. Having said that ...
/endforwardwarning
If you are using ASP.NET you can externalize your templates simply by placing them inside one or more partial views of their own. Aka inside your .cshtml:
#Html.Partial("path/to/template")
Inside your template.cshtml:
// this is razorview and thusly if you ever need to use the # character in here
// you will have to either escape it as ## or use the html codepoint which is @
// http://stackoverflow.com/questions/3626250/escape-character-in-razor-view-engine
<script type="text/x-template" id="someId">
<span class="foo"><%= name %></span>
</script>
And now you can use the template like usual:
_.template($("#someId").html())({ name: "Foobar" });
Hope this elusively-obvious approach helps someone save an hour's worth of head-scratching.
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/