I want to use AngularJS with Django however they both use {{ }}
as their template tags. Is there an easy way to change one of the two to use some other custom templating tag?
templates
directory, the rest I put in static
. That way you don't have interference. There's a tutorial I wrote here: coderwall.com/p/bzjuka/…
For Angular 1.0 you should use the $interpolateProvider apis to configure the interpolation symbols: http://docs.angularjs.org/api/ng.$interpolateProvider.
Something like this should do the trick:
myModule.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
Keep in mind two things:
mixing server-side and client-side templates is rarely a good idea and should be used with caution. The main issues are: maintainability (hard to read) and security (double interpolation could expose a new security vector - e.g. while escaping of serverside and clientside templating by themselves might be secure, their combination might not be).
if you start using third-party directives (components) that use {{ }} in their templates then your configuration will break them. (fix pending)
While there is nothing we can do about the first issue, except for warning people, we do need to address the second issue.
you can maybe try verbatim Django template tag and use it like this :
{% verbatim %}
10 is {{ 5 + 5 }}
json_script
built in template tag. This creates a <script />
tag with a given ID that you can then load in your javascript with no vectors for injection.
If you did separate sections of page properly then you can easily use angularjs tags in "raw" tag scope.
In jinja2
{% raw %}
// here you can write angularjs template tags.
{% endraw %}
In Django template (above 1.5)
{% verbatim %}
// here you can write angularjs template tags.
{% endverbatim %}
We created a very simple filter in Django 'ng' that makes it easy to mix the two:
foo.html:
...
<div>
{{ django_context_var }}
{{ 'angularScopeVar' | ng }}
{{ 'angularScopeFunction()' | ng }}
</div>
...
The ng
filter looks like this:
from django import template
from django.utils import safestring
register = template.Library()
@register.filter(name='ng')
def Angularify(value):
return safestring.mark_safe('{{%s}}' % value)
So I got some great help in the Angular IRC channel today. It turns out you can change Angular's template tags very easily. The necessary snippets below should be included after your angular include (the given example appears on their mailing lists and would use (())
as the new template tags, substitute for your own):
angular.markup('(())', function(text, textNode, parentElement){
if (parentElement[0].nodeName.toLowerCase() == 'script') return;
text = text.replace(/\(\(/g,'{{').replace(/\)\)/g, '}}');
textNode.text(text);
return angular.markup('{{}}').call(this, text, textNode, parentElement);
});
angular.attrMarkup('(())', function(value, name, element){
value = value.replace(/\(\(/g,'{{').replace(/\)\)/, '}}');
element[0].setAttribute(name, value);
return angular.attrMarkup('{{}}').call(this, value, name, element);
});
Also, I was pointed to an upcoming enhancement that will expose startSymbol
and endSymbol
properties that can be set to whatever tags you desire.
I vote against using double parentheses (()) as template tag. It may work well as long as no function call is involved but when tried the following
ng:disabled=(($invalidWidgets.visible()))
with Firefox (10.0.2) on Mac I got a terribly long error instead of the intended logic. <[]> went well for me, at least up until now.
Edit 2012-03-29: Please note that $invalidWidgets is deprecated. However I'd still use another wrapper than double braces. For any angular version higher than 0.10.7 (I guess) you could change the wrapper a lot easier in your app / module definition:
angular.module('YourAppName', [], function ($interpolateProvider) {
$interpolateProvider.startSymbol('<[');
$interpolateProvider.endSymbol(']>');
});
(())
, I just wanted to be able to configure the delimiters.
I found the code below helpful. I found the code here: http://djangosnippets.org/snippets/2787/
"""
filename: angularjs.py
Usage:
{% ng Some.angular.scope.content %}
e.g.
{% load angularjs %}
<div ng-init="yourName = 'foobar'">
<p>{% ng yourName %}</p>
</div>
"""
from django import template
register = template.Library()
class AngularJS(template.Node):
def __init__(self, bits):
self.ng = bits
def render(self, ctx):
return "{{%s}}" % " ".join(self.ng[1:])
def do_angular(parser, token):
bits = token.split_contents()
return AngularJS(bits)
register.tag('ng', do_angular)
<p>{% ng location %}</p>
it gets rendered as {{location}}
- yes with curly braces! It doesn't render the value of $scope.location which is hardcoded in my controller. Any idea what am I missing?
You could always use ng-bind instead of {{ }} http://docs.angularjs.org/api/ng/directive/ngBind
<span ng-bind="name"></span>
If you use django 1.5 and newer use:
{% verbatim %}
{{if dying}}Still alive.{{/if}}
{% endverbatim %}
If you are stuck with django 1.2 on appengine extend the django syntax with the verbatim template command like this ...
from django import template
register = template.Library()
class VerbatimNode(template.Node):
def __init__(self, text):
self.text = text
def render(self, context):
return self.text
@register.tag
def verbatim(parser, token):
text = []
while 1:
token = parser.tokens.pop(0)
if token.contents == 'endverbatim':
break
if token.token_type == template.TOKEN_VAR:
text.append('{{')
elif token.token_type == template.TOKEN_BLOCK:
text.append('{%')
text.append(token.contents)
if token.token_type == template.TOKEN_VAR:
text.append('}}')
elif token.token_type == template.TOKEN_BLOCK:
text.append('%}')
return VerbatimNode(''.join(text))
In your file use:
from google.appengine.ext.webapp import template
template.register_template_library('utilities.verbatim_template_tag')
Source: http://bamboobig.blogspot.co.at/2011/09/notebook-using-jquery-templates-in.html
from django import template
to: from google.appengine._internal.django import template
Then, in my main file, just changed the filename: template.register_template_library('utilities.verbatim_template_tag')
You can tell Django to output {{
and }}
, as well as other reserved template strings by using the {% templatetag %}
tag.
For instance, using {% templatetag openvariable %}
would output {{
.
I would stick with a solution that uses both django tags {{}} as well angularjs {{}} with a either a verbatim section or templatetag.
That is simply because you can change the way angularjs works (as mentioned) via the $interpolateProvider.startSymbol $interpolateProvider.endSymbol but if you start to use other angularjs components like the ui-bootstrap you will find that some of the templates are ALREADY built with standard angularjs tags {{ }}.
For example look at https://github.com/angular-ui/bootstrap/blob/master/template/dialog/message.html.
If you do any server-side interpolation, the only correct way to do this is with <>
$interpolateProvider.startSymbol('<{').endSymbol('}>');
Anything else is an XSS vector.
This is because any Angular delimiters which are not escaped by Django can be entered by the user into the interpolated string; if someone sets their username as "{{evil_code}}", Angular will happily run it. If you use a character than Django escapes, however, this won't happen.
Success story sharing
$interpolateProvider.startSymbol('{[{').endSymbol('}]}');