AngularJS avec Django - Balises de modèles en conflit


302

Je veux utiliser AngularJS avec Django, mais ils utilisent tous les deux {{ }}comme balises de modèle. Existe-t-il un moyen facile de modifier l'un des deux pour utiliser une autre balise de modèle personnalisée?


1
Je ne rend qu'un seul modèle à partir du templatesrépertoire django , le reste que j'ai mis static. De cette façon, vous n'avez aucune interférence. Il y a un tutoriel que j'ai écrit ici: coderwall.com/p/bzjuka/…
Connor Leech

comment passer les données entre angular2 et jinja2? Toute aide
Narendra

@Narendra, c'est un problème différent sans rapport avec cette question. Veuillez la rechercher et si vous ne trouvez pas de réponse, posez-la en tant que nouvelle question.
Endophage

Réponses:


299

Pour Angular 1.0, vous devez utiliser les API $ interpolateProvider pour configurer les symboles d'interpolation: http://docs.angularjs.org/api/ng.$interpolateProvider .

Quelque chose comme ça devrait faire l'affaire:

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

Gardez à l'esprit deux choses:

  • mélanger les modèles côté serveur et côté client est rarement une bonne idée et doit être utilisé avec prudence. Les principaux problèmes sont: la maintenabilité (difficile à lire) et la sécurité (la double interpolation pourrait exposer un nouveau vecteur de sécurité - par exemple, alors que l'échappement des modèles côté serveur et côté client par eux-mêmes peut être sécurisé, leur combinaison peut ne pas l'être).
  • si vous commencez à utiliser des directives (composants) tierces qui utilisent {{ }}dans leurs modèles, votre configuration les cassera. ( correction en attente )

Bien que nous ne puissions rien faire à propos du premier problème, sauf pour avertir les gens, nous devons régler le deuxième problème.


4
Pourriez-vous expliquer votre premier point (maintenance, sécurité et autres préoccupations concernant le mélange de modèles côté serveur et côté client)? Un peu plus d'explications seraient utiles.
Brian

1
@btlachance - J'ai développé la réponse.
Igor Minar

12
Puisque $ interpolateProvider retourne lui-même lorsqu'il est utilisé comme setter, voici une version légèrement plus compacte: $interpolateProvider.startSymbol('{[{').endSymbol('}]}');
Mark Rajcok

5
Il semble que le "correctif" soit fermé. Cela signifie-t-il qu'il n'est plus sûr d'utiliser des composants tiers?
Alex Okrushko

1
une manière de mettre également à jour le $ interpolateProvider pour la sortie brute? par exemple {{{foo}}} devenant {{[{foo}]}}?
testeur

122

vous pouvez peut-être essayer la balise de modèle textuellement Django et l'utiliser comme ceci:

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


Bien que ce soit une solution très valable, il y a des cas où je veux pouvoir amorcer mes vues avec des données du serveur afin que cela devienne rapidement désordonné. Pensez à des choses comme le nom d'utilisateur de l'utilisateur, cela ne changera pas, alors je vais simplement l'écrire dans le modèle sur le serveur, mais il peut y avoir des éléments autour que j'écrirai avec angulaire.
Endophage

16
Verbatim fait partie des balises principales de Django depuis la version 1.5: docs.djangoproject.com/en/dev/ref/templates/builtins/…
Pratyush

11
Dans Django 1.7, vous n'avez pas besoin de charger textuellement car il se trouve dans la bibliothèque de balises standard. Il vous suffit d'utiliser les balises elles-mêmes.
highpost

1
Ce serait bien d'avoir un moyen de changer les crochets par défaut de Django à partir des paramètres, mais cela fonctionne aussi.
Adrian Lopez

42

Si vous avez correctement séparé les sections de la page, vous pouvez facilement utiliser les balises angularjs dans la portée de la balise "raw".

Dans jinja2

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

Dans le modèle Django (supérieur à 1.5)

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

1
Cette solution ne rompt pas la compatibilité avec les packages externes qui constituent la réponse acceptée.
partizanos

30

Nous avons créé un filtre très simple dans Django 'ng' qui permet de mélanger facilement les deux:

foo.html:

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

Le ngfiltre ressemble à ceci:

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)

Une autre façon très valable de le faire, mais je préfère changer les balises à un seul endroit que d'ajouter le filtre à plusieurs ...
Endophage

1
Comment créez-vous le filtre ng? Pouvez-vous ajouter un exemple?
Ben Liyanage

Réponse mise à jour. @Endophage J'ai beaucoup plus de paires {{}} angulaires que de paires Django {{}}, donc je préfère mettre à jour celles de Django.
Wes Alvaro

@WesAlvaro, malheureusement, je ne peux accepter qu'une seule réponse.
Endophage

26

