AngularJS z Django - sprzeczne tagi szablonów

302

Chcę używać AngularJS z Django, jednak oba używają {{ }}jako znaczników szablonów. Czy istnieje prosty sposób na zmianę jednego z dwóch, aby użyć innego niestandardowego znacznika szablonów?

Endofag
źródło
1
Renderuję tylko jeden szablon z templateskatalogu django , resztę wstawiam static. W ten sposób nie będziesz mieć zakłóceń. Jest tutorial, który napisałem tutaj: coderwall.com/p/bzjuka/…
Connor Leech
jak przekazać dane między angular2 i jinja2? Wszelka pomoc
Narendra
@ Narendra to inny problem niezwiązany z tym pytaniem. Wyszukaj ją, a jeśli nie znajdziesz odpowiedzi, zadaj ją jako nowe pytanie.
Endofag

Odpowiedzi:

299

W przypadku Angulara 1.0 należy użyć apis $ interpolateProvider do skonfigurowania symboli interpolacji: http://docs.angularjs.org/api/ng.$interpolateProvider .

Coś takiego powinno załatwić sprawę:

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

Pamiętaj o dwóch rzeczach:

  • mieszanie szablonów po stronie serwera i klienta rzadko jest dobrym pomysłem i powinno być używane z ostrożnością. Główne problemy to: łatwość konserwacji (trudność do odczytania) i bezpieczeństwo (podwójna interpolacja może narazić nowy wektor bezpieczeństwa - np. Podczas gdy ucieczka od szablonów po stronie serwera i po stronie klienta może być bezpieczna, ich kombinacja może nie być).
  • jeśli zaczniesz używać dyrektyw (komponentów) innych firm, które używają {{ }}ich szablonów, twoja konfiguracja je zepsuje. ( poprawka w toku )

Chociaż nic nie możemy zrobić z pierwszym problemem, z wyjątkiem ostrzegania ludzi, musimy zająć się drugim problemem.

Igor Minar
źródło
4
Czy mógłbyś wyjaśnić swój pierwszy punkt (konserwacja, bezpieczeństwo i inne obawy związane z mieszaniem szablonów po stronie serwera i klienta)? Przydałoby się trochę więcej wyjaśnień.
Brian
1
@btlachance - rozszerzyłem odpowiedź.
Igor Minar,
12
Ponieważ $ interpolateProvider zwraca self, gdy jest używany jako setter, oto nieco bardziej kompaktowa wersja: $interpolateProvider.startSymbol('{[{').endSymbol('}]}');
Mark Rajcok
5
Wygląda na to, że „poprawka” jest zamknięta. Czy to oznacza, że ​​korzystanie z komponentów innych firm nie jest teraz bezpieczne?
Alex Okrushko,
1
jakikolwiek sposób, aby zaktualizować $ interpolateProvider dla surowego wyjścia? np. {{{foo}}} staje się {{[{foo}]}}?
tester
122

możesz spróbować dosłownie tagu szablonu Django i użyć go w następujący sposób:

<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
źródło
Chociaż jest to bardzo poprawne rozwiązanie, są przypadki, w których chcę mieć możliwość ładowania moich widoków z danymi z serwera, aby szybko się bałaganił. Pomyśl o tym, jak nazwa użytkownika, to się nie zmieni, więc po prostu napiszę go do szablonu na serwerze, ale mogą być wokół niego fragmenty, które napiszę pod kątem.
Endofag
16
Verbatim jest częścią podstawowych tagów Django od wersji 1.5: docs.djangoproject.com/en/dev/ref/templates/builtins/…
Pratyush
11
W Django 1.7 nie musisz ładować dosłownie, ponieważ znajduje się w standardowej bibliotece znaczników. Wystarczy użyć samych tagów.
highpost
1
Byłoby miło mieć sposób na zmianę domyślnych nawiasów Django w ustawieniach, ale to też działa.
Adrian Lopez
42

Jeśli poprawnie rozdzieliłeś sekcje strony, możesz łatwo użyć tagów angularjs w zakresie „surowego” tagu.

W jinja2

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

W szablonie Django (powyżej 1.5)

{% verbatim %}    
    // here you can write angularjs template tags.
{% endverbatim %}
dziękczynienie
źródło
1
To rozwiązanie nie narusza kompatybilności z zewnętrznymi pakietami, podobnie jak zaakceptowana odpowiedź.
partizanos
30

Stworzyliśmy bardzo prosty filtr w Django 'ng', który ułatwia mieszanie dwóch:

foo.html:

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

ngFiltr wygląda następująco:

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
źródło
Kolejny bardzo ważny sposób, aby to zrobić, jednak wolałbym zmienić tagi w jednym miejscu niż dodać filtr w wielu ...
Endophage
1
Jak tworzysz filtr ng? Czy możesz dodać przykład?
Ben Liyanage,
Zaktualizowana odpowiedź. @ Endofag Mam o wiele więcej par Angular {{}} niż par Django {{}}, więc wolę zaktualizować te Django.
Wes Alvaro,
@WesAlvaro niestety nie mogę zaakceptować tylko jednej odpowiedzi.
Endofag
26

