Plantilla externa en subrayado

121

Yo uso la plantilla de subrayado . ¿Es posible adjuntar un archivo externo como plantilla ?

En Backbone View tengo:

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

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

En mi html es:

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

Funciona bien. Pero necesito una plantilla externa . Lo intento:

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

o

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

o

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

Pero no funcionó.

Tomáš
fuente

Respuestas:

51

EDITAR: Esta respuesta es antigua y obsoleta. Lo eliminaría, pero es la respuesta "aceptada". Inyectaré mi opinión en su lugar.

No recomendaría hacer esto nunca más. En cambio, separaría todas las plantillas en archivos HTML individuales. Algunos sugerirían cargarlos de forma asíncrona (Require.js o una especie de caché de plantillas). Eso funciona bien en proyectos pequeños, pero en proyectos grandes con muchas plantillas, te encuentras haciendo un montón de pequeñas solicitudes asíncronas en la carga de la página que realmente no me gusta. (ugh ... ok, puedes solucionarlo con Require.js precompilando tus dependencias iniciales con r.js, pero para las plantillas, todavía me parece mal)

Me gusta usar una tarea gruñona (grunt-contrib-jst) para compilar todas las plantillas HTML en un solo archivo templates.js e incluir eso. Obtiene lo mejor de todos los mundos IMO ... las plantillas viven en un archivo, la compilación de dichas plantillas se realiza en el momento de la compilación (no en tiempo de ejecución) y no tiene cien pequeñas solicitudes asíncronas cuando se inicia la página.

Todo lo que hay debajo es basura

Para mí, prefiero la simplicidad de incluir un archivo JS con mi plantilla. Entonces, podría crear un archivo llamado view_template.js que incluye la plantilla como una variable:

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

Luego, es tan simple como incluir el archivo de script como uno normal y luego usarlo en su vista:

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

Yendo un paso más allá, en realidad uso coffeescript, por lo que mi código se parece más a esto y evito los caracteres de escape al final de la línea:

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

El uso de este enfoque evita la acumulación en require.js donde realmente no es necesario.

Brian Genisio
fuente
46
este enfoque perdería cualquier función de resaltado de sintaxis, reformateo y refactorización disponible con el ide. Sin embargo, no votar.
Kinjal Dixit
1
Lo siento, pero tuve que rechazar esta respuesta. Es horriblemente torpe, ya que aún conservará los archivos de plantilla como archivos de script, solo un poco forzados a parecerse a las plantillas. Las plantillas deben ser plantillas, por lo que si tiene que traer Require.js o usar la brillante solución de koorchik a continuación, creo que definitivamente vale la pena.
Tommi Forsström
3
@ TommiForsström Estoy de acuerdo. Me he alejado de este enfoque. ¡Guauu! 4 de diciembre de 2011 hace mucho tiempo en el mundo del desarrollo de Backbone.js :)
Brian Genisio
En realidad, me gustaría eliminar esta respuesta pero no puedo porque es la respuesta aceptada. Está desactualizado y hay soluciones mucho mejores que esta. Hoy, los tendría como archivos de plantilla separados y usaría una tarea gruñona (JST, por ejemplo) para construirlos en un archivo templates.js separado para evitar la naturaleza asincrónica de buscarlos individualmente. Es lo mejor de ambos mundos acercarse a la OMI.
Brian Genisio
bueno, si no hay muchas plantillas, creo que la solución anterior es realmente la más eficiente.
silkAdmin
107

Aquí hay una solución simple:

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

Usar "async: false" aquí no es una mala manera porque, en cualquier caso, debe esperar hasta que se cargue la plantilla.

Entonces, la función "render"

  1. le permite almacenar cada plantilla en un archivo html separado en un directorio estático
  2. es muy ligero
  3. compila y almacena en caché plantillas
  4. lógica de carga de plantillas de resúmenes. Por ejemplo, en el futuro puede usar plantillas precargadas y precompiladas.
  5. es fácil de usar

[Estoy editando la respuesta en lugar de dejar un comentario porque creo que esto es importante.]

si las plantillas no se muestran en la aplicación nativa , y usted ve HIERARCHY_REQUEST_ERROR: DOM Exception 3, mire la respuesta de Dave Robinson a ¿Qué puede causar exactamente un error "HIERARCHY_REQUEST_ERR: DOM Exception 3"? .

Básicamente, debes agregar

dataType: 'html'

a la solicitud $ .ajax.

