Szablon zewnętrzny w podkreśleniu

121

Używam szablonu podkreślenia . Czy można załączyć plik zewnętrzny jako szablon ?

W widoku Backbone mam:

 textTemplate: _.template( $('#practice-text-template').html() ),

 initialize: function(){                                            
  this.words = new WordList;            
  this.index = 0;
  this.render();
 },

W moim html jest:

<script id="practice-text-template" type="text/template">
   <h3>something code</h3>
</script>

To dobrze działa. Ale potrzebuję zewnętrznego szablonu . Próbuję:

<script id="practice-text-template" type="text/template" src="templates/tmp.js">

lub

textTemplate: _.template( $('#practice-text-template').load('templates/tmp.js') ),

lub

$('#practice-text-template').load('templates/tmp.js', function(data){ this.textTemplate = _.template( data ) })

ale to nie zadziałało.

Tomáš
źródło

Odpowiedzi:

51

EDYCJA: Ta odpowiedź jest stara i nieaktualna. Skasowałbym ją, ale jest to odpowiedź „zaakceptowana”. Zamiast tego wstrzyknę swoją opinię.

Nie polecałbym już tego robić. Zamiast tego rozdzieliłbym wszystkie szablony na pojedyncze pliki HTML. Niektórzy sugerowaliby ładowanie ich asynchronicznie (Require.js lub pewnego rodzaju pamięć podręczna szablonów). To działa dobrze w małych projektach, ale w dużych projektach z dużą ilością szablonów, przy ładowaniu strony tworzysz mnóstwo małych żądań asynchronicznych, których naprawdę nie lubię. (ugh ... ok, możesz to obejść z Require.js, wstępnie kompilując swoje początkowe zależności z r.js, ale w przypadku szablonów nadal wydaje mi się to niewłaściwe)

Lubię używać gruntownego zadania (grunt-contrib-jst), aby skompilować wszystkie szablony HTML w jednym pliku templates.js i dołączyć go. Dostajesz to, co najlepsze ze wszystkich światów IMO ... szablony żyją w pliku, kompilacja wspomnianych szablonów odbywa się w czasie kompilacji (nie w czasie wykonywania) i nie masz stu małych żądań asynchronicznych podczas uruchamiania strony.

Wszystko poniżej to śmieci

Dla mnie wolę prostotę dołączania pliku JS do mojego szablonu. Mogę więc utworzyć plik o nazwie view_template.js, który zawiera szablon jako zmienną:

app.templates.view = " \
    <h3>something code</h3> \
";

Następnie jest to tak proste, jak włączenie pliku skryptu jak normalny, a następnie użycie go w widoku:

template: _.template(app.templates.view)

Idąc krok dalej, właściwie używam coffeescript, więc mój kod w rzeczywistości wygląda bardziej tak i unikam znaków ucieczki końca linii:

app.templates.view = '''
    <h3>something code</h3>
'''

Takie podejście pozwala uniknąć zalewania w require.js, gdzie naprawdę nie jest to konieczne.

Brian Genisio
źródło
46
to podejście spowodowałoby utratę wszystkich funkcji podświetlania składni, ponownego formatowania i refaktoryzacji dostępnych w ide. nie głosuje jednak.
Kinjal Dixit
1
Przepraszam, ale musiałem zlekceważyć tę odpowiedź. Jest strasznie niezgrabny, ponieważ nadal zachowuje pliki szablonów jako pliki skryptów, po prostu zmuszone do wyglądania jak szablony. Szablony muszą być szablonami, więc jeśli musisz wprowadzić Require.js lub skorzystać z genialnego rozwiązania firmy koorchik poniżej, uważam, że zdecydowanie warto.
Tommi Forsström
3
@ TommiForsström Zgadzam się. Odszedłem od tego podejścia. Łał! 4 grudnia 2011 to naprawdę dawno temu w świecie tworzenia Backbone.js :)
Brian Genisio.
Właściwie chciałbym usunąć tę odpowiedź, ale nie mogę, ponieważ jest to zaakceptowana odpowiedź. Jest przestarzały i istnieją znacznie lepsze rozwiązania niż to. Dzisiaj miałbym je jako osobne pliki szablonów i użyłbym zadania gruntowego (na przykład JST), aby zbudować je w osobnym pliku templates.js, aby uniknąć asynchronicznego charakteru pobierania ich wszystkich osobno. Jest to najlepsze z obu światów podejście IMO.
Brian Genisio,
cóż, jeśli nie ma wielu szablonów, myślę, że poprzednie rozwiązanie jest naprawdę najbardziej wydajne.
silkAdmin
107

