Externe Vorlage in Unterstrich


121

Ich verwende eine Unterstreichungsvorlage . Ist es möglich, eine externe Datei als Vorlage anzuhängen ?

In der Backbone-Ansicht habe ich:

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

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

In meinem HTML ist:

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

Es funktioniert gut. Aber ich brauche eine externe Vorlage . Ich versuche:

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

oder

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

oder

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

aber es hat nicht funktioniert.

Antworten:


51

EDIT: Diese Antwort ist alt und veraltet. Ich würde es löschen, aber es ist die "akzeptierte" Antwort. Ich werde stattdessen meine Meinung einbringen.

Ich würde das nicht mehr befürworten. Stattdessen würde ich alle Vorlagen in einzelne HTML-Dateien aufteilen. Einige würden vorschlagen, diese asynchron zu laden (Require.js oder eine Art Vorlagen-Cache). Das funktioniert gut bei kleinen Projekten, aber bei großen Projekten mit vielen Vorlagen stellen Sie beim Laden der Seite eine Menge kleiner asynchroner Anforderungen, die ich wirklich nicht mag. (ugh ... ok, Sie können es mit Require.js umgehen, indem Sie Ihre anfänglichen Abhängigkeiten mit r.js vorkompilieren, aber für Vorlagen fühlt sich das für mich immer noch falsch an.)

Ich verwende gerne eine Grunzaufgabe (grunt-contrib-jst), um alle HTML-Vorlagen in eine einzige templates.js-Datei zu kompilieren und diese einzuschließen. Sie erhalten die besten IMO ... -Vorlagen aller Welten live in einer Datei, die Kompilierung dieser Vorlagen erfolgt zur Erstellungszeit (nicht zur Laufzeit) und Sie haben beim Start der Seite keine hundert winzigen asynchronen Anforderungen.

Alles unten ist Müll

Ich bevorzuge die Einfachheit, eine JS-Datei in meine Vorlage aufzunehmen. Daher kann ich eine Datei mit dem Namen view_template.js erstellen, die die Vorlage als Variable enthält:

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

Dann ist es so einfach, die Skriptdatei wie eine normale einzuschließen und sie dann in Ihrer Ansicht zu verwenden:

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

Wenn ich noch einen Schritt weiter gehe , verwende ich tatsächlich Coffeescript, sodass mein Code eher so aussieht und die Escape-Zeichen am Zeilenende vermieden werden:

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

Durch die Verwendung dieses Ansatzes wird vermieden, dass in require.js gesalzen wird, wo dies wirklich nicht erforderlich ist.


46
Dieser Ansatz würde alle Syntax-Hervorhebungs-, Neuformatierungs- und Refactoring-Funktionen verlieren, die mit der ide verfügbar sind. aber nicht abstimmen.
Kinjal Dixit

1
Es tut mir leid, aber ich musste diese Antwort ablehnen. Es ist schrecklich klobig, da es weiterhin Vorlagendateien als Skriptdateien behält, nur irgendwie gezwungen, wie Vorlagen auszusehen. Vorlagen müssen Vorlagen sein. Wenn Sie also Require.js einbringen oder die unten genannte brillante Lösung von koorchik verwenden müssen, ist es meiner Meinung nach definitiv wert.
Tommi Forsström

3
@ TommiForsström Ich stimme zu. Ich habe mich von diesem Ansatz entfernt. Beeindruckend! Der 4. Dezember 2011 ist in der Welt der Entwicklung von Backbone.js wirklich lange her :)
Brian Genisio

Eigentlich möchte ich diese Antwort löschen, aber ich kann nicht, weil es die akzeptierte Antwort ist. Es ist veraltet und es gibt viel bessere Lösungen als diese. Heute würde ich sie als separate Vorlagendateien haben und eine Grunzaufgabe (zum Beispiel JST) verwenden, um sie in eine separate templates.js-Datei aufzubauen, um die asynchrone Natur des Abrufens aller einzeln zu vermeiden. Es ist das Beste aus beiden Welten, sich IMO zu nähern.
Brian Genisio

Nun, wenn es nicht viele Vorlagen gibt, denke ich, dass die frühere Lösung wirklich die effizienteste ist.
SeideAdmin

107

Hier ist eine einfache Lösung:

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