koorchik
fuente
3
@BinaryNights: ¿deberíamos agregar siempre dataType: 'html'a nuestra solicitud ajax, por si acaso?
Matt
¿Funciona esto también para vistas anidadas? Aparentemente no puedo hacer que funcione si una vista se refiere a otra vista.
T. Rossi
1
Sí, también debería funcionar para plantillas anidadas. Simplemente agregue el asistente de renderización y llámelo como: <% = render ('nested_template', data)%>
koorchik
Hola, ¿puedes explicarme un poco más sobre las "plantillas de compilaciones y cachés"? Cuando intenté llamar a la función de representación, no agregó tmpl_data para devolver el valor, simplemente lo pasó como está. Tuve que llamar al método "Handlebars.compile" después de eso. Gracias.
cdagli
18

Este mixin permite generar una plantilla externa mediante subrayado de manera muy sencilla: _.templateFromUrl(url, [data], [settings]). Método API es casi lo mismo que el subrayado _.template () . Almacenamiento en caché incluido.

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

Uso:

var someHtml = _.templateFromUrl("http://example.com/template.html", {"var": "value"});
Dmitriy
fuente
2
Realmente un pequeño mixin muy bueno! :) aplausos por compartir
Nick White
Muy bien D, este era el tipo de solución que estaba buscando. y creo que podría usarse para mantener privado un conjunto de plantillas.
bigmadwolf
@abhi se proporciona en la respuesta. Además, necesita jQuery para cargar la plantilla, pero puede reescribir parte del código que carga la plantilla a través de AJAX a su gusto utilizando cualquier otra biblioteca.
Dmitriy
@Dmitriy async: false está en desuso, por lo que si llamo sin parámetro asincrónico no funciona, creo que esto se debe a que, de forma predeterminada, async es verdadero, lo que significa llamar a syncronisilly, entonces, ¿tiene una solución para este problema
Abhi
@abhi, funciona para jQuery 1. * También vea esta respuesta stackoverflow.com/a/11755262/541961
Dmitriy
17

No quería usar require.js para esta tarea simple, así que usé la solución de koorchik modificada.

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

¿Por qué agregar plantillas al documento, en lugar de almacenarlas en un objeto javascript? Debido a que en la versión de producción me gustaría generar un archivo html con todas las plantillas ya incluidas, por lo que no necesitaré hacer ninguna solicitud adicional de ajax. Y al mismo tiempo, no necesitaré realizar ninguna refactorización en mi código, ya que uso

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

en mis puntos de vista Backbone.

Tyth
fuente
1
Usando esto también, es genial para el escenario en el que trato de usar Jasmine para TDD y deseo probar plantillas antes de haber implementado requirejs y su complemento textjs. Bien hecho @Tramp
Nicholas Murray
La llamada a $ .ajax es asíncrona, cualquier cosa que dependa de los resultados, debe ejecutarse dentro del método hecho de la promesa devuelta.
JoshRoss
Gracias por esto. Lo usé. Una sugerencia: no hay razón para agregar una etiqueta de script, simplemente podría seguir adelante y convertir a una plantilla y mantenerla en un hash de búsqueda. Aquí hay un ejemplo de violín (no funcional): jsfiddle.net/PyzeF
webnesto
async: falseahora está en desuso
ProblemsOfSumit
Como async: falseestá en desuso, mejoré la respuesta al agregar la completedevolución de llamada.
Alexander
16

Esto podría estar ligeramente fuera de tema, pero podría usar Grunt (http://gruntjs.com/), que se ejecuta en node.js (http://nodejs.org/, disponible para todas las plataformas principales) para ejecutar tareas desde línea de comando. Hay un montón de complementos para esta herramienta, como un compilador de plantillas, https://npmjs.org/package/grunt-contrib-jst . Consulte la documentación en GitHub, https://github.com/gruntjs/grunt-contrib-jst . (También deberá comprender cómo ejecutar el administrador de paquetes de nodos, https://npmjs.org/ . No se preocupe, es increíblemente fácil y versátil).

Luego puede mantener todas sus plantillas en archivos html separados, ejecutar la herramienta para precompilarlas todas con subrayado (lo que creo que es una dependencia para el complemento JST, pero no se preocupe, el administrador de paquetes de nodo instalará automáticamente las dependencias por usted).

Esto compila todas sus plantillas en un script, digamos

templates.js

Al cargar el script se establecerá un global - "JST" por defecto - que es una matriz de funciones, y se puede acceder de esta manera:

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

que sería similar a

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

si coloca el contenido de esa etiqueta de script en (templates /) listView.html

Sin embargo, el verdadero truco es este: Grunt viene con esta tarea llamada 'watch', que básicamente supervisará los cambios en los archivos que ha definido en su archivo grunt.js local (que es básicamente un archivo de configuración para su proyecto Grunt, en javascript ) Si tienes gruñido comienza esta tarea por ti, escribiendo:

grunt watch

desde la línea de comandos, Grunt supervisará todos los cambios que realice en los archivos y ejecutará automáticamente todas las tareas que haya configurado en ese archivo grunt.js si detecta cambios, como el tarea jst descrita anteriormente. Edite y guarde sus archivos, y todas sus plantillas se vuelven a compilar en un solo archivo js, ​​incluso si están distribuidas en varios directorios y subdirectorios.

Se pueden configurar tareas similares para alinear su javascript, ejecutar pruebas, concatenar y minimizar / uglificar sus archivos de script. Y todo puede estar vinculado a la tarea de observación, por lo que los cambios en sus archivos desencadenarán automáticamente una nueva 'compilación' de su proyecto.

Lleva algo de tiempo configurar las cosas y entender cómo configurar el archivo grunt.js, pero vale la pena el tiempo invertido, y no creo que alguna vez vuelvas a una forma de trabajo previa al gruñido.

Mansiemans
fuente
Respuesta favorita Esta debería ser la respuesta aceptada. (no mío)
Brian Genisio
Buen punto de entrada para gruñir. Funciona bien para HTML simple, pero si tengo <% = price%> o similar, obtengo: token inesperado =, no se pudo compilar desde gruñido
mcktimo
Me gusta este enfoque (usando JST), excepto que tengo problemas para hacer esto: template: JST['test.html']()no parece estar cargando los datos desde JST :( (vea mi pregunta aquí: stackoverflow.com/questions/29723392/… )
timhc22
15

Creo que esto es lo que podría ayudarte. Todo en la solución gira en torno a la require.jsbiblioteca, que es un cargador de módulos y archivos JavaScript.

El tutorial en el enlace de arriba muestra muy bien cómo se podría organizar un proyecto de red troncal. También se proporciona una implementación de muestra . Espero que esto ayude.

nayaab
fuente
3
Gracias por la referencia a mi sitio, para cualquiera que esté buscando, he comenzado un proyecto que intenta implementar las mejores prácticas backboneboilerplate.com
Thomas Davis
4

Me interesé en la plantilla de JavaScript y ahora estoy dando los primeros pasos con la red troncal. Esto es lo que se me ocurrió y parece funcionar bastante bien.

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
fuente
En su getfunción, probablemente devolvería el $.ajaxmismo para que devuelva un objeto de promesa, en caso de que su plantilla no responda de inmediato.
Dennis Rongo
4

Tuve que establecer el tipo de datos en "texto" para que funcione para mí:

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;
}
usuario1828189
fuente
2

Encontré una solución que me funciona usando jQuery.

Agrego el código de plantilla de subrayado, con el método jQuery.load (), al archivo html principal.

Una vez que está allí, lo estoy usando para generar las plantillas. ¡Todo debe suceder sincrónicamente!

El concepto es:

Tengo un código de plantilla de mapa de subrayado:

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

Y puse ese código en un archivo llamado map-template.html

Después de eso, creo un contenedor para los archivos de plantilla.

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

Luego incluyo ese archivo en mi archivo html principal de esta manera.

En cabeza:

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

Salud.

Kaloyan Stamatov
fuente
1

Sé que esta pregunta es muy antigua, pero surgió como el primer resultado en una búsqueda en Google de plantillas de subrayado ajax.

Estaba cansado de no encontrar una buena solución para esto, así que creé el mío:

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

Además de cargar plantillas de subrayado con AJAX, agrega la funcionalidad <% include%>. Espero que pueda ser útil para alguien.

ziad-saab
fuente
0

Estaba un poco incómodo forzando a jQuery a funcionar sincrónicamente, así que modifiqué el ejemplo sincrónico anterior usando promesas. Es casi lo mismo, pero se ejecuta de forma asincrónica. Estoy usando plantillas hbs en este ejemplo:

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

Luego, para usar el html representado:

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

NOTA: Según lo discutido por otros, sería preferible compilar todas las plantillas en un solo archivo templates.js y cargarlo al principio en lugar de tener muchas llamadas AJAX sincrónicas pequeñas para obtener plantillas cuando se carga la página web.

Megatron
fuente
0

Advertencia delantera: aquí hay dragones:

Menciono el enfoque que se muestra a continuación simplemente para ayudar a aquellos que luchan por hacer que las pilas ASP.NET (y marcos similares) funcionen en armonía con el ecosistema de js-libs. No hace falta decir que esta no es una solución genérica. Una vez dicho esto ...

/ endforwardwarning

Si está utilizando ASP.NET, puede externalizar sus plantillas simplemente colocándolas dentro de una o más vistas parciales propias. Aka dentro de su .cshtml:

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

Dentro de su 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>

Y ahora puedes usar la plantilla como de costumbre:

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

Espero que este enfoque tan obvio ayude a alguien a ahorrar una hora de rascarse la cabeza.

XDS
fuente