templates - parser - En Moustache, Cómo obtener el índice de la Sección actual
mustache template (10)
Estoy usando Mustache y usando los datos
{ "names": [ {"name":"John"}, {"name":"Mary"} ] }
Mi plantilla de bigote es:
{{#names}}
{{name}}
{{/names}}
Lo que quiero poder hacer es obtener un índice del número actual en la matriz. Algo como:
{{#names}}
{{name}} is {{index}}
{{/names}}
y hacer que se imprima
John is 1
Mary is 2
¿Es posible obtener esto con Moustache? o con manubrios u otra extensión?
(Probado en el nodo 4.4.7, bigote 2.2.1.)
Si desea una forma agradable y limpia de hacerlo, que no involucre variables globales ni mute los objetos, use esta función;
var withIds = function(list, propertyName, firstIndex) {
firstIndex |= 0;
return list.map( (item, idx) => {
var augmented = Object.create(item);
augmented[propertyName] = idx + firstIndex;
return augmented;
})
};
Úselo cuando esté armando su vista;
var view = {
peopleWithIds: withIds(people, ''id'', 1) // add ''id'' property to all people, starting at index 1
};
Lo bueno de este enfoque es que crea un nuevo conjunto de objetos ''viewmodel'', utilizando el conjunto antiguo como prototipos. Puede leer la person.id
mismo modo que leería person.firstName
. Sin embargo, esta función no cambia sus objetos de personas, por lo que ningún otro código (que podría haber dependido de que la propiedad de ID no esté allí) no se verá afectada.
El algoritmo es O (N), muy agradable y rápido.
Así es como lo hago en JavaScript:
var idx = 0;
var data = {
"names": [
{"name":"John"},
{"name":"Mary"}
],
"idx": function() {
return idx++;
}
};
var html = Mustache.render(template, data);
Su plantilla:
{{#names}}
{{name}} is {{idx}}
{{/names}}
Como referencia, esta funcionalidad ahora está integrada en Handlebars, que tiene compatibilidad con Moustache.
Usa {{@index}}
{{#names}}
{{name}} is {{@index}}
{{/names}}
John es 0
Mary es 1
En handlebars.js puedes lograr esto con una función de ayuda. (De hecho, una de las ventajas mencionadas sobre los manubrios aquí http://yehudakatz.com/2010/09/09/announcing-handlebars-js/ es que puede usar ayudantes en lugar de tener que reescribir los objetos antes de llamar a la plantilla.
Entonces, podrías hacer esto:
var nameIndex = 0;
Handlebars.registerHelper(''name_with_index'', function() {
nameIndex++;
return this.name + " is " + nameIndex;
})
Y, entonces tu plantilla puede ser esta:
{{#names}}
<li>{{name_with_index}}</li>
{{/names}}
Sus datos son los mismos que antes, es decir:
{ "names": [ {"name":"John"}, {"name":"Mary"} ] };
Y obtienes esta salida:
<li>John is 1</li>
<li>Mary is 2</li>
Para que esto realmente funcione, nameIndex necesita restablecerse cada vez que se procesa la plantilla, de modo que para hacer eso puede tener un ayudante de restablecimiento al principio de la lista. El código completo se ve así:
var data = { "names": [ {"name":"John"}, {"name":"Mary"} ] };
var templateSource = "<ul>{{reset_index}}{{#names}}<li>{{name_with_index}}</li>{{/names}}</ul>";
var template = Handlebars.compile(templateSource);
var helpers = function() {
var nameIndex = 0;
Handlebars.registerHelper(''name_with_index'', function() {
nameIndex++;
return this.name + " is " + nameIndex;
});
Handlebars.registerHelper(''reset_index'', function() {
nameIndex = 0;
})
}();
var htmlResult= template(data);
$(''#target'').html(htmlResult);
var htmlResult2= template(data);
$(''#target2'').html(htmlResult2);
(Esto puede renderizar correctamente la plantilla dos veces).
Gran ayudante aquí:
// {{#each_with_index records}}
// <li class="legend_item{{index}}"><span></span>{{Name}}</li>
// {{/each_with_index}}
Handlebars.registerHelper("each_with_index", function(array, fn) {
var buffer = "";
for (var i = 0, j = array.length; i < j; i++) {
var item = array[i];
// stick an index property onto the item, starting with 1, may make configurable later
item.index = i+1;
// show the inside of the block
buffer += fn(item);
}
// return the finished buffer
return buffer;
});
Fuente: https://gist.github.com/1048968
Mi principal caso de uso para esto fue la posibilidad de colocar una clase ''activa'' en los ítems. Me metí con la combinación de un ayudante "igual" con el ayudante "index_for_each", pero era demasiado complicado.
En cambio, se me ocurrió la siguiente ayuda básica "activa". Es muy específico, pero es un caso de uso común para cualquier escenario de menú / lista de selección:
Uso:
<ul>
{{{active 2}}}
{{#each menuItem}}
<li class="{{{active}}}">{{this}}</li>
{{/each}}
</div>
Haría que el tercer elemento del menú tenga una "clase = ''activa''".
El ayudante está aquí (esta es la versión de CoffeeScript):
active = 0
cur = 0
handlebars.registerHelper ''active'', (item, context) ->
if arguments.length == 2
active = item
cur = 0
''''
else if cur++ == active
''active''
Puede ejecutar el siguiente ciclo en su lista de objetos.
Esta solución tiene las siguientes ventajas:
- No cambia los datos del modelo
- Se puede acceder al índice varias veces
Código:
for( var i=0; i< the_list.length; i++) {
the_list[i].idx = (function(in_i){return in_i+1;})(i);
}
Explicación:
Se utiliza una función en lugar de un nombre de variable, por lo que los datos no están mutados. El cierre se usa para devolver el valor de ''i'' en el momento en que se crea la función en el bucle en lugar del valor de i al final del bucle.
Si puedes controlar la salida de la cadena JSON, entonces prueba esto.
{ "names": [ {"name":"John", "index":"1"}, {"name":"Mary", "index":"2"} ] }
Por lo tanto, cuando crea su cadena JSON, agregue el índice como otra propiedad para cada objeto.
Siempre que no esté viajando en más de una matriz, puede almacenar el índice en la ayuda y solo incrementar cuando el contexto cambie. Estoy usando algo similar a lo siguiente:
var data = {
foo: [{a: ''a'', ''b'': ''a''}, {''a'':''b'', ''b'':''b''}],
i: function indexer() {
indexer.i = indexer.i || 0;
if (indexer.last !== this && indexer.last) {
indexer.i++;
}
indexer.last = this;
return String(indexer.i);
}
};
Mustache.render(''{{#foo}}{{a}}{{i}}{{b}}{{i}}{{/foo}}'', data);
Un mejor enfoque para Moustache sería usar una función que obtiene el índice usando indexOf
:
var data = {
names: [ {"name":"John"}, {"name":"Mary"} ],
index: function() {
return data.names.indexOf(this);
}
};
var html = Mustache.render(template, data);
En tu plantilla:
{{#names}}
{{name}} is {{index}}
{{/names}}
El inconveniente es que esta función de index
tiene la matriz de data.names
de data.names
codificados data.names
Para que sea más dinámica, podemos usar otra función como esta:
var data = {
names: [ {"name":"John"}, {"name":"Mary"} ],
index: function() {
return function(array, render) {
return data[array].indexOf(this);
}
}
};
var html = Mustache.render(template, data);
Uso en tu plantilla:
{{#names}}
{{name}} is {{#index}}names{{/index}}
{{/names}}
También un pequeño inconveniente es que debe pasar el nombre de la matriz a la función de índice en este ejemplo de names
, {{#index}}names{{/index}}
.