Guiador / Bigode - Existe uma maneira integrada de percorrer as propriedades de um objeto?

216

Como o título da pergunta diz, existe uma maneira de bigode / guidão de percorrer as propriedades de um objeto ?

Então com

var o = {
  bob : 'For sure',
  roger: 'Unknown',
  donkey: 'What an ass'
}

Posso então fazer algo no mecanismo de modelo que seria equivalente a

for(var prop in o)
{
    // with say, prop a variable in the template and value the property value
}

?

Ben
fonte

Respostas:

448

Suporte interno desde o guidão 1.0rc1

O suporte para essa funcionalidade foi adicionado ao Handlebars.js, portanto, não há mais necessidade de auxiliares externos.

Como usá-lo

Para matrizes:

{{#each myArray}}
    Index: {{@index}} Value = {{this}}
{{/each}}

Para objetos:

{{#each myObject}}
    Key: {{@key}} Value = {{this}}
{{/each}}

Observe que apenas as propriedades que passam no hasOwnPropertyteste serão enumeradas.

Jon
fonte
2
@ Rafi: não se pode entender muito isso sem conhecer sua estrutura de dados.
22413 Jon
3
@ Rafi: você não quis dizer {{this.title}}?
Nevyn
2
@qodeninja: Simples: da mesma maneira que você se refere aos valores nos exemplos acima - com {{#each this}}. Sua escolha de termos também é confusa (o que torna um objeto "de nível superior" e outro não? O que são chaves "predefinidas" exatamente? Etc)); portanto, você pode revisitar esses conceitos.
11114 Jon
1
se não se enganar, apenas a v1.1.0 estará disponível, mas ótima resposta, obrigado.
Renars Sirotins
2
Como você faz isso apenas para uma lista de permissões específica de propriedades?
Marco Prins
70

Na verdade, é muito fácil de implementar como auxiliar:

Handlebars.registerHelper('eachProperty', function(context, options) {
    var ret = "";
    for(var prop in context)
    {
        ret = ret + options.fn({property:prop,value:context[prop]});
    }
    return ret;
});

Depois, use-o assim:

{{#eachProperty object}}
    {{property}}: {{value}}<br/>
{{/eachProperty }}
Ben
fonte
2
Parece bom, você precisa adicionar uma verificação hasOwnProperty dentro do loop para não iterar sobre as propriedades do protótipo?
Monkeyboy
Ótima solução @Ben. Caso alguém esteja tentando usar isso com o Ember, veja minha resposta abaixo para a solução para fazê-lo funcionar.
flynfish
27

EDIT: O guidão agora tem uma maneira interna de fazer isso; veja a resposta selecionada acima. Ao trabalhar com bigode comum, o seguinte ainda se aplica.

O bigode pode iterar sobre itens em uma matriz. Então, sugiro criar um objeto de dados separado, formatado de forma que o Bigode possa trabalhar:

var o = {
  bob : 'For sure',
  roger: 'Unknown',
  donkey: 'What an ass'
},
mustacheFormattedData = { 'people' : [] };

for (var prop in o){
  if (o.hasOwnProperty(prop)){
    mustacheFormattedData['people'].push({
      'key' : prop,
      'value' : o[prop]
     });
  }
}

Agora, seu modelo de bigode seria algo como:

{{#people}}
  {{key}} : {{value}}
{{/people}}

Confira a seção "Listas não vazias" aqui: https://github.com/janl/mustache.js

Amit
fonte
1
Acabei seguindo a sua sugestão, pois preciso passar por algumas propriedades adicionais de qualquer maneira. Obrigado pela ajuda!
Ben
Muito obrigado, sua ideia me salvou mais um dia de busca de alternativas. Esta linha é a chave mustacheFormattedData = {'people': []};
Matt
Como você faria isso com uma matriz de objetos "o"?
precisa saber é o seguinte
4

Esta é a resposta de @ Ben atualizada para uso com o Ember ... note que você deve usar Ember.getporque o contexto é passado como uma String.

Ember.Handlebars.registerHelper('eachProperty', function(context, options) {
  var ret = "";
  var newContext = Ember.get(this, context);
  for(var prop in newContext)
  {
    if (newContext.hasOwnProperty(prop)) {
      ret = ret + options.fn({property:prop,value:newContext[prop]});
    }
  }
  return ret;
});

Modelo:

{{#eachProperty object}}
  {{key}}: {{value}}<br/>
{{/eachProperty }}
peixe voador
fonte
Obrigado @flynfish. contexto é uma string em Ember ?? isso parece .. um pouco estranho.
Ben
Sim, eu não tenho muita certeza, pois sou novo em Ember e ainda estou tentando descobrir o caminho.
flynfish
1

@ A resposta de Amit é boa porque funcionará tanto no bigode quanto no guidão.

Quanto às soluções exclusivas do guidão, já vi algumas e gosto do each_with_keyauxiliar de bloco em https://gist.github.com/1371586 da melhor maneira.

  • Ele permite iterar sobre literais de objetos sem precisar reestruturá-los primeiro e
  • Isso lhe dá controle sobre o que você chama de variável-chave. Com muitas outras soluções, você precisa ter cuidado ao usar chaves de objeto nomeadas 'key', ou 'property', etc.
mjumbewu
fonte
Bom achado. Apenas um aviso para outros leitores: o ajudante "key_value" nesta essência tem um bug. Leia os comentários para saber como corrigi-lo.
sirentian
0

Obrigado pela solução de Ben, meu caso de uso para exibir apenas campos específicos em ordem

com objeto

Código:

    handlebars.registerHelper('eachToDisplayProperty', function(context, toDisplays, options) {
    var ret = "";
    var toDisplayKeyList = toDisplays.split(",");
    for(var i = 0; i < toDisplayKeyList.length; i++) {
        toDisplayKey = toDisplayKeyList[i];
        if(context[toDisplayKey]) {
            ret = ret + options.fn({
                property : toDisplayKey,
                value : context[toDisplayKey]
            });
        }

    }
    return ret;
});

Objeto de origem:

   { locationDesc:"abc", name:"ghi", description:"def", four:"you wont see this"}

Modelo:

{{#eachToDisplayProperty this "locationDesc,description,name"}}
    <div>
        {{property}} --- {{value}}
    </div>
    {{/eachToDisplayProperty}}

Resultado:

locationDesc --- abc
description --- def
name --- ghi
vincentlcy
fonte
0

Essa é uma função auxiliar do mustacheJS, sem pré-formatar os dados e obtê-los durante a renderização.

var data = {
    valueFromMap: function() {
        return function(text, render) {
            // "this" will be an object with map key property
            // text will be color that we have between the mustache-tags
            // in the template
            // render is the function that mustache gives us

            // still need to loop since we have no idea what the key is
            // but there will only be one
            for ( var key in this) {
                if (this.hasOwnProperty(key)) {
                    return render(this[key][text]);
                }
            }
        };
    },

    list: {
        blueHorse: {
            color: 'blue'
        },

        redHorse: {
            color: 'red'
        }
    }
};

Modelo:

{{#list}}
    {{#.}}<span>color: {{#valueFromMap}}color{{/valueFromMap}}</span> <br/>{{/.}}
{{/list}}

Saídas:

color: blue
color: red

(a ordem pode ser aleatória - é um mapa) Isso pode ser útil se você souber o elemento do mapa que deseja. Cuidado com os valores falsos.

Cuel
fonte
-1

Eu estava usando a versão antiga 1.0.beta.6do guidão, acho que em algum lugar durante 1.1 - 1.3 essa funcionalidade foi adicionada, então a atualização para a 1.3.0 resolveu o problema, aqui está o uso:

Uso:

{{#each object}}
  Key {{@key}} : Value {{this}}
{{/people}}
AamirR
fonte