Oto proste rozwiązanie:

var rendered_html = render('mytemplate', {});

function render(tmpl_name, tmpl_data) {
    if ( !render.tmpl_cache ) { 
        render.tmpl_cache = {};
    }

    if ( ! render.tmpl_cache[tmpl_name] ) {
        var tmpl_dir = '/static/templates';
        var tmpl_url = tmpl_dir + '/' + tmpl_name + '.html';

        var tmpl_string;
        $.ajax({
            url: tmpl_url,
            method: 'GET',
            dataType: 'html', //** Must add 
            async: false,
            success: function(data) {
                tmpl_string = data;
            }
        });

        render.tmpl_cache[tmpl_name] = _.template(tmpl_string);
    }

    return render.tmpl_cache[tmpl_name](tmpl_data);
}

Używanie „async: false” tutaj nie jest złym sposobem, ponieważ w każdym przypadku musisz poczekać, aż szablon zostanie załadowany.

Zatem funkcja „renderuj”

  1. umożliwia przechowywanie każdego szablonu w osobnym pliku html w statycznym katalogu
  2. jest bardzo lekki
  3. kompiluje i buforuje szablony
  4. streszczenie logiki ładowania szablonu. Na przykład w przyszłości możesz używać wstępnie załadowanych i wstępnie skompilowanych szablonów.
  5. jest łatwy w użyciu

[Edytuję odpowiedź zamiast zostawiać komentarz, ponieważ uważam, że jest to ważne.]

jeśli szablony nie wyświetlają się w aplikacji natywnej , a widzisz HIERARCHY_REQUEST_ERROR: DOM Exception 3, spójrz na odpowiedź Dave'a Robinsona na pytanie Co dokładnie może spowodować błąd „HIERARCHY_REQUEST_ERR: DOM Exception 3” -Error? .

Zasadniczo musisz dodać

dataType: 'html'

do żądania $ .ajax.

koorchik
źródło
3
@BinaryNights - czy powinniśmy zawsze dodawać dataType: 'html'do naszego żądania Ajax , na wszelki wypadek?
Matt
Czy to działa również w przypadku widoków zagnieżdżonych? Najwyraźniej nie mogę tego zrobić, jeśli widok odnosi się do innego widoku.
T. Rossi
1
Tak, powinno działać również w przypadku szablonów zagnieżdżonych. Po prostu dodaj pomocnika renderowania i wywołaj go w następujący sposób: <% = render ('nested_template', data)%>
koorchik
Witaj, czy możesz wyjaśnić trochę więcej na temat „szablonów kompilacji i pamięci podręcznej”? Kiedy próbowałem wywołać funkcję renderującą, nie dodała tmpl_data do zwracania wartości, po prostu przekazała ją tak, jak jest. Potem musiałem wywołać metodę „Handlebars.compile”. Dziękuję Ci.
cdagli
18

Ten wstawek pozwala na renderowanie szablonu zewnętrznych przy użyciu Underscore w bardzo prosty sposób: _.templateFromUrl(url, [data], [settings]). API metody jest prawie takie samo, jak _.template () podkreślenia . Zawiera buforowanie.

_.mixin({templateFromUrl: function (url, data, settings) {
    var templateHtml = "";
    this.cache = this.cache || {};

    if (this.cache[url]) {
        templateHtml = this.cache[url];
    } else {
        $.ajax({
            url: url,
            method: "GET",
            async: false,
            success: function(data) {
                templateHtml = data;
            }
        });

        this.cache[url] = templateHtml;
    }

    return _.template(templateHtml, data, settings);
}});

Stosowanie:

var someHtml = _.templateFromUrl("http://example.com/template.html", {"var": "value"});
Dmitriy
źródło
2
Naprawdę fajna mała mieszanka tam bardzo schludna! :) Pozdrawiam za udostępnienie
Nick White
Bardzo fajne D, takiego właśnie rozwiązania szukałem. i myślę, że można by go użyć do zachowania prywatności zestawu szablonów.
bigmadwolf
@abhi jest podane w odpowiedzi. Potrzebujesz również jQuery do załadowania szablonu, ale możesz przepisać część kodu, która ładuje szablon przez AJAX, według własnego uznania, używając dowolnej innej biblioteki.
Dmitriy
@Dmitriy async: false jest przestarzałe, więc jeśli wywołuję bez parametru async, to nie działa, myślę, że dzieje się tak, ponieważ domyślnie async jest true, co oznacza wywołanie syncronisilly, więc masz rozwiązanie tego problemu
abhi
@abhi, działa z jQuery 1. * Zobacz także tę odpowiedź stackoverflow.com/a/11755262/541961
Dmitriy
17

