AngularJS com Django - Tags de modelo conflitantes

302

Eu quero usar o AngularJS com o Django, no entanto, ambos usam {{ }}como tags de modelo. Existe uma maneira fácil de alterar um dos dois para usar outra tag de modelo personalizada?

Endófago
fonte
1
Eu apenas renderizo um modelo do templatesdiretório django , o resto eu coloco static. Dessa forma, você não tem interferência. Há um tutorial que eu escrevi aqui: coderwall.com/p/bzjuka/...
Connor Sanguessuga
como passar os dados entre angular2 e jinja2? Qualquer ajuda
Narendra 30/10
@ Narendra, esse é um problema diferente, não relevante para esta questão. Por favor, procure-o e, se não encontrar uma resposta, faça-o como uma nova pergunta.
Endofagia 7/11

Respostas:

299

Para o Angular 1.0, você deve usar as apis $ interpolateProvider para configurar os símbolos de interpolação: http://docs.angularjs.org/api/ng.$interpolateProvider .

Algo assim deve fazer o truque:

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

Lembre-se de duas coisas:

  • misturar modelos do lado do servidor e do cliente raramente é uma boa ideia e deve ser usado com cautela. Os principais problemas são: manutenibilidade (difícil de ler) e segurança (a interpolação dupla pode expor um novo vetor de segurança - por exemplo, enquanto o escape do modelo do lado do servidor e do cliente por si só pode ser seguro, sua combinação pode não ser).
  • se você começar a usar diretivas de terceiros (componentes) usadas {{ }}em seus modelos, sua configuração as quebrará. ( correção pendente )

Embora não haja nada que possamos fazer sobre o primeiro problema, exceto avisar as pessoas, precisamos resolver o segundo problema.

Igor Minar
fonte
4
Você se importaria de explicar seu primeiro ponto (manutenção, segurança e outras preocupações para misturar modelos do lado do servidor e do cliente)? Um pouco mais de explicação seria útil.
317 Brian
1
@btlachance - eu ampliei a resposta.
Igor Minar
12
Desde $ interpolateProvider retorna auto quando usado como um setter, aqui está uma versão ligeiramente mais compacto: $interpolateProvider.startSymbol('{[{').endSymbol('}]}');
Mark Rajcok
5
Parece que a "correção" está fechada. Isso significa que agora não é seguro usar componentes de terceiros?
Alex Okrushko
1
alguma maneira de também atualizar o $ interpolateProvider para saída bruta? por exemplo, {{{foo}}} se tornando {{[{foo}]}}?
Testador
122

você pode tentar textualmente a tag template Django e usá-la assim:

<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 %}

Bessoufi Mounir
fonte
Embora essa seja uma solução muito válida, há casos em que eu quero inicializar minhas visualizações com dados do servidor, para que isso fique confuso rapidamente. Pense em coisas como o nome de usuário do usuário, pois isso não mudará, então eu vou escrevê-lo no modelo no servidor, mas pode haver partes em torno dele que eu escreverei com angular.
Endophage
16
Verbatim faz parte de marcas centrais Django desde a versão 1.5: docs.djangoproject.com/en/dev/ref/templates/builtins/...
Pratyush
11
No Django 1.7, você não precisa carregar verbatim, pois está na biblioteca de tags padrão. Você só precisa usar as tags em si.
highpost 22/10/14
1
Seria bom ter uma maneira de alterar os colchetes do Django das configurações, mas isso também funciona.
Adrian Lopez
42

Se você separou as seções da página corretamente, poderá usar facilmente as tags angularjs no escopo "bruto".

Em jinja2

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

No modelo do Django (acima de 1,5)

{% verbatim %}    
    // here you can write angularjs template tags.
{% endverbatim %}
obrigado
fonte
1
Esta solução não quebra a compatibilidade, se os pacotes externos encontrarem a resposta aceita.
Partizanos #
30

Criamos um filtro muito simples no Django 'ng' que facilita a mistura dos dois:

foo.html:

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

O ngfiltro fica assim:

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)
Wes Alvaro
fonte
Outra forma muito válida para fazê-lo, no entanto eu prefiro mudar as tags em um lugar de adicionar o filtro em muitos ...
endófago
1
Como você cria o filtro ng? Você pode adicionar um exemplo?
Ben Liyanage
Resposta atualizada. @ Endophage Eu tenho muito mais pares Angular {{}} do que Django {{}}, então prefiro atualizar os do Django.
Wes Alvaro
@WesAlvaro, infelizmente, só posso aceitar uma resposta.
Endophage
26

