AngularJS 与 Django - 模板标签冲突

Posted

技术标签:

【中文标题】AngularJS 与 Django - 模板标签冲突【英文标题】:AngularJS with Django - Conflicting template tags 【发布时间】:2020-04-02 23:14:49 【问题描述】:

我想在 Django 中使用 AngularJS,但是它们都使用 作为模板标签。有没有一种简单的方法可以将两者之一更改为使用其他自定义模板标签?

【问题讨论】:

我只从 django templates 目录渲染一个模板,其余的我放在static 中。这样你就不会受到干扰。我在这里写了一个教程:coderwall.com/p/bzjuka/… 如何在 angular2 和 jinja2 之间传递数据?任何帮助 @Narendra 这是一个与这个问题无关的不同问题。请搜索它,如果找不到答案,请作为新问题提出。 【参考方案1】:

对于 Angular 1.0,您应该使用 $interpolateProvider api 来配置插值符号:http://docs.angularjs.org/api/ng.$interpolateProvider。

这样的事情应该可以解决问题:

myModule.config(function($interpolateProvider) 
  $interpolateProvider.startSymbol('[');
  $interpolateProvider.endSymbol(']');
);

记住两点:

混合服务器端和客户端模板很少是一个好主意,应谨慎使用。主要问题是:可维护性(难以阅读)和安全性(双插值可能会暴露一个新的安全向量 - 例如,虽然服务器端和客户端模板本身的转义可能是安全的,但它们的组合可能不安全)。 如果您开始使用在其模板中使用 的第三方指令(组件),那么您的配置将破坏它们。 (fix pending)

虽然对于第一个问题我们无能为力,除了警告人们,我们确实需要解决第二个问题。

【讨论】:

您介意解释一下您的第一点(维护、安全以及混合服务器端和客户端模板的其他问题)吗?多一点解释会有所帮助。 @btlachance - 我扩展了答案。 因为 $interpolateProvider 在用作 setter 时会返回 self,所以这里有一个更紧凑的版本:$interpolateProvider.startSymbol('[').endSymbol(']'); 看起来“修复”已关闭。这是否意味着现在使用第三方组件不安全? 还有什么方法可以更新 $interpolateProvider 以获取原始输出?例如foo 变成 [foo] ?【参考方案2】:

你可以试试verbatim Django 模板标签 并像这样使用它:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>

% verbatim %
<div ng-app="">
    <p>10 is  5 + 5 </p>
</div>
% endverbatim %

【讨论】:

虽然这是一个非常有效的解决方案,但在某些情况下,我希望能够使用来自服务器的数据引导我的视图,这样会很快变得混乱。想想用户的用户名之类的东西,它不会改变,所以我只是将它写入服务器的模板中,但它周围可能有一些我会用 angular 写入的部分。 Verbatim 从 1.5 版开始成为 Django 核心标签的一部分:docs.djangoproject.com/en/dev/ref/templates/builtins/… 在 Django 1.7 中,您不需要逐字加载,因为它位于标准标记库中。你只需要使用标签本身。 如果有办法从设置中更改 Django 默认括号会很好,但这也有效。 要从 Django 上下文中获取值到 javascript,你应该使用 json_script 内置模板标签。这将创建一个具有给定 ID 的 &lt;script /&gt; 标记,然后您可以将其加载到您的 javascript 中,而无需注入向量。【参考方案3】:

如果您正确地分离了页面的各个部分,那么您可以轻松地在“原始”标签范围内使用 angularjs 标签。

在jinja2中

% raw %
    // here you can write angularjs template tags.
% endraw %

在 Django 模板中(1.5 以上)

% verbatim %    
    // here you can write angularjs template tags.
% endverbatim %

【讨论】:

这个解决方案不会破坏兼容性,因为外部包是公认的答案。【参考方案4】:

我们在 Django 'ng' 中创建了一个 非常 简单的过滤器,可以轻松地将两者混合:

foo.html:

...
<div>
   django_context_var 
   'angularScopeVar' | ng 
   'angularScopeFunction()' | ng 
</div>
...

ng 过滤器如下所示:

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)

【讨论】:

另一种非常有效的方法,但是我宁愿在一个地方更改标签而不是在许多地方添加过滤器...... 如何创建 ng 过滤器?可以举个例子吗? 更新了答案。 @Endophage 我的 Angular 对比 Django 对多 很多,所以我宁愿更新 Django 的。 @WesAlvaro 很遗憾我只能接受一个答案。【参考方案5】:

所以我今天在 Angular IRC 频道中得到了一些很大的帮助。事实证明,您可以非常轻松地更改 Angular 的模板标签。以下必要的 sn-ps 应包含在您的角度包含之后(给定的示例出现在他们的 mailing lists 上,并将使用 (()) 作为新的模板标签,代替您自己的标签):

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);
);