Die Verwendung von "async: false" ist hier kein schlechter Weg, da Sie auf jeden Fall warten müssen, bis die Vorlage geladen wird.

Also "Rendern" -Funktion

  1. Mit dieser Option können Sie jede Vorlage in einer separaten HTML-Datei im statischen Verzeichnis speichern
  2. ist sehr leicht
  3. Kompiliert und speichert Vorlagen
  4. Ladelogik für Abstracts-Vorlagen. Beispielsweise können Sie in Zukunft vorinstallierte und vorkompilierte Vorlagen verwenden.
  5. ist einfach zu bedienen

[Ich bearbeite die Antwort, anstatt einen Kommentar zu hinterlassen, weil ich dies für wichtig halte.]

Wenn Vorlagen in der nativen App nicht angezeigt werden und Sie sehen HIERARCHY_REQUEST_ERROR: DOM Exception 3, lesen Sie die Antwort von Dave Robinson auf Was genau kann einen "HIERARCHY_REQUEST_ERR: DOM-Ausnahme 3" -Fehler verursachen? .

Grundsätzlich müssen Sie hinzufügen

dataType: 'html'

auf die $ .ajax Anfrage.


3
@BinaryNights - sollten wir dataType: 'html'unsere Ajax-Anfrage immer ergänzen , nur für den Fall?
Matt

Funktioniert dies auch für verschachtelte Ansichten? Anscheinend kann es nicht funktionieren, wenn sich eine Ansicht auf eine andere Ansicht bezieht.
T. Rossi

1
Ja, es sollte auch für verschachtelte Vorlagen funktionieren. Fügen Sie einfach den Render-Helfer hinzu und nennen Sie ihn wie folgt: <% = render ('nested_template', data)%>
koorchik

Hallo, können Sie etwas mehr über "Kompilieren und Zwischenspeichern von Vorlagen" erklären? Als ich versuchte, die Renderfunktion aufzurufen, wurden die tmpl_data nicht als Rückgabewert hinzugefügt, sondern nur so übergeben, wie sie ist. Danach musste ich die Methode "Handlebars.compile" aufrufen. Danke dir.
cdagli

18

Mit diesem Mixin können Sie externe Vorlagen mit Underscore auf sehr einfache Weise rendern : _.templateFromUrl(url, [data], [settings]). Die Methoden-API ist fast identisch mit der _.template () von Underscore . Caching inklusive.

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

Verwendung:

var someHtml = _.templateFromUrl("http://example.com/template.html", {"var": "value"});

2
Wirklich nettes kleines Mixin da sehr ordentlich! :) Prost für das Teilen
Nick White

Sehr cool D, das war die Art von Lösung, nach der ich gesucht habe. und ich denke, könnte verwendet werden, um eine Reihe von Vorlagen privat zu halten.
Bigmadwolf

@abhi es ist in der Antwort angegeben. Außerdem benötigen Sie jQuery, um die Vorlage zu laden. Sie können jedoch einen Teil des Codes, der die Vorlage über AJAX lädt, nach Belieben mit einer anderen Bibliothek neu schreiben.
Dmitriy

@Dmitriy async: false ist veraltet. Wenn ich also ohne async-Parameter aufrufe, funktioniert dies nicht. Ich denke, dies liegt daran, dass async standardmäßig true ist, was bedeutet, dass
syncronisilly aufgerufen wird.

@abhi, es funktioniert für jQuery 1. * Siehe auch diese Antwort stackoverflow.com/a/11755262/541961
Dmitriy

17

Ich wollte require.js nicht für diese einfache Aufgabe verwenden, also habe ich die modifizierte Lösung von koorchik verwendet.

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

Warum sollten Vorlagen an Dokumente angehängt werden, anstatt sie in einem Javascript-Objekt zu speichern? Da ich in der Produktionsversion eine HTML-Datei mit allen bereits enthaltenen Vorlagen generieren möchte, muss ich keine zusätzlichen Ajax-Anforderungen stellen. Gleichzeitig muss ich in meinem Code kein Refactoring vornehmen, wie ich es verwende

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

in meinen Backbone-Ansichten.


1
Dies ist auch ideal für das Szenario, in dem ich versuche, Jasmine für TDD zu verwenden, und Vorlagen testen möchte, bevor ich requirejs und sein textjs-Plugin implementiert habe. Gut gemacht @Tramp
Nicholas Murray