Nie chciałem używać require.js do tego prostego zadania, więc użyłem zmodyfikowanego rozwiązania koorchika.

function require_template(templateName, cb) {
    var template = $('#template_' + templateName);
    if (template.length === 0) {
        var tmpl_dir = './templates';
        var tmpl_url = tmpl_dir + '/' + templateName + '.tmpl';
        var tmpl_string = '';

        $.ajax({
            url: tmpl_url,
            method: 'GET',
            contentType: 'text',
            complete: function (data, text) {
                tmpl_string = data.responseText;
                $('head').append('<script id="template_' + templateName + '" type="text/template">' + tmpl_string + '<\/script>');
                if (typeof cb === 'function')
                    cb('tmpl_added');
            }
        });
    } else {
        callback('tmpl_already_exists');
    }
}

require_template('a', function(resp) {
    if (resp == 'tmpl_added' || 'tmpl_already_exists') {
        // init your template 'a' rendering
    }
});
require_template('b', function(resp) {
    if (resp == 'tmpl_added' || 'tmpl_already_exists') {
        // init your template 'b' rendering
    }
});

Po co dołączać szablony do dokumentu, zamiast przechowywać je w obiekcie javascript? Ponieważ w wersji produkcyjnej chciałbym wygenerować plik html ze wszystkimi szablonami już zawartymi, więc nie będę musiał wykonywać żadnych dodatkowych żądań Ajax. W tym samym czasie nie będę musiał dokonywać żadnych refaktoryzacji w moim kodzie, jak używam

this.template = _.template($('#template_name').html());

w moich widokach Backbone.

Tyth
źródło
1
Używając tego również, świetnie nadaje się do scenerio, w którym próbuję użyć Jasmine do TDD i chciałbym przetestować szablony, zanim zaimplementuję requirejs i jego wtyczkę textjs. Dobra robota @Tramp
Nicholas Murray
Wywołanie $ .ajax jest asynchroniczne, cokolwiek zależy od wyników, powinno być wykonane w ramach metody done zwróconej obietnicy.
JoshRoss,
Dzięki za to. Użyłem tego. Jedna sugestia: nie ma powodu, aby dołączać jako tag skryptu - można po prostu przejść dalej i przekonwertować na szablon i zachować go w skrócie wyszukiwania. Oto (niefunkcjonalny) przykład skrzypiec: jsfiddle.net/PyzeF
webnesto Kwietnia
async: falsejest teraz przestarzała
ProblemsOfSumit
Ponieważ async: falsejest przestarzały, poprawiłem odpowiedź, dodając completewywołanie zwrotne.
Alexander
16