J'ai donc obtenu une aide précieuse dans le canal IRC angulaire aujourd'hui. Il s'avère que vous pouvez modifier les balises de modèle d'Angular très facilement. Les extraits nécessaires ci-dessous doivent être inclus après votre inclusion angulaire (l'exemple donné apparaît sur leurs listes de diffusion et utiliserait (())comme nouvelles balises de modèle, en remplacement de la vôtre):

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

De plus, on m'a signalé une amélioration à venir qui exposera startSymbolet des endSymbolpropriétés qui pourront être définies sur les balises que vous désirez.


17
et voici comment vous le faites dans angularjs 1.0: var m = angular.module ('myApp', []); m.config (fonction ($ interpolateProvider) {$ interpolateProvider.startSymbol ('(('); $ interpolateProvider.endSymbol ('))');});
idursun

Canal IRC angulaire. fwiw à qui que ce soit, j'en ai trouvé un à #angularjs
Shanimal

17

Je vote contre l'utilisation de doubles parenthèses (()) comme balise de modèle. Cela peut bien fonctionner tant qu'aucun appel de fonction n'est impliqué, mais lorsque vous essayez ce qui suit

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

avec Firefox (10.0.2) sur Mac, j'ai eu une erreur terriblement longue au lieu de la logique voulue. <[]> s'est bien passé pour moi, du moins jusqu'à présent.

Edit 2012-03-29: Veuillez noter que $ invalidWidgets est déconseillé. Cependant, j'utiliserais toujours un autre wrapper que les accolades doubles. Pour toute version angulaire supérieure à 0.10.7 (je suppose), vous pouvez changer le wrapper beaucoup plus facilement dans la définition de votre application / module:

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

Documents API .


Bon point. Je n'y avais pas pensé mais je ne préconisais pas particulièrement l'utilisation (()), je voulais juste pouvoir configurer les délimiteurs.
Endophage

15

J'ai trouvé le code ci-dessous utile. J'ai trouvé le code ici: 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)

J'ai utilisé cette balise personnalisée, mais si j'utilise quelque chose comme: <p>{% ng location %}</p> elle est rendue comme {{location}}- oui avec des accolades! Il ne rend pas la valeur de $ scope.location qui est codée en dur dans mon contrôleur. Une idée de ce qui me manque?
Keshav Agrawal


11

Si vous utilisez django 1.5 et une utilisation plus récente:

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

Si vous êtes bloqué avec django 1.2 sur appengine, étendez la syntaxe django avec la commande de modèle verbatim comme ceci ...

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

Dans votre fichier, utilisez:

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


Merci ... enfin ça a fonctionné mais j'ai dû ... 1) créer un nouveau module Python. Je l'ai nommé utilties et y ai mis le fichier verbatim_templatetag.py. (Le fichier ci-dessus avec la classe VerbatimNode définie dans celui-ci). 2) Modifiez l'instruction d'importation de: from django import template à: from google.appengine._internal.django import template Ensuite, dans mon fichier principal, changez simplement le nom de fichier: template.register_template_library('utilities.verbatim_template_tag')
Roger

7

Vous pouvez dire à Django de sortir {{et }}, ainsi que d'autres chaînes de modèle réservées, en utilisant la {% templatetag %}balise.

Par exemple, l'utilisation {% templatetag openvariable %}produirait {{.


3
Je sais que c'est possible mais c'est désordonné ... Il serait beaucoup plus propre (et ne semble pas trop demander) que la balise de modèle soit simplement configurable dans l'un des cadres. À la fin de la journée, il s'agit simplement de faire correspondre les cordes dans les coulisses ...
Endophage

3

Je m'en tiendrai à une solution qui utilise à la fois les balises django {{}} ainsi que les angularjs {{}} avec une section verbatim ou un templatetag.

C'est simplement parce que vous pouvez changer la façon dont angularjs fonctionne (comme mentionné) via $ interpolateProvider.startSymbol $ interpolateProvider.endSymbol mais si vous commencez à utiliser d'autres composants angularjs comme le ui-bootstrap, vous constaterez que certains des modèles sont DÉJÀ construits avec des balises angularjs standard {{}}.

Par exemple, regardez https://github.com/angular-ui/bootstrap/blob/master/template/dialog/message.html .


Bon point. Il y a maintenant un paquet django-angular dans PyPI qui est destiné à faire jouer les deux ensemble, mais je n'ai pas examiné dans quelle mesure il atténue le problème de balise de modèle.
Endophage

0

Si vous effectuez une interpolation côté serveur, la seule façon correcte de le faire est de<>

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

Tout le reste est un vecteur XSS.

En effet, tout délimiteur angulaire non échappé par Django peut être entré par l'utilisateur dans la chaîne interpolée; si quelqu'un définit son nom d'utilisateur sur "{{evil_code}}", Angular l'exécute avec plaisir . Cependant, si vous utilisez un personnage que Django échappe , cela ne se produira pas.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.