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?

Endophage
la source
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.

Igor Minar
la source
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 %}

Bessoufi Mounir
la source
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 %}
remerciement
la source
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)
Wes Alvaro
la source
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.

Endophage
la source
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 .

Lukas Bünger
la source
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)
nu everest
la source
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

chat
la source
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 {{.

Thomas Orozco
la source
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 .

silviud
la source
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.

Dan
la source