To może być trochę nie na temat, ale możesz użyć Grunt (http://gruntjs.com/) - który działa na node.js (http://nodejs.org/, dostępny dla wszystkich głównych platform) do uruchamiania zadań z wiersz poleceń. Istnieje kilka wtyczek do tego narzędzia, takich jak kompilator szablonów, https://npmjs.org/package/grunt-contrib-jst . Zobacz dokumentację na GitHub, https://github.com/gruntjs/grunt-contrib-jst . (Musisz również zrozumieć, jak uruchomić menedżera pakietów węzłów, https://npmjs.org/ . Nie martw się, jest to niezwykle łatwe i wszechstronne).

Następnie możesz przechowywać wszystkie szablony w osobnych plikach html, uruchomić narzędzie, aby wstępnie skompilować je wszystkie za pomocą podkreślenia (co moim zdaniem jest zależne od wtyczki JST, ale nie martw się, menedżer pakietów węzłów automatycznie zainstaluje zależności).

Powiedzmy, że kompiluje to wszystkie szablony do jednego skryptu

templates.js

Wczytanie skryptu domyślnie ustawi globalny - „JST” - który jest tablicą funkcji, do których można uzyskać dostęp w następujący sposób:

JST['templates/listView.html']()

który byłby podobny do

_.template( $('#selector-to-your-script-template'))

jeśli umieścisz zawartość tego tagu skryptu w (templates /) listView.html

Jednak prawdziwy kicker jest następujący: Grunt zawiera zadanie zwane „obserwacją”, które w zasadzie będzie monitorować zmiany w plikach, które zdefiniowałeś w lokalnym pliku grunt.js (który jest w zasadzie plikiem konfiguracyjnym dla twojego projektu Grunt, w javascript ). Jeśli masz chrząknięcie, rozpocznij to zadanie za siebie, wpisując:

grunt watch

z wiersza poleceń Grunt będzie monitorował wszystkie zmiany, które wprowadzasz w plikach i automatycznie wykona wszystkie zadania, które ustawiłeś dla niego w tym pliku grunt.js, jeśli wykryje zmiany - tak jak zadanie jst opisane powyżej. Edytuj, a następnie zapisz swoje pliki, a wszystkie szablony zostaną ponownie skompilowane w jednym pliku js, nawet jeśli są rozmieszczone w wielu katalogach i podkatalogach.

Podobne zadania można skonfigurować do lintowania javascript, uruchamiania testów, konkatenacji i minifikacji / uglifowania plików skryptów. A wszystko to można powiązać z zadaniem podglądu, więc zmiany w plikach automatycznie uruchomią nową „kompilację” projektu.

Skonfigurowanie rzeczy i zrozumienie, jak skonfigurować plik grunt.js, zajmuje trochę czasu, ale jest to warte zainwestowanego czasu i nie sądzę, abyś kiedykolwiek wrócił do sposobu pracy sprzed pracy

Mansiemans
źródło
Ulubiona odpowiedź. To powinna być akceptowana odpowiedź. (nie moje)
Brian Genisio
Niezły punkt wejścia do chrząknięcia. Działa dobrze w przypadku zwykłego HTML, ale jeśli mam <% = price%> lub podobny, otrzymuję: nieoczekiwany token =, nie udało się skompilować z grunt
mcktimo
Podoba mi się to podejście (przy użyciu JST), z wyjątkiem tego, że mam problemy z zrobieniem tego: template: JST['test.html']()wydaje się, że nie ładuje danych z JST :( (zobacz moje pytanie tutaj: stackoverflow.com/questions/29723392/ ... )
timhc22
15

Myślę, że to może ci pomóc. Wszystko w rozwiązaniu kręci się wokół require.jsbiblioteki, która jest plikiem JavaScript i modułem ładującym.

Samouczek pod powyższym linkiem bardzo ładnie pokazuje, jak można zorganizować projekt szkieletowy. Dostarczono również przykładową implementację . Mam nadzieję że to pomoże.

nayaab
źródło
3
Dzięki za odniesienie do mojej witryny, dla każdego, kto szuka, rozpocząłem projekt, który próbuje wdrożyć najlepsze praktyki backboneboilerplate.com
Thomas Davis
4

Zainteresowałem się szablonami javascript i teraz stawiam pierwsze kroki z kręgosłupem. To właśnie wymyśliłem i wydaje się, że działa całkiem nieźle.

window.App = {

    get : function(url) {
        var data = "<h1> failed to load url : " + url + "</h1>";
        $.ajax({
            async: false,
            url: url,
            success: function(response) {
                data = response;
            }
        });
        return data;
    }
}

App.ChromeView = Backbone.View.extend({
    template: _.template( App.get("tpl/chrome.html") ),
    render: function () {
        $(this.el).html(this.template());
        return this;
    },
});

App.chromeView = new App.ChromeView({ el : document.body });
App.chromeView.render();
j040p3d20
źródło
W twojej getfunkcji prawdopodobnie zwróciłbym $.ajaxsamą siebie, więc zwraca obiekt obietnicy, więc na wypadek, gdyby twój szablon nie odpowiedział od razu.
Dennis Rongo,
4

Musiałem ustawić typ danych na „tekst”, aby działał dla mnie:

get : function(url) {
    var data = "<h1> failed to load url : " + url + "</h1>";
    $.ajax({
        async: false,
        dataType: "text",
        url: url,
        success: function(response) {
            data = response;
        }
    });
    return data;
}
user1828189
źródło
2

Znalazłem rozwiązanie, które działa dla mnie z wykorzystaniem jQuery.

Dodaję kod szablonu podkreślenia, za pomocą metody jQuery.load (), do głównego pliku html.

Gdy już tam jest, używam go do generowania szablonów. Wszystko musi się odbywać synchronicznie!

Koncepcja jest taka:

Mam podkreślony kod szablonu mapy:

<!-- MAP TEMPLATE-->
<script type="text/template" id="game-map-template">
    <% _.each(rc, function(rowItem, index){ %>
      <ul class="map-row" data-row="<%- index %>">
        <li class="map-col <%- colItem.areaType ? 'active-area' : '' %>"></li>
        ...
</script>

Umieściłem ten kod w pliku o nazwie map-template.html

Następnie tworzę opakowanie dla plików szablonów.

<div id="templatesPool"></div>

Następnie dołączam ten plik do mojego głównego pliku HTML.

W głowie:

<!-- Template Loader -->
<script> 
    $(function(){
      $("#templatesPool").append($('<div>').load("map-template.html")); 
    });
</script> 

Twoje zdrowie.

Kaloyan Stamatov
źródło
1

Wiem, że to pytanie jest naprawdę stare, ale pojawiło się jako pierwszy wynik w wyszukiwarce Google dla szablonów Ajax podkreślenia.

Byłem zmęczony nie znajdowaniem dobrego rozwiązania, więc stworzyłem własne:

https://github.com/ziad-saab/underscore-async-templates

Oprócz wczytywania szablonów podkreślenia za pomocą AJAX, dodaje funkcję <% include%>. Mam nadzieję, że komuś się przyda.

ziad-saab
źródło
0

Byłem trochę niespokojny, zmuszając jQuery do synchronicznego działania, więc zmodyfikowałem poprzedni przykład synchroniczny, używając obietnic. To prawie to samo, ale działa asynchronicznie. W tym przykładzie używam szablonów HBS:

var asyncRenderHbs= function(template_name, template_data) {
    if (!asyncRenderHbs.template_cache) { 
        asyncRenderHbs.template_cache= {};
    }

    var promise= undefined;

    if (!asyncRenderHbs.template_cache[template_name]) {
        promise= new Promise(function(resolve, reject) {
            var template_url= '/templates/' + template_name;
            $.ajax({
                url: template_url,
                method: 'GET',
                success: function(data) {
                    asyncRenderHbs.template_cache[template_name]= Handlebars.compile(data);
                    resolve(asyncRenderHbs.template_cache[template_name](template_data));
                },
                error: function(err, message) {
                    reject(err);
                }           
            });
        });
    } else {
        promise= Promise.resolve(asyncRenderHbs.template_cache[template_name](template_data));
    }

    return promise;
};

Następnie użyj wyrenderowanego html:

asyncRenderHbs('some_template.hbs', context)
    .then(function(html) {
        applicationMain.append(html);
        // Do other stuff here after html is rendered...
    })
    .catch(function(err) {
        // Handle errors
    });

UWAGA: Jak omówiono przez innych, lepiej byłoby skompilować wszystkie szablony w jednym pliku templates.js i załadować go na początku, zamiast wykonywać wiele małych synchronicznych wywołań AJAX, aby uzyskać szablony podczas ładowania strony internetowej.

Megatron
źródło
0

Ostrzeżenie do przodu - oto smoki:

Wspominam o podejściu pokazanym poniżej, aby pomóc tym, którzy walczą o to, aby stosy ASP.NET (i podobne frameworki) działały harmonijnie z ekosystemem js-libs. Nie ulega wątpliwości, że nie jest to ogólne rozwiązanie. Powiedziawszy to ...

/ endforwardwarning

Jeśli używasz ASP.NET, możesz udostępnić szablony na zewnątrz, po prostu umieszczając je w jednym lub kilku własnych widokach częściowych. Aka wewnątrz twojego .cshtml:

  @Html.Partial("path/to/template")

Wewnątrz pliku template.cshtml:

   // this is razorview and thusly if you ever need to use the @ character in here  
   // you will have to either escape it as @@ or use the html codepoint which is &#64
   // http://stackoverflow.com/questions/3626250/escape-character-in-razor-view-engine
   <script type="text/x-template" id="someId">
        <span class="foo"><%= name %></span>
   </script>

A teraz możesz użyć szablonu jak zwykle:

  _.template($("#someId").html())({ name: "Foobar" });

Mam nadzieję, że to nieuchwytne i oczywiste podejście pomoże komuś zaoszczędzić godzinę na drapaniu się po głowie.

XDS
źródło