Więc dzisiaj otrzymałem świetną pomoc w kanale Angular IRC. Okazuje się, że możesz bardzo łatwo zmieniać tagi szablonu Angulara. Niezbędne fragmenty poniżej powinny zostać uwzględnione po uwzględnieniu angulara (podany przykład pojawia się na ich listach mailingowych i posłużyłby (())jako nowe tagi szablonu, zastępując własne):

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

Wskazano także na nadchodzące ulepszenie, które ujawni startSymboli endSymbolwłaściwości, które można ustawić na dowolne tagi.

Endofag
źródło
17
i tak to robisz w angularjs 1.0: var m = angular.module ('myApp', []); m.config (function ($ interpolateProvider) {$ interpolateProvider.startSymbol ('(('); $ interpolateProvider.endSymbol ('))');});
idursun
Kątowy kanał IRC. fwiw z kimkolwiek, znalazłem jeden na #angularjs
Shanimal,
17

Głosuję przeciwko użyciu podwójnego nawiasu (()) jako tagu szablonu. Może działać dobrze, o ile nie ma w nim wywołania funkcji, ale po wypróbowaniu następujących czynności

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

z Firefoksem (10.0.2) na Macu dostałem strasznie długi błąd zamiast zamierzonej logiki. <[]> szło mi dobrze, przynajmniej do tej pory.

Edytuj 2012-03-29: Pamiętaj, że $ invalidWidgets jest przestarzałe. Jednak nadal użyłbym innego opakowania niż podwójne szelki. W przypadku dowolnej wersji kątowej wyższej niż 0.10.7 (tak sądzę) można znacznie łatwiej zmienić opakowanie w definicji aplikacji / modułu:

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

Dokumentacja API .

Lukas Bünger
źródło
Uczciwy punkt. Nie myślałem o tym, ale nie zalecałem używania (()), po prostu chciałem móc skonfigurować ograniczniki.
Endofag
15

Poniższy kod był pomocny. Znalazłem kod tutaj: 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
źródło
Użyłem tego niestandardowego znacznika, ale jeśli użyję czegoś takiego: <p>{% ng location %}</p> zostanie renderowany jako {{location}}- tak z nawiasami klamrowymi! Nie wyświetla wartości $ scope.location, która jest zapisana na stałe w moim kontrolerze. Masz pojęcie, czego mi brakuje?
Keshav Agrawal
11

Jeśli używasz django 1.5 i nowszych:

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

Jeśli utkniesz z django 1.2 na appengine, rozszerz składnię django za pomocą polecenia szablonu verbatim w ten sposób ...

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

W swoim pliku użyj:

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

Źródło: http://bamboobig.blogspot.co.at/2011/09/notebook-using-jquery-templates-in.html

kot
źródło
Dzięki ... w końcu działało, ale musiałem ... 1) utworzyć nowy moduł Python. Nadałem mu nazwę utilties i umieściłem w nim plik verbatim_templatetag.py. (Powyższy plik ze zdefiniowaną w nim klasą VerbatimNode). 2) Zmień instrukcję importu z: from django import template na: from google.appengine._internal.django import template Następnie, w moim głównym pliku, właśnie zmieniłem nazwę pliku: template.register_template_library('utilities.verbatim_template_tag')
Roger
7

Można powiedzieć Django do wyjścia {{i }}, jak również inne zastrzeżone ciągi szablon za pomocą {% templatetag %}znacznika.

Na przykład użycie {% templatetag openvariable %}spowoduje wyjście {{.

Thomas Orozco
źródło
3
Wiem, że to możliwe, ale jest niechlujne ... Byłoby o wiele czystsze (i nie wydaje się zbyt duże pytanie), aby znacznik szablonu był po prostu konfigurowalny w jednym z ram. Pod koniec dnia po prostu dopasowuje struny za kulisami ...
Endophage
3

Trzymałbym się rozwiązania, które wykorzystuje zarówno tagi django {{}}, jak i angularjs {{}} z sekcją dosłowną lub szablonem.

Jest tak po prostu dlatego, że możesz zmienić sposób działania angularjs (jak wspomniano) za pomocą $ interpolateProvider.startSymbol $ interpolateProvider.endSymbol, ale jeśli zaczniesz używać innych komponentów angularjs, takich jak ui-bootstrap, przekonasz się, że niektóre szablony są już zbudowane ze standardowymi tagami angularjs {{}}.

Na przykład spójrz na https://github.com/angular-ui/bootstrap/blob/master/template/dialog/message.html .

silviud
źródło
Słuszna uwaga. W PyPI jest teraz pakiet kątowy django, który ma sprawić, że ta dwójka będzie się dobrze bawić, ale nie zastanawiałem się, jak bardzo to rozwiązuje problem ze znacznikiem szablonu.
Endofag
0

Jeśli wykonujesz interpolację po stronie serwera, jedynym prawidłowym sposobem na to jest użycie<>

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

Wszystko inne jest wektorem XSS.

Wynika to z faktu, że dowolne ograniczniki kątowe, które nie są poprzedzane przez Django, mogą zostać wprowadzone przez użytkownika do interpolowanego ciągu; jeśli ktoś ustawi swoją nazwę użytkownika na „{{evil_code}}”, Angular chętnie ją uruchomi . Jeśli użyjesz postaci, a Django ucieknie , tak się nie stanie.

Dan
źródło