另外,有人指出即将推出的增强功能将公开startSymbolendSymbol 属性,这些属性可以设置为您想要的任何标签。

【讨论】:

这就是你在 angularjs 1.0 中的做法:var m = angular.module('myApp', []); m.config(function($interpolateProvider) $interpolateProvider.startSymbol('(('); $interpolateProvider.endSymbol('))'); ); Angular IRC 频道。不管是谁,我在#angularjs 找到了一个【参考方案6】:

我投票反对使用双括号 (()) 作为模板标签。只要不涉及函数调用,它就可以正常工作,但是尝试以下方法时

ng:disabled=(($invalidWidgets.visible()))

在 Mac 上使用 Firefox (10.0.2) 我得到了一个非常长的错误,而不是预期的逻辑。 对我来说一切顺利,至少到现在为止。

编辑 2012-03-29: 请注意 $invalidWidgets 已被弃用。但是我仍然会使用另一个包装而不是双括号。对于任何高于 0.10.7 的 Angular 版本(我猜),您可以在应用程序/模块定义中更轻松地更改包装器:

angular.module('YourAppName', [], function ($interpolateProvider) 
    $interpolateProvider.startSymbol('<[');
    $interpolateProvider.endSymbol(']>');
); 

API docs.

【讨论】:

公平点。我没有想到这一点,但我并不是特别提倡使用(()),我只是希望能够配置分隔符。【参考方案7】:

我发现下面的代码很有帮助。我在这里找到了代码: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)

【讨论】:

我确实使用了这个自定义标签,但是如果我使用类似的东西:&lt;p&gt;% ng location %&lt;/p&gt; 它会呈现为location - 是的,带有花括号!它不会呈现在我的控制器中硬编码的 $scope.location 的值。知道我错过了什么吗?【参考方案8】:

你总是可以使用 ng-bind 而不是 http://docs.angularjs.org/api/ng/directive/ngBind

<span ng-bind="name"></span>

【讨论】:

【参考方案9】:

如果您使用 django 1.5 及更新版本:

  % verbatim %
    if dyingStill alive./if
  % endverbatim %

如果您在 appengine 上被 django 1.2 卡住,请使用这样的逐字模板命令扩展 django 语法 ...

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))

在您的文件中使用:

from google.appengine.ext.webapp import template
template.register_template_library('utilities.verbatim_template_tag')

来源: http://bamboobig.blogspot.co.at/2011/09/notebook-using-jquery-templates-in.html

【讨论】:

谢谢......终于让它工作了,但我不得不...... 1)创建一个新的Python模块。我将它命名为 utilties 并将文件 verbatim_templatetag.py 放入其中。 (上面的文件中定义了 VerbatimNode 类)。 2) 将导入语句从:from django import template 更改为:from google.appengine._internal.django import template 然后,在我的主文件中,只需更改文件名:template.register_template_library('utilities.verbatim_template_tag')【参考方案10】:

您可以使用% templatetag % 标签告诉 Django 输出,以及其他保留的模板字符串。

例如,使用% templatetag openvariable % 将输出

【讨论】:

我知道这是可能的,但它很混乱......模板标签可以简单地在其中一个框架中进行配置会更简洁(而且看起来也不是太大的要求)。归根结底,它只是在幕后进行字符串匹配......【参考方案11】:

我会坚持使用 django 标签 和 angularjs 以及逐字部分或模板标签的解决方案。

这仅仅是因为您可以通过 $interpolateProvider.startSymbol $interpolateProvider.endSymbol 更改 angularjs 的工作方式(如前所述),但是如果您开始使用其他 angularjs 组件,例如 ui-bootstrap,您会发现一些模板已经使用标准 angularjs 标签 构建。

例如查看https://github.com/angular-ui/bootstrap/blob/master/template/dialog/message.html。

【讨论】:

好点。现在 PyPI 中有一个 django-angular 包,旨在让两者一起玩得很好,但我还没有研究它在多大程度上缓解了模板标签问题。【参考方案12】:

如果您进行任何服务器端插值,唯一正确的方法是使用&lt;&gt;

$interpolateProvider.startSymbol('<').endSymbol('>');

其他任何东西都是 XSS 向量。

这是因为用户可以将任何未被 Django 转义的 Angular 分隔符输入到插值字符串中;如果有人将他们的用户名设置为“evil_code”,Angular will happily run it。但是,如果您使用character than Django escapes,则不会发生这种情况。

【讨论】:

以上是关于AngularJS 与 Django - 模板标签冲突的主要内容,如果未能解决你的问题,请参考以下文章

AngularJS 与 Django - 冲突的模板标签

与 AngularJS 一起使用时,Django 模板不呈现 JSON 响应

如何将 Angular Material 图标资产路径与 Django 模板集成?

Django和Angular.js模板标签冲突的解决方式

angular js 和 dajango 标签{{}} 冲突

将 Django 变量值分配给 AngularJS