Handlebars.js: suggestion de fonctionnalité | Assistants synchrones/asynchrones

Créé le 23 janv. 2014  ·  24Commentaires  ·  Source: handlebars-lang/handlebars.js

En enregistrant un assistant en tant que Sync ou Async, il est utile d'écouter les rappels et d'obtenir les données du rappel.

Commentaire le plus utile

je vais essayer express-hbs - mais je pense qu'en 2018, c'est une chose étrange à ne pas prendre en charge. Je sais qu'il y a une vision puriste selon laquelle les choses asynchrones ne devraient pas être faites dans le cadre de la vue mais plutôt dans une chose magique appelée contrôleur (comme si MVC était incontestablement "correct") - mais c'est un peu myope pour deux clé les raisons

a) bibliothèques externes - la plupart des gens écrivent maintenant entièrement avec async/wait - je pense que dans mon code, plus de 9 fonctions sur 10 sont asynchrones... dans certains cas "juste au cas où". Ne pas prendre en charge une fonction asynchrone signifie que toutes les bibliothèques asynchrones sont soudainement complètement inaccessibles

b) fonctions génériques du contrôleur. Je dirais que quelque chose comme ça:

    {{#query "select name, total from summary"}}
          <tr><td>{{this.name}}</td><td>{{this.total}}</td></tr>
    {{/query}}

est plus court, plus propre, plus facile à entretenir et fondamentalement supérieur de toutes les manières imaginables par rapport à une fonction de contrôleur sur mesure qui colle ces éléments dans une variable et les transmet à un modèle, que le modèle doit ensuite connaître et accéder.

Tous les 24 commentaires

Cela s'est produit dans le passé, mais nous n'avons pas agi car le cas d'utilisation n'était pas clair. Étant donné que le guidon doit encore attendre que toutes les données soient disponibles pour le rendu, fournir une évaluation asynchrone est simplement une commodité pour quelque chose que le code qui génère le contexte peut faire de manière beaucoup plus efficace.

Fondamentalement, l'ajout d'une évaluation asynchrone à ce stade serait assez coûteux à la fois en termes de compatibilité et de performances d'exécution pour lesquelles je ne vois pas vraiment de cas d'utilisation pour le moment. Avez-vous un exemple concret de ce que vous essayez de faire ?

Je suis d'accord avec les problèmes de performances, mais je pense que ce sera mieux si nous pouvons éventuellement le rendre aysnc, par exemple, RegisterHelper & RegisterHelperAsync, ou quelque chose comme ça.

En fait, j'ai pensé à ces guidons asynchrones, pendant que je travaillais avec node.js. Je travaille sur certaines applications en utilisant express.js et le moteur de modèle que j'utilise est le guidon. Donc, si j'ai besoin d'obtenir de la valeur à partir d'un appel de base de données, lors de la compilation de la vue, ce n'est pas possible avec ce travail synchrone,

Par example,

Handlebars.registerHelper('getDbValue', function(id) {
     var Model = require('./myModel.js');
     Model.getValue(id, function(data){
           return data;
     });
});

L'exemple ci-dessus ne fonctionnera pas et ne retournera rien. Voici mon concept. Et, je ne sais pas si c'est tout à fait juste ou si cela peut être mis en œuvre ou non. Il suffit d'utiliser une fonction de rappel au lieu de return, dans le cas d'une méthode async.

Handlebars.registerHelperAsync('getDbValue', function(id, callback) {
     var Model = require('./myModel.js');
     Model.getValue(id, function(data){
           callback(data);
           //or
           //callback(new Handlebars.SafeString(data)); //in case of safestring.
     });
});

Je rencontre plus de problèmes comme celui ci-dessus, et je peux montrer plus d'exemples selon mon scénario, si quelqu'un est intéressé par cette fonctionnalité.

Merci

@robincsamuel , Lors de l'inclusion de recherches de base de données dans la génération de vues, toute l'idée de la séparation MVC va à la fenêtre. Je pense que vous soutenez que vous ne savez peut-être pas que vous avez besoin des données jusqu'à ce que la vue soit rendue, mais pour moi, cela suggère une fonctionnalité qui devrait être implémentée au niveau du contrôleur plutôt que lors de la génération de votre vue. Combinés aux considérations de performances mentionnées par @kpdecker , les assistants asynchrones semblent tout simplement faux. -- mon 2c

J'ai juste utilisé cet exemple pour exprimer mon problème. Et je n'essaie pas de discuter, mais, juste fait une suggestion. J'espère que cela aidera si nous appelons une fonction avec rappel de l'assistant. Quoi qu'il en soit, merci pour votre temps :) @kpdecker @jwilm

À ce stade, la position du projet est que la résolution des données doit être effectuée avant d'appeler le modèle. En dehors de la logique métier dans ou en dehors des problèmes de modèle, la résolution asynchrone est davantage un comportement utilitaire que d'autres bibliothèques telles que async sont bien mieux adaptées à la gestion.

Je veux commenter quelque chose. Même si cela reviendrait à jeter par la fenêtre l'exemple de base de données, cela pourrait être très utile pour les modèles mutationnels. Par exemple, un modèle avec des "sous-vues" à l'intérieur, et que vous ne voulez pas diviser en plusieurs autres modèles. Je veux juste mettre à jour une partie de la vue, et avoir une logique simple pour cela, au lieu de repeindre toute ma vue (effet de scintillement) ou d'avoir mon contrôleur pour construire le tout pour toutes ces "mini vues"

Qu'en penses-tu?

@tomasdev je veux dire ça :)

Cette fonctionnalité est disponible dans le nœud avec express si vous utilisez https://github.com/barc/express-hbs. Cependant, la version asynchrone des assistants ne fonctionne pas bien avec les sous-expressions et quelques autres cas extrêmes.

J'aimerais voir cette fonctionnalité reconsidérée pour être incluse dans les guidons, ou au moins considérer comment le noyau du guidon peut mieux supporter ce type d'extension.

Je crois que Ghost démontre un cas d'utilisation clair et valide (bien que peut-être rare) pour les assistants asynchrones, car notre couche de vue est personnalisable.

Sur le frontend, tous les modèles de Ghost sont fournis par le thème. Le thème est une très fine couche de guidon, CSS et client JS, les seules données auxquelles il a accès sont celles que nous fournissons à l'avance. Il n'a pas accès à un contrôleur ou à une logique de changement de comportement. C'est très délibéré.

Afin d'étendre l'API du thème, nous souhaitons commencer à ajouter des assistants qui définissent des collections de données supplémentaires que le thème souhaite utiliser. Par exemple quelque chose comme :

{{#fetch tags}}
.. do something with the list of tags..
{{else}}
No tags available
{{/fetch}}

Ghost dispose d'une API JSON qui est disponible à la fois en interne et en externe. Ainsi, cette requête de récupération correspondrait à notre fonction de navigation dans les balises. Il n'est pas nécessaire d'utiliser ajax/http sur tous les points de terminaison, à la place, un assistant asynchrone peut récupérer ces données à partir de l'API en interne et continuer comme d'habitude.

Je ne prétends pas qu'il s'agit d'un cas d'utilisation courant, et j'accepte qu'il brise le modèle MVC standard, mais je pense qu'il est valide et utile.

@ErisDS Bonne nouvelle ! Et moi non plus, je ne dis pas que c'est un problème commun, mais cela aide.

Il convient de noter dans ce cas que de nombreuses opérations pour lesquelles nous utilisons actuellement des assistants asynchrones sont synchrones sous le capot, mais elles sont structurées comme des promesses.

Pour donner un exemple détaillé...

Toutes les données de Ghost sont accessibles via une API interne. Cela inclut des informations globales telles que les paramètres. Les requêtes adressées à l'API des paramètres atteignent un cache en mémoire pré-rempli avant d'atteindre la base de données. Nous ne renvoyons donc qu'une variable, mais en structurant cela comme une promesse, il est facile de passer à la base de données si nous en avons besoin.

Cela garantit également que tout est cohérent - sinon l'API des paramètres serait synchrone et toutes les autres demandes de données internes seraient asynchrones, ce qui n'aurait aucun sens.

Je sais que tout structurer avec des promesses peut être assez déroutant au début, mais c'est une de ces choses dont vous ne comprenez pas comment vous avez vécu sans une fois que vous l'avez. Avec les générateurs à venir dans ES6, la prise en charge de la résolution asynchrone des fonctions sera directement intégrée à JavaScript - et ce problème similaire : https://github.com/wycats/handlebars.js/issues/141 mentionne qu'il serait agréable de créer des guidons travailler avec rendement.

Je ne sais pas comment la prochaine version de HTMLbars pourrait avoir un impact sur cela, mais je pense que cela mérite au moins une discussion plus approfondie.

Couru dans un autre cas d'utilisation en essayant de créer une aide pour la résolution ACL. Cela s'intégrerait bien dans mes modèles:

        {{#allowedTo 'edit' '/config'}}
            <li>
                <a href="/config">Config</a>
            </li>
        {{/allowedTo}}

Mais la méthode isAllowed réelle de node-acl est asynchrone (autorisant un backend de base de données par exemple).

Une solution de contournement consiste à récupérer toutes les autorisations de l'utilisateur à l'avance ( allowPermissions ), mais c'est un peu irritant

@kpdecker D' autres réflexions sur ces cas d'utilisation ?

@ErisDS Je comprends le désir ici, mais je doute fortement que cela entrera jamais dans la langue sous forme de rappel ou de promesses. C'est quelque chose qui est très difficile à faire proprement du point de vue de l'API et nous oblige effectivement à réécrire de grandes parties du moteur de modèle pour le prendre en charge. Ma recommandation est que tout cela soit géré avant que le cycle de rendu ne soit entré par le modèle/la source de données en amont.

L'idée de rendement est intéressante, mais si quelqu'un voulait jeter un coup d'œil à ce qui serait nécessaire là-bas, ce serait un projet de recherche incroyable, mais la prise en charge du navigateur pour cela me semble très éloignée et honnêtement, je n'ai pas déconné avec aucune de ces fonctionnalités encore sur l'un de mes projets.

Juste mes "deux" (enfin, quelques) cents que vous voudrez peut-être considérer :

  • MVC n'est pas sacro-saint. Les choses ne sont pas fausses simplement parce qu'elles semblent contredire MVC. Il faut évaluer si les alternatives n'offrent pas d'avantages positifs nets par rapport au suivi strict de MVC.
  • Si la vue demande au contrôleur des données, pas des modèles directement, ce n'est pas une violation du MVC de toute façon, n'est-ce pas ?
  • On pourrait peut-être faire valoir que le fait que le contrôleur sache à l'avance tout ce dont la vue aura besoin est une duplication d'informations (c'est-à-dire que les informations "X, Y, Z, W sont nécessaires" sont dupliquées dans les vues et les contrôleurs.) En d'autres termes, notre courant la pratique peut être une violation du principe DRY, qui est beaucoup plus important que MVC, imo.
  • L'impact sur les performances des assistants asynchrones dans le but de charger uniquement les modèles nécessaires aux vues en cours de rendu pourrait facilement être compensé en chargeant moins de données à partir de la base de données.

Je pourrais être en mesure d'offrir un meilleur exemple où il serait utile d'avoir.

Nous travaillons beaucoup avec cordova pour les applications mobiles et avons besoin de localiser pour de nombreuses langues. Cordova propose des fonctions pour vous aider à formater les dates, les nombres, les devises, etc.
Le problème est qu'ils nécessitent tous un rappel asynchrone.

Exemple:

Handlebars.registerHelper('stringToNumber', function(string, type)
{
    type = type || 'decimal';
    navigator.globalization.stringToNumber(string, function(number)
    {
        return number;
    }, function()
    {
        return NaN;
    }, {
        type: type
    });
});

Ce serait génial d'avoir imo.

J'ai trouvé le package handlebars-async sur npm. Mais c'est un peu plus ancien et je ne sais pas si cela fonctionne avec la version actuelle du guidon.

Je viens aussi d'écrire quelque chose de similaire pour les promesses. Le paquet promise-handlebars vous permet de retourner des promesses depuis les assistants. Je prévois de l'utiliser dans l'un de mes projets, mais jusqu'à présent, il n'a pas été utilisé dans un environnement de production. Mais il existe des tests unitaires pour plusieurs cas extrêmes (tels que l'appel d'assistants asynchrones à partir d'assistants de bloc asynchrones) et ils sont tous verts ...

@nknapp ça a l'air incroyable ! express-hbs a un support asynchrone, et l'asynchrone fonctionne pour les aides de bloc, mais l'imbrication des aides asynchrones ne fonctionne pas - donc je suis vraiment intéressé de voir cela fonctionner - signifie qu'il y a encore de l'espoir pour express-hbs :+1 :

@ErisDS , pensez-vous que je devrais le poster là-bas. Je ne savais pas que express-hbs ne pouvait pas imbriquer l'assistant asynchrone. Mon objectif principal n'est pas express , mais un générateur README sur lequel je travaille actuellement. J'apprécierais vraiment que d'autres personnes l'essaient ( guidon promis ) donnent leur avis.

Pour ajouter aux cas d'utilisation valides, que se passe-t-il si vous devez extraire des valeurs d'une base de données de traduction basée sur les paramètres régionaux actuels ?

<div class="howItWorks">
    {{{i18nFetch id=how-it-works locale=locale}}}
</div>

De plus, qu'en est-il de l'ajout d'un bloc CMS à partir d'une entrée de base de données à l'aide d'un identifiant dynamique comme :

<div class="searchCms">
    {{{cmsLoader 'search-{$term}' term=params.input defaultId='search-default'}}}
</div>

Ceci est particulièrement utile pour le rendu côté serveur (c'est-à-dire en utilisant express-handlebars ).

Voici un autre cas d'utilisation : j'écris un générateur de documentation pour Swagger ( simple-swagger ), qui permet des définitions de schéma externes. J'aimerais écrire un assistant Handlebars qui reconnaît quand un schéma est défini en externe, accède à l'URL fournie où se trouve ce schéma, le récupère et utilise ces données pour restituer cette partie du modèle Handlebars. Si je devais récupérer ces données avant d'appeler la méthode de compilation de Handlebars, je devrais parcourir de manière récursive un document JSON dont je ne connais pas la structure à l'avance, trouver toutes les instances de schémas externes, les récupérer et les insérer dans le JSON.

Fondamentalement, chaque fois qu'un modèle Handlebars est utilisé pour restituer des données de schéma JSON ( json-schema.org ), une méthode de rendu asynchrone serait utile, car le schéma JSON permet toujours de définir des sous-parties d'un schéma en externe.

@dwhieb avez-vous jeté un coup d'œil à bootprint-swagger pour le générateur de documentation? C'est presque ce que vous décrivez (sauf que les schémas externes ne sont pas encore implémentés, mais ce serait une fonctionnalité intéressante). Si vous avez des commentaires, veuillez y ouvrir un sujet.

Et, je pense que le guidon promis fonctionne assez bien avec les assistants asynchrones.

J'ai un cas d'utilisation où il serait utile de pouvoir utiliser des promesses dans des assistants. J'utilise des guidons pour générer le code HTML de mon blog. Afin de créer des données structurées valides pour chaque article, j'ai besoin d'obtenir les dimensions que j'utilise pour l'image de l'article. En ce moment, je procède comme ceci :

{{#imageSize post.frontMatter.previewImage}}
  <div itemprop="image" itemscope itemtype="https://schema.org/ImageObject">
    <meta itemprop="url" content="{{#staticResource ../post.frontMatter.previewImage}}{{/staticResource}}">
    <meta itemprop="width" content="{{width}}">
    <meta itemprop="height" content="{{height}}">
  </div>
{{/imageSize}}

L'assistant imageSize fonctionne car il lit le fichier de manière synchrone, mais idéalement, il devrait pouvoir le faire de manière asynchrone afin que le rendu des pages ne soit pas ralenti par les E/S. De plus, faire cela pour une image sur une URL, plutôt que sur le système de fichiers, est impossible.

J'examinerai l'utilisation de promise-handlebars et express-hbs, mais je pense que la possibilité d'utiliser des promesses dans les fonctions d'assistance serait un excellent ajout à Handlebars !

FWIW, j'ai fait beaucoup de rendu HTML asynchrone en utilisant Hyperscript, hyperscript-helpers , ES7's async/await , et ça a été une vraie joie. Mais bien sûr, cette solution ne fonctionne que pour HTML. Une solution asynchrone avec Handlebars nous permettrait de générer d'autres types de fichiers de manière asynchrone... Cependant, pour HTML, je pense que je ne regarde jamais en arrière !

je vais essayer express-hbs - mais je pense qu'en 2018, c'est une chose étrange à ne pas prendre en charge. Je sais qu'il y a une vision puriste selon laquelle les choses asynchrones ne devraient pas être faites dans le cadre de la vue mais plutôt dans une chose magique appelée contrôleur (comme si MVC était incontestablement "correct") - mais c'est un peu myope pour deux clé les raisons

a) bibliothèques externes - la plupart des gens écrivent maintenant entièrement avec async/wait - je pense que dans mon code, plus de 9 fonctions sur 10 sont asynchrones... dans certains cas "juste au cas où". Ne pas prendre en charge une fonction asynchrone signifie que toutes les bibliothèques asynchrones sont soudainement complètement inaccessibles

b) fonctions génériques du contrôleur. Je dirais que quelque chose comme ça:

    {{#query "select name, total from summary"}}
          <tr><td>{{this.name}}</td><td>{{this.total}}</td></tr>
    {{/query}}

est plus court, plus propre, plus facile à entretenir et fondamentalement supérieur de toutes les manières imaginables par rapport à une fonction de contrôleur sur mesure qui colle ces éléments dans une variable et les transmet à un modèle, que le modèle doit ensuite connaître et accéder.

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

Questions connexes

nknapp picture nknapp  ·  3Commentaires

rizen picture rizen  ·  6Commentaires

janus-reith picture janus-reith  ·  3Commentaires

snimavat picture snimavat  ·  5Commentaires

morgondag picture morgondag  ·  5Commentaires