Der Aufruf von $ .ajax ist asynchron. Abhängig von den Ergebnissen sollte alles innerhalb der Methode done des zurückgegebenen Versprechens ausgeführt werden.
JoshRoss

Danke dafür. Ich benutzte es. Ein Vorschlag: Kein Grund, als Skript-Tag anzuhängen - Sie können einfach in eine Vorlage konvertieren und diese in einem Such-Hash aufbewahren. Hier ist ein (nicht funktionierendes) Geigenbeispiel
webnesto

async: falseist jetzt veraltet
ProblemsOfSumit

Da async: falsees veraltet ist, habe ich die Antwort durch Hinzufügen eines completeRückrufs verbessert .
Alexander

16

Dies mag etwas vom Thema abweichen, aber Sie können Grunt (http://gruntjs.com/) verwenden, das auf node.js (http://nodejs.org/, verfügbar für alle wichtigen Plattformen) ausgeführt wird, um Aufgaben von der Website aus auszuführen Befehlszeile. Es gibt eine Reihe von Plugins für dieses Tool, z. B. einen Vorlagen-Compiler, https://npmjs.org/package/grunt-contrib-jst . Siehe Dokumentation zu GitHub unter https://github.com/gruntjs/grunt-contrib-jst . (Sie müssen auch wissen , wie Sie den Node Package Manager https://npmjs.org/ ausführen . Keine Sorge, es ist unglaublich einfach und vielseitig.)

Sie können dann alle Ihre Vorlagen in separaten HTML-Dateien aufbewahren und das Tool ausführen, um sie alle mit Unterstrich vorkompilieren (was meiner Meinung nach eine Abhängigkeit für das JST-Plugin ist, aber keine Sorge, der Node Package Manager installiert Abhängigkeiten automatisch für Sie).

Dadurch werden beispielsweise alle Ihre Vorlagen zu einem Skript zusammengefasst

templates.js

Beim Laden des Skripts wird ein globaler Wert festgelegt - standardmäßig "JST" - ein Array von Funktionen, auf den wie folgt zugegriffen werden kann:

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

das wäre ähnlich wie

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

Wenn Sie den Inhalt dieses Skript-Tags in (templates /) listView.html einfügen

Der eigentliche Kicker ist jedoch folgender: Grunt wird mit dieser Aufgabe namens 'watch' geliefert, die im Wesentlichen Änderungen an Dateien überwacht, die Sie in Ihrer lokalen grunt.js-Datei definiert haben (die im Grunde eine Konfigurationsdatei für Ihr Grunt-Projekt ist, in Javascript) ). Wenn Sie grunzen, starten Sie diese Aufgabe für Sie, indem Sie Folgendes eingeben:

grunt watch

Über die Befehlszeile überwacht Grunt alle Änderungen, die Sie an den Dateien vornehmen, und führt alle Aufgaben, die Sie in dieser Datei grunt.js eingerichtet haben, automatisch aus, wenn Änderungen festgestellt werden - wie bei der oben beschriebenen jst- Aufgabe. Bearbeiten und speichern Sie Ihre Dateien, und alle Ihre Vorlagen werden in einer js-Datei neu kompiliert, auch wenn sie über mehrere Verzeichnisse und Unterverzeichnisse verteilt sind.

Ähnliche Aufgaben können konfiguriert werden, um Ihr Javascript zu fusseln, Tests auszuführen, Ihre Skriptdateien zu verketten und zu minimieren / zu verkleinern. Und alles kann an die Überwachungsaufgabe gebunden werden, sodass Änderungen an Ihren Dateien automatisch einen neuen "Build" Ihres Projekts auslösen.

Es dauert einige Zeit, um die Dinge einzurichten und zu verstehen, wie die Datei grunt.js konfiguriert wird, aber es lohnt sich die investierte Zeit, und ich glaube nicht, dass Sie jemals wieder zu einer vorgrunzenden Arbeitsweise zurückkehren werden


Lieblingsantwort. Dies sollte die akzeptierte Antwort sein. (nicht meins)
Brian Genisio

Schöner Einstiegspunkt zum Grunzen. Es funktioniert gut für einfaches HTML, aber wenn ich <% = price%> oder ähnliches habe, bekomme ich: unerwartetes Token =, konnte nicht von grunt kompiliert werden
mcktimo