Então, recebi uma grande ajuda no canal Angular IRC hoje. Acontece que você pode alterar as tags de modelo do Angular com muita facilidade. Os snippets necessários abaixo devem ser incluídos após a inclusão angular (o exemplo fornecido aparece nas listas de discussão e seria usado (())como as novas tags de modelo, substituindo as suas):

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

Além disso, apontei para um aprimoramento futuro que irá expor startSymbole endSymbolpropriedades que podem ser definidas para as tags que você desejar.

Endófago
fonte
17
e é assim que você faz no angularjs 1.0: var m = angular.module ('myApp', []); m.config (function ($ interpolateProvider) {$ interpolateProvider.startSymbol ('(('); $ interpolateProvider.endSymbol ('))');});
Idsun 19/03/12
Canal de IRC angular. fwiw para quem, eu encontrei um no #angularjs
Shanimal 10/10
17

Eu voto contra o uso de parênteses duplos (()) como tag de modelo. Pode funcionar bem desde que nenhuma chamada de função esteja envolvida, mas, quando tentada, a seguinte

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

com o Firefox (10.0.2) no Mac, recebi um erro terrivelmente longo, em vez da lógica pretendida. <[]> correu bem para mim, pelo menos até agora.

Editar 2012-03-29: Observe que $ invalidWidgets está obsoleto. No entanto, eu ainda usaria outro invólucro além de aparelho duplo. Para qualquer versão angular superior a 0.10.7 (eu acho), você pode alterar o wrapper muito mais facilmente na definição de seu aplicativo / módulo:

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

Documentos da API .

Lukas Bünger
fonte
Ponto justo. Eu não tinha pensado nisso, mas não estava particularmente defendendo o uso (()), só queria poder configurar os delimitadores.
Endofago
15

Achei o código abaixo útil. Encontrei o código aqui: 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)
nu everest
fonte
Eu usei essa tag personalizada, mas se eu usar algo como: <p>{% ng location %}</p> ela é renderizada como {{location}}- sim, com chaves! Ele não renderiza o valor de $ scope.location que está codificado no meu controlador. Alguma idéia do que estou perdendo?
Keshav Agrawal
11

Se você usa o django 1.5 e o uso mais recente:

  {% verbatim %}
    {{if dying}}Still alive.{{/if}}
  {% endverbatim %}

Se você estiver com o django 1.2 no appengine, estenda a sintaxe do django com o comando verbatim template como este ...

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

No seu arquivo, use:

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

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

gato
fonte
Obrigado ... finalmente consegui funcionar, mas eu tive que ... 1) criar um novo módulo Python. Eu o nomeei utilities e coloquei o arquivo verbatim_templatetag.py nele. (O arquivo acima com a classe VerbatimNode definido nele). 2) Altere a instrução de importação de: from django import template para: from google.appengine._internal.django import template Então, no meu arquivo principal, alterei o nome do arquivo: template.register_template_library('utilities.verbatim_template_tag')
Roger
7

Você pode dizer ao Django para saída {{e }}, assim como outras seqüências de modelo reservadas, usando a {% templatetag %}tag

Por exemplo, usando {% templatetag openvariable %}seria produzido {{.

Thomas Orozco
fonte
3
Eu sei que isso é possível, mas é uma bagunça ... Seria muito mais limpo (e não parece muito grande pedir) que a tag template seja simplesmente configurável em uma das estruturas. No final do dia, é só fazer strings nos bastidores ...
endófago
3

Eu ficaria com uma solução que usa as tags django {{}} e também angularjs {{}} com uma seção literal ou um modelo de etiqueta de modelo.

Isso é simplesmente porque você pode alterar a maneira como o angularjs funciona (conforme mencionado) através do $ interpolateProvider.startSymbol $ interpolateProvider.endSymbol, mas se você começar a usar outros componentes do angularjs como o ui-bootstrap, verá que alguns dos modelos JÁ estão criados com tags angularjs padrão {{}}.

Por exemplo, consulte https://github.com/angular-ui/bootstrap/blob/master/template/dialog/message.html .

silviud
fonte
Bom ponto. Agora há um pacote django-angular no PyPI que visa tornar os dois bons juntos, mas não analisei o quanto isso alivia o problema da tag do modelo.
Endofago
0

Se você fizer alguma interpolação no servidor, a única maneira correta de fazer isso é com<>

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

Qualquer outra coisa é um vetor XSS.

Isso ocorre porque quaisquer delimitadores angulares que não são escapados pelo Django podem ser inseridos pelo usuário na cadeia de caracteres interpolada; se alguém definir seu nome de usuário como "{{evil_code}}", o Angular o executará com prazer . Se você usar um personagem para escapar do Django , isso não acontecerá.

Dan
fonte