Mustache.js: Accéder à la portée parent

Créé le 11 déc. 2014  ·  18Commentaires  ·  Source: janl/mustache.js

Accéder à la portée parent

Considérant :

node = {
  id: 1,
  children : [
      { id : 2 },
      { id : 3 }
  ]
}

Et modèle suivant :

{{ id }} {# will output node.id #}
{{#children}}
    {{children.id}}  {# will output node.children[i].id #}
    {{id}}  {# will also output node.children[i].id #}
{{/children}}

En l'état, vous devez parfois accéder à la propriété parent (dans un modèle de nœud imbriqué par exemple).
Il peut être facilement implémenté comme utiliser "../" pour obtenir la portée parent

Ex :

{{ id }} {# will output node.id #}
{{#children}}
    {{children.id}}  {# will output node.children[i].id #}
    {{id}}  {# will also output node.children[i].id #}
    {{ ../id }}  {# will output node.id #}
{{/children}}

Pour y parvenir :

  Context.prototype.lookup = function (name) {
    var cache = this.cache;

    var value;
    if (name in cache) {
      console.log(name + ' found');
      value = cache[name];
    } else {
      var context = this, names, index;

      while (context) {
        if (name.indexOf('.') > 0) {
          value = context.view;
          names = name.split('.');
          index = 0;

          while (value != null && index < names.length)
            value = value[names[index++]];
        } else if(name.match(/^\.\.\//)) {
          name = name.replace(/^\.\.\//, '');
        } else {
          value = context.view[name];
        }

        if (value != null)
          break;

        context = context.parent;
      }

      cache[name] = value;
    }

    if (isFunction(value))
      value = value.call(this.view);

    return value;
  };
Future Plugin

Commentaire le plus utile

À droite. J'ai complètement oublié le guidon.

Permet de perdre les utilisateurs au guidon.
C'est une décision de conception acceptable.

Tous les 18 commentaires

Ce n'est pas dans les spécifications de mustache , n'est-ce pas ? Il n'y a pas de solution de contournement pour cela? Je suis surpris que personne n'ait rencontré cette limitation auparavant.

Je conviens qu'il est souvent nécessaire d'accéder aux éléments de la portée parent. Mon approche pragmatique a toujours été d'éviter les noms de propriétés ambigus. Cela a fonctionné pendant longtemps pour moi, même si cela aboutit souvent à des objets étranges.

D'autre part, en parlant d'expérience avec les guidons ; avoir cette capacité pourrait inciter les gens à créer une résolution de portée parent emmêlée folle : {{../../../id}} est beaucoup plus difficile à saisir que {{movieId}} .

Il est vrai que l'utilisation de "../" peut entraîner une illisibilité. Mais dans l'autre sens, l'utilisation de caml pour résoudre le parent ne peut pas être réalisée car vous pouvez avoir caml dans une portée locale. De plus, définir un mot-clé pour accéder au parent ne respecterait pas la philosophie Moustache je pense.

pour parler franchement, je ne pouvais pas trouver une idée plus simple et meilleure que d'utiliser la représentation du répertoire.

Oui, éviter les noms de propriété ambigus conduit également à des modèles plus lisibles/verbeux. Mais je comprends pourquoi certaines personnes aimeraient cette fonctionnalité.

Que diriez-vous d'écrire un package séparé qui modifie le fonctionnement interne de mustache.js pour ajouter la fonctionnalité souhaitée ? Un peu comme un plugin.

Pour être honnête, je ne vois pas vraiment cela se produire sauf via un plugin ou un pragma. Et une API de plugin ne semble pas être la priorité pour le moment.

J'y ai réfléchi un peu plus...

Je crois que la philosophie de Moustache n'est pas de transmettre les données telles quelles au moteur de rendu, mais de les « préparer » dans une vue à l'avance. Vous auriez alors une propriété parentId dans vos nœuds.

Il est également plus facile de lire et de gérer les modèles qui ont des variables plus détaillées :

{{ id }}
{{#children}}
    {{children.id}}
    {{id}}
{{/children}}

Avant vs Après

{{ nodeId }}
{{#children}}
    {{ nodeId }}
    {{ parentId }}
{{/children}}

Pertinent : http://stackoverflow.com/questions/4067093/mustache-read-variables-from-parent-section-in-child-section

(désolé de vous appeler @bobthecow , mais j'apprécie toujours votre sagesse en matière de moustache :sourire :)

vous pouvez déjà monter, donc tout ce qu'il faut faire pour résoudre le problème montré avec le premier modèle est d'envelopper les données avec un objet temporaire {node: ... } , envelopper le modèle avec {{#node}}...{{/node}} puis {{node.id}} partie peut fonctionner. vous n'avez pas besoin (et n'aurez pas) de muter les données existantes de cette façon, et vous pouvez ajouter ces deux "JIT" en tant que to_html() le modèle ...

ressemble beaucoup à une solution de contournement inégale. Il ne fonctionne également qu'avec un modèle simpliste à 2 niveaux où vous pouvez résoudre par une enveloppe d'enveloppe pour la couche externe. Mais que faire si le modèle est plus profond ? ça donne l'impression de jongler.

Souvent, j'ai besoin de lier un modèle que je reçois des couches de données inférieures, et mon travail consiste à le présenter - sous la forme que je l'ai obtenue de l'infra. La proposition ici est que je dois convertir le modèle de manière récursive jusqu'à un état présentable - ce qui serait considéré ici redable property names .
Cela ne correspond pas commodément à la réalité...
Oui, c'est vrai, il couple le gabarit avec le modèle. Mais pouvez-vous me montrer un modèle qui n'est pas couplé avec un modèle de règles concrètes ? Tous les modèles sont par définition conçus pour rendre un modèle défini.
Une autre couche recule la couche de définition et nécessite une couche de traduction - ce qui est fastidieux et pas toujours nécessaire

IMHO, je pense que l'outil devrait laisser le choix à l'utilisateur, plutôt que d'imposer des règles opiniâtres

Pour les lecteurs, exemple de ce que suggère @rndme :

const Mustache = require('mustache')

var view = {
  node: {
    id: 5,
    children: [ { id: 6 }, { id: 7 } ]
  }
}

const template = `
{{#node}}
  children:
  {{#children}}

    id: {{ id }}
    parentId: {{ node.id }}
  {{/children}}
{{/node}}
`

const output = Mustache.render(template, view)
console.log(output)

  children:

    id: 6
    parentId: 5

    id: 7
    parentId: 5

L'utilisation du modèle suivant ne fonctionne pas à partir de latest , mais _il devrait fonctionner_, imo.

const template = `
  children:
  {{#node.children}}

    id: {{ id }}
    parentId: {{ node.id }}
  {{/node.children}}
`

Oui, c'est vrai, il couple le gabarit avec le modèle. Mais pouvez-vous me montrer un modèle qui n'est pas couplé avec un modèle de règles concrètes ? Tous les modèles sont par définition conçus pour rendre un modèle défini.
Une autre couche recule la couche de définition et nécessite une couche de traduction - ce qui est fastidieux et pas toujours nécessaire

Afaik, la philosophie de Moustache a toujours été que vous générez une vue qui est transmise au modèle - vous ne transmettez pas le modèle directement.

@osher Pouvez-vous nous montrer un exemple que vous ne pouvez pas facilement comprendre avec l'astuce/l'astuce mentionnée ci-dessus ?

J'essaierai de fournir des extraits plus tard, mais en gros - avec 3 niveaux d'imbrication, cela ne fonctionnera pas car vous ne pouvez pas envelopper le niveau intermédiaire - vous devez transmuter la source en une vue traitée.
Vous pourrez accéder au niveau supérieur encapsulé, mais vous n'avez pas de solution pour le niveau intermédiaire.

Prenez par exemple un document swagger, où vous avez le niveau racine, le niveau chemin, le niveau verbe (et il y a plus mais arrêtons-nous ici). Chaque niveau peut spécifier une directive personnalisée - x-uses , qui est une directive DI pour la couche d'implémentation.

Supposons que vous souhaitiez générer des documents HTML à partir de ce document swagger.
Vous avez besoin d'un tableau plat spécifiant pour chaque gestionnaire d'opérations (le niveau de verbe) le DI qu'il accepte et de quelle couche il l'hérite.
Bien que toutes les informations soient inhérentes au document swagger, vous avez maintenant un problème.

Suivant.
Essayez d'utiliser moustache pour générer le code qui implémente l'API décrite dans le document qui renvoie des réponses fictives en fonction de la réponse par défaut de l'opération.
Essayez de générer des doclets qui décrivent ce à quoi le développeur d'implémentation qui vient remplacer la réponse fictive par une logique réelle doit s'attendre dans son contexte DI et précisez le niveau auquel il l'obtient.
Même...

Pas de génération HTML classique - oui. mais qui a dit que la moustache était réservée au HTML ? c'est un moteur de template, et la génération de code est couramment implémentée à l'aide de tels moteurs de template ;)

vous ne passez pas le modèle directement.

cela devrait être le choix d'un utilisateur, pas une limitation/restriction

Je vais vous donner un autre exemple, et je vais essayer de le faire sans trahir la sauce secrète.

Supposons une structure de données arborescente décrivant les actifs détenus par un joueur dans un jeu de stratégie.
L'arbre peut aller sur 5 niveaux environ, par exemple :
Aliance -> Empire -> City -> Army -> Troops

Chaque niveau peut fournir un bonus modificateur - par exemple - ou un bonus d'attaque, un bonus de défense, un bonus de santé, etc.
Les modificateurs qui traitent des mêmes statistiques sont décrits par le même nom à tous les niveaux (principalement parce qu'ils sont calculés de manière récursive).
Vous devez utiliser le moteur de modèle pour présenter un simulateur de combat qui devrait aider les joueurs à choisir quelle armée est l'armée idéale pour un défi donné en affichant les statistiques de combat des troupes de l'armée - qui résident aux niveaux les plus bas, mais collectent la bataille modificateurs nommés par le même nom d'attribut dans l'arborescence.
C'est _très_ simplifié, mais basé sur une histoire vraie où d'autres outils ont résolu le problème avec une grande facilité, sans nécessiter une couche de traduction intermédiaire.

J'ajouterai une difficulté : parfois, les armées sont affectées à des groupes de travail au niveau de l'Alliance.
Alliance -> Rally -> Troops
L'outil doit être suffisamment générique (simple récursif) et ne pas dépendre de niveaux concrets.

Je l'ai résolu avec ce que la moustache appellera partiels, seulement que je n'ai pas utilisé de moustache...

@osher a dit :

cela devrait être le choix d'un utilisateur, pas une limitation/restriction

Il ne fait aucun doute que la moustache a des opinions. Sa philosophie "logique sans modèles" impose de nombreuses restrictions sur les modèles, ce fait nécessite souvent une préparation des données/modèles avant de les donner au modèle pour le rendu. Si cela ne répond pas à vos besoins, il existe des alternatives qui peuvent être meilleures, telles que le guidon ou même lodash.template pour n'en nommer que quelques-uns

À droite. J'ai complètement oublié le guidon.

Permet de perdre les utilisateurs au guidon.
C'est une décision de conception acceptable.

Je suis presque sûr que @osher était sarcastique lorsqu'il a dit "C'est une décision de conception acceptable.", mais ce sujet a été abandonné depuis 2016. Que se passe-t-il ? Il semble que cette question soit évitée dans ce référentiel JavaScript ainsi que dans le référentiel principal :
https://github.com/mustache/mustache.github.com/issues/103

Pour ma part, je pense que pouvoir référencer la portée parent est sans logique et ne devrait pas interférer avec les idéaux de la moustache.

J'aime l'idée de toujours pouvoir accéder à la portée racine avec un symbole, comme un "../" de début :

Mustache.render('{{a1}}{{#a}}{{b.c}}{{../a1}}{{/a}}',{"a":{"b":{"c":"x1"}},"a1":"x2"})
"x2x1"

Je voudrais que cela rende "x2x1x2" mais il omet le dernier parce que ce n'est pas comme ça que cela fonctionne.
J'ai pensé que je recommanderais d'utiliser quelque chose comme JSONPath : https://goessner.net/articles/JsonPath/index.html#e2 mais contrairement à XPath pour XML, il ne recommande/implémente pas l'opérateur parent, ce que j'espérais pour.

Peut-être que Moustache pourrait simplement essayer de rester compatible avec les guidons et utiliser la syntaxe ../ pour le contexte parent ?

AFAIK, le guidon est uniquement en JS, tandis que la moustache utilise la même syntaxe dans de nombreux environnements, PHP par exemple. Si vous souhaitez modifier la syntaxe de la moustache, vous devrez convaincre toutes les autres implémentations non js de faire de même ; Une grande commande. De plus, en modifiant le code JS, j'ai trouvé problématique de monter "d'un niveau", bien que j'aie ajouté un moyen de revenir à la racine dans mon fork , ce qui était assez simple à implémenter ...

Rien à ce sujet ?

Il doit également y avoir une documentation et une recommandation de style pour les situations où la notation par points est facultative.

view = { wrap: { txt: "test" } };
{{#wrap}}
  {{wrap.txt}} {{! Should I use this?}}
  {{txt}} {{! Or this?}}
{{/wrap}}

Plus de détails ici : https://stackoverflow.com/q/62166467/5637701

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

Immortalin picture Immortalin  ·  12Commentaires

chlab picture chlab  ·  11Commentaires

barbalex picture barbalex  ·  5Commentaires

mbrodala picture mbrodala  ·  16Commentaires

rlightner picture rlightner  ·  7Commentaires