Ich mag diesen Ansatz (mit JST), außer ich habe Probleme damit: template: JST['test.html']()Es scheint nicht, dass die Daten von JST geladen werden :( (siehe meine Frage hier: stackoverflow.com/questions/29723392/… )
timhc22

15

Ich denke , das ist das, was Ihnen helfen könnte. Alles in der Lösung dreht sich um die require.jsBibliothek, bei der es sich um eine JavaScript-Datei und einen Modullader handelt.

Das Tutorial unter dem obigen Link zeigt sehr gut, wie ein Backbone-Projekt organisiert werden kann. Eine Beispielimplementierung wird ebenfalls bereitgestellt. Hoffe das hilft.


3
Vielen Dank für den Verweis auf meine Website, für alle, die suchen, habe ich ein Projekt gestartet, das versucht, Best Practices zu implementieren backboneboilerplate.com
Thomas Davis

4

Ich habe mich für Javascript-Vorlagen interessiert und mache jetzt die ersten Schritte mit Backbone. Das habe ich mir ausgedacht und scheint ziemlich gut zu funktionieren.

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

In Ihrer getFunktion würde ich wahrscheinlich das $.ajaxselbst zurückgeben, damit es ein Versprechungsobjekt zurückgibt, falls Ihre Vorlage nicht sofort reagiert.
Dennis Rongo

4

Ich musste den Datentyp auf "Text" setzen, damit er für mich funktioniert:

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

2

Ich habe eine Lösung gefunden, die für mich mit jQuery funktioniert.

Ich füge den Unterstrich-Vorlagencode mit der Methode jQuery.load () zur HTML-Hauptdatei hinzu.

Sobald es dort ist, verwende ich es zum Generieren der Vorlagen. Alle müssen synchron ablaufen!

Das Konzept lautet:

Ich habe einen unterstrichenen Kartenvorlagencode:

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

Und ich habe diesen Code in eine Datei namens map-template.html eingefügt

Danach erstelle ich einen Wrapper für die Vorlagendateien.

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

Dann füge ich diese Datei wie folgt in meine HTML-Hauptdatei ein.

Im Kopf:

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

Prost.


1

Ich weiß, dass diese Frage sehr alt ist, aber sie war das erste Ergebnis einer Google-Suche nach Ajax-Vorlagen mit Unterstrich.

Ich war es leid, keine gute Lösung dafür zu finden, also habe ich meine eigene erstellt:

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

Zusätzlich zum Laden von Unterstrichvorlagen mit AJAX werden die Funktionen <% include%> hinzugefügt. Ich hoffe, es kann jemandem nützlich sein.


0

Es war mir etwas unangenehm, jQuery zu zwingen, synchron zu funktionieren, deshalb habe ich das vorherige synchrone Beispiel mithilfe von Versprechungen geändert. Es ist so ziemlich das Gleiche, läuft aber asynchron. Ich verwende in diesem Beispiel hbs-Vorlagen:

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

Um dann das gerenderte HTML zu verwenden:

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

HINWEIS: Wie von anderen erläutert, ist es vorzuziehen, alle Vorlagen in einer einzigen Datei templates.js zu kompilieren und diese am Anfang zu laden, anstatt viele kleine synchrone AJAX-Aufrufe zu haben, um Vorlagen beim Laden der Webseite abzurufen.


0

Vorwärtswarnung - Hier sind Drachen:

Ich erwähne den unten gezeigten Ansatz nur, um denjenigen zu helfen, die Schwierigkeiten haben, ASP.NET-Stacks (und ähnliche Frameworks) in Einklang mit dem Ökosystem von js-libs zu bringen. Es versteht sich von selbst, dass dies keine generische Lösung ist. Trotzdem ...

/ endforwardwarning

Wenn Sie ASP.NET verwenden, können Sie Ihre Vorlagen externalisieren, indem Sie sie einfach in einer oder mehreren eigenen Teilansichten platzieren. Aka in Ihrer .cshtml:

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

In Ihrer 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>

Und jetzt können Sie die Vorlage wie gewohnt verwenden:

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

Ich hoffe, dieser schwer fassbare Ansatz hilft jemandem, eine Stunde Kopfkratzen zu sparen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.