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?


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.


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


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

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)

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.


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 .


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)

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


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 {{.


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 .


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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.