javascript - debounce - Plantilla externa en Underscore
underscore js vs lodash (12)
Advertencia hacia adelante - Aquí hay dragones:
Menciono el enfoque que se muestra a continuación simplemente para ayudar a aquellos que luchan por hacer que las pilas de ASP.NET (y marcos similares) funcionen armoniosamente 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 tu .cshtml:
@Html.Partial("path/to/template")
Dentro de tu 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 @
// 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 siempre:
_.template($("#someId").html())({ name: "Foobar" });
Espero que este enfoque elusivamente obvio ayude a alguien a ahorrar una hora en rascarse la cabeza.
Yo uso la plantilla 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 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ó.
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 opción porque, en cualquier caso, debes esperar hasta que se cargue la plantilla.
Entonces, la función "render"
- le permite almacenar cada plantilla en un archivo html separado en un directorio estático
- es muy liviano
- compila y almacena en caché las plantillas
- resume la lógica de carga de la plantilla. Por ejemplo, en el futuro puede usar plantillas precargadas y precargadas.
- 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 aparecen en la aplicación nativa , y ve HIERARCHY_REQUEST_ERROR: DOM Exception 3
, mire la respuesta de Dave Robinson a What exactamente puede causar un "HIERARCHY_REQUEST_ERR: DOM Exception 3" -Error? .
Básicamente, debes agregar
dataType: ''html''
a la solicitud $ .ajax.
Creo que this es lo que podría ayudarte. Todo en la solución gira en torno a la biblioteca require.js
, que es un archivo JavaScript y un cargador de módulos.
El tutorial en el enlace de arriba muestra muy bien cómo se podría organizar un proyecto troncal. También se proporciona una implementación de muestra . Espero que esto ayude.
Encontré una solución que funciona para mí con el uso de jQuery.
Agregué 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. ¡Todos deben 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 envoltorio para los archivos de plantilla.
<div id="templatesPool"></div>
Luego incluyo ese archivo en mi archivo html principal como tal.
En cabeza:
<!-- Template Loader -->
<script>
$(function(){
$("#templatesPool").append($(''<div>'').load("map-template.html"));
});
</script>
Aclamaciones.
Estaba un poco incómodo forzando a jQuery a funcionar sincrónicamente, así que modifiqué el ejemplo sincrónico anterior usando promesas. Es más o menos lo mismo, pero se ejecuta de forma asíncrona. 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 renderizado:
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 único archivo templates.js y cargar eso al principio en lugar de tener muchas llamadas AJAX pequeñas sincrónicas para obtener plantillas cuando se carga la página web.
Este mixin le permite representar una plantilla externa con Underscore de una manera muy simple: _.templateFromUrl(url, [data], [settings])
. Method API es casi lo mismo que _.template() Underscore . 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"});
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 el línea de comando. Hay una gran cantidad de complementos para esta herramienta, como un compilador de plantilla, https://npmjs.org/package/grunt-contrib-jst . Consulte la documentación en GitHub, https://github.com/gruntjs/grunt-contrib-jst . (También necesitará comprender cómo ejecutar el administrador de paquetes de nodos, https://npmjs.org/ . No se preocupe, es increíblemente fácil y versátil).
A continuación, puede mantener todas sus plantillas en archivos html separados, ejecute la herramienta para precompilarlos todos usando guiones bajos (que creo que es una dependencia para el plugin JST, pero no se preocupe, el administrador de paquetes del nodo instalará automáticamente dependencias para usted).
Esto compila todas sus plantillas en un script, por ejemplo
templates.js
Al cargar la secuencia de comandos, se establecerá un global - "JST" por defecto - que es una matriz de funciones, y se puede acceder de esta forma:
JST[''templates/listView.html'']()
que sería similar a
_.template( $(''#selector-to-your-script-template''))
si pones 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 a los archivos que haya definido en su archivo local grunt.js (que básicamente es un archivo de configuración para su proyecto Grunt, en javascript ) Si tienes un 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 el archivo grunt.js si detecta cambios, como la tarea jst descrita anteriormente. Edite y guarde sus archivos, y todas sus plantillas vuelvan a compilarse en un archivo js, incluso si están distribuidos en varios directorios y subdirectorios.
Se pueden configurar tareas similares para deshilar su javascript, ejecutar pruebas, concatenar y minificar / uglificar los archivos de script. Y todo puede vincularse a la tarea de vigilancia, por lo que los cambios en sus archivos activarán automáticamente una nueva ''compilación'' de su proyecto.
Lleva algo de tiempo configurar las cosas y comprender cómo configurar el archivo grunt.js, pero vale la pena, así que vale la pena invertir el tiempo invertido, y no creo que vuelvas nunca a la rutina de trabajo.
Me interesé en la creación de plantillas de JavaScript y ahora estoy dando los primeros pasos con la columna vertebral. 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();
No quería usar require.js para esta simple tarea, así que utilicé 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? Porque 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é hacer ninguna refactorización en mi código, ya que utilizo
this.template = _.template($(''#template_name'').html());
en mis vistas de Backbone.
Sé que esta pregunta es muy antigua, pero surgió como el primer resultado en una búsqueda en google de plantillas de ajax de subrayado.
Estaba cansado de no encontrar una buena solución para esto, así que creé la mía:
https://github.com/ziad-saab/underscore-async-templates
Además de cargar plantillas de subrayado mediante AJAX, agrega la funcionalidad <% include%>. Espero que pueda ser útil para alguien.
Tuve que configurar 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;
}
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 sugieren cargar estos de forma asincrónica (Require.js o una especie de caché de plantilla). Eso funciona bien en proyectos pequeños, pero en grandes proyectos con muchas plantillas, te encuentras haciendo un montón de pequeñas solicitudes de asincronización en la carga de la página que realmente no me gusta. (ugh ... bien, puedes resolverlo con Require.js compilando previamente tus dependencias iniciales con r.js, pero para las plantillas, esto todavía me parece incorrecto)
Me gusta usar una tarea de grunt (grunt-contrib-jst) para compilar todas las plantillas HTML en un único archivo templates.js e incluir eso. Obtienes lo mejor de todos los mundos IMO ... plantillas en vivo en un archivo, la compilación de dichas plantillas ocurre en tiempo de compilación (no en tiempo de ejecución), y no tienes cien pequeñas solicitudes asincrónicas cuando la página se inicia.
Todo a continuación 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 incluya la plantilla como una variable:
app.templates.view = " /
<h3>something code</h3> /
";
Entonces, 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á, realmente uso coffeescript, por lo que mi código en realidad se parece más a esto y evita los caracteres de escape al final de la línea:
app.templates.view = ''''''
<h3>something code</h3>
''''''
El uso de este enfoque evita saltear en require.js donde realmente no es necesario.