AngularJS con Django - Etiquetas de plantilla en conflicto

302

Quiero usar AngularJS con Django, sin embargo, ambos lo usan {{ }}como etiquetas de plantilla. ¿Hay una manera fácil de cambiar uno de los dos para usar alguna otra etiqueta de plantilla personalizada?

Endophage
fuente
1
Solo renderizo una plantilla del templatesdirectorio de django , el resto la pongo static. De esa manera no tienes interferencia. Hay un tutorial que escribí aquí: coderwall.com/p/bzjuka/…
Connor Leech
¿Cómo pasar los datos entre angular2 y jinja2? Cualquier ayuda
Narendra
@Narendra es un problema diferente que no es relevante para esta pregunta. Búsquelo y, si no encuentra una respuesta, hágala como una nueva pregunta.
Endophage

Respuestas:

299

Para Angular 1.0, debe usar la API $ interpolateProvider para configurar los símbolos de interpolación: http://docs.angularjs.org/api/ng.$interpolateProvider .

Algo como esto debería hacer el truco:

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

Tenga en cuenta dos cosas:

  • mezclar plantillas del lado del servidor y del lado del cliente rara vez es una buena idea y debe usarse con precaución. Los principales problemas son: mantenibilidad (difícil de leer) y seguridad (la doble interpolación podría exponer un nuevo vector de seguridad, por ejemplo, mientras que escapar de las plantillas del lado del servidor y del cliente por sí mismas podría ser seguro, su combinación podría no serlo).
  • si comienza a usar directivas de terceros (componentes) que usan {{ }}en sus plantillas, entonces su configuración las romperá. ( arreglo pendiente )

Si bien no hay nada que podamos hacer sobre el primer problema, excepto advertir a las personas, debemos abordar el segundo problema.

Igor Minar
fuente
44
¿Le importaría explicar su primer punto (mantenimiento, seguridad y otras preocupaciones para mezclar plantillas del lado del servidor y del lado del cliente)? Un poco más de explicación sería útil.
Brian
1
@btlachance: amplié la respuesta.
Igor Minar
12
Dado que $ interpolateProvider devuelve self cuando se usa como setter, aquí hay una versión un poco más compacta: $interpolateProvider.startSymbol('{[{').endSymbol('}]}');
Mark Rajcok
55
Parece que el "arreglo" está cerrado. ¿Eso significa que ahora no es seguro usar componentes de terceros?
Alex Okrushko
1
¿Hay alguna forma de actualizar también $ interpolateProvider para la salida sin formato? por ejemplo, {{{foo}}} se convierte en {{[{foo}]}}?
probador
122

tal vez puedas probar la etiqueta de plantilla Django textualmente y usarla así:

<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
fuente
Si bien esa es una solución muy válida, hay casos en los que quiero poder arrancar mis vistas con datos del servidor para que esto se vuelva desordenado rápidamente. Piense cosas como el nombre de usuario del usuario, no va a cambiar, así que lo escribiré en la plantilla en el servidor, pero puede haber partes a su alrededor que escribiré con angular.
Endophage
16
Verbatim es parte de las etiquetas principales de Django desde la versión 1.5: docs.djangoproject.com/en/dev/ref/templates/builtins/…
Pratyush
11
En Django 1.7 no necesita cargar textualmente ya que está en la biblioteca de etiquetas estándar. Solo necesita usar las etiquetas.
Highpost
1
Sería bueno tener una manera de cambiar los corchetes predeterminados de Django desde la configuración, pero esto también funciona.
Adrián López
42

Si separó las secciones de la página correctamente, puede usar fácilmente las etiquetas angularjs en el alcance de la etiqueta "en bruto".

En jinja2

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

En la plantilla de Django (superior a 1.5)

{% verbatim %}    
    // here you can write angularjs template tags.
{% endverbatim %}
nota de agradecimiento
fuente
1
Esta solución no rompe la compatibilidad si los paquetes externos tienen la respuesta aceptada.
partizanos
30

Creamos un filtro muy simple en Django 'ng' que facilita la mezcla de los dos:

foo.html:

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

El ngfiltro se ve así:

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
fuente
Otra forma muy válida de hacerlo, sin embargo, prefiero cambiar las etiquetas en un lugar que agregar el filtro en muchos ...
Endophage
1
¿Cómo se crea el filtro ng? ¿Puedes agregar un ejemplo?
Ben Liyanage
Respuesta actualizada @Endophage Tengo muchos más pares angulares {{}} que pares Django {{}}, así que prefiero actualizar los pares Django.
Wes Alvaro
@WesAlvaro desafortunadamente solo puedo aceptar una respuesta.
Endophage
26

Así que obtuve una gran ayuda en el canal Angular IRC hoy. Resulta que puedes cambiar las etiquetas de plantilla de Angular muy fácilmente. Los fragmentos necesarios a continuación se deben incluir después de su inclusión angular (el ejemplo dado aparece en sus listas de correo y se usaría (())como las nuevas etiquetas de plantilla, en lugar de las suyas propias):

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

Además, me señalaron una próxima mejora que expondrá startSymboly las endSymbolpropiedades que se pueden configurar para cualquier etiqueta que desee.

Endophage
fuente
17
y así es como lo haces en angularjs 1.0: var m = angular.module ('myApp', []); m.config (function ($ interpolateProvider) {$ interpolateProvider.startSymbol ('(('); $ interpolateProvider.endSymbol ('))');});
idursun
Canal angular IRC. Gracias a quien sea, encontré uno en #angularjs
Shanimal
17

Voto en contra del uso de paréntesis dobles (()) como etiqueta de plantilla. Puede funcionar bien siempre que no exista una llamada a la función, pero cuando se intenta lo siguiente

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

con Firefox (10.0.2) en Mac obtuve un error terriblemente largo en lugar de la lógica prevista. <[]> me fue bien, al menos hasta ahora.

Editar 2012-03-29: Tenga en cuenta que $ invalidWidgets está en desuso. Sin embargo, todavía usaría otra envoltura que no sean llaves dobles. Para cualquier versión angular superior a 0.10.7 (supongo), podría cambiar el contenedor mucho más fácilmente en la definición de su aplicación / módulo:

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

API docs .

Lukas Bünger
fuente
Punto justo. No había pensado en eso, pero no estaba abogando particularmente por el uso (()), solo quería poder configurar los delimitadores.
Endophage
15

Encontré el siguiente código útil. Encontré el código aquí: 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
fuente
Utilicé esta etiqueta personalizada, pero luego, si uso algo como: <p>{% ng location %}</p> se muestra como {{location}}- ¡sí con llaves! No representa el valor de $ scope.location, que está codificado en mi controlador. ¿Alguna idea de lo que me estoy perdiendo?
Keshav Agrawal
11

Si usa django 1.5 y un uso más reciente:

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

Si está atascado con django 1.2 en appengine, extienda la sintaxis de django con el comando de plantilla textual 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))

En su archivo use:

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

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

gato
fuente
Gracias ... finalmente conseguí que esto funcionara pero tuve que ... 1) crear un nuevo módulo de Python. Lo llamé utilities y puse el archivo verbatim_templatetag.py en él. (El archivo anterior con la clase VerbatimNode definida en él). 2) Cambiar la declaración de importación de: from django import template a: from google.appengine._internal.django import template Luego, en mi archivo principal, solo cambié el nombre del archivo: template.register_template_library('utilities.verbatim_template_tag')
Roger
7

Puede decirle a Django que imprima {{y }}, al igual que otras cadenas de plantillas reservadas, use la {% templatetag %}etiqueta.

Por ejemplo, usar {% templatetag openvariable %}sería salida {{.

Thomas Orozco
fuente
3
Sé que es posible, pero es desordenado ... Sería mucho más limpio (y no parece una pregunta demasiado grande) que la etiqueta de la plantilla sea simplemente configurable en uno de los marcos. Al final del día, solo se trata de hacer coincidir las cuerdas detrás de escena ...
Endophage
3

Me quedaría con una solución que usa las etiquetas django {{}} y angularjs {{}} con una sección literal o una etiqueta de plantilla.

Eso es simplemente porque puede cambiar la forma en que funciona angularjs (como se mencionó) a través de $ interpolateProvider.startSymbol $ interpolateProvider.endSymbol, pero si comienza a usar otros componentes de angularjs como ui-bootstrap, encontrará que algunas de las plantillas YA están construidas YA. con etiquetas angulares estándar {{}}.

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

silviud
fuente
Buen punto. Ahora hay un paquete django-angular en PyPI destinado a hacer que los dos jueguen bien juntos, pero no he investigado cuánto alivia el problema de la etiqueta de la plantilla.
Endophage
0

Si realiza una interpolación del lado del servidor, la única forma correcta de hacerlo es con<>

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

Cualquier otra cosa es un vector XSS.

Esto se debe a que el usuario puede ingresar cualquier delimitador angular que Django no haya escapado en la cadena interpolada; si alguien establece su nombre de usuario como "{{evil_code}}", Angular lo ejecutará felizmente . Sin embargo, si usa un personaje que Django escapa , esto no sucederá.

Dan
fuente