Handlebars.js: Funktionsvorschlag | Synchrone/asynchrone Helfer

Erstellt am 23. Jan. 2014  ·  24Kommentare  ·  Quelle: handlebars-lang/handlebars.js

Das Registrieren eines Helfers als Sync oder Async hilft, auf Rückrufe zu hören und die Daten von Rückrufen abzurufen.

Hilfreichster Kommentar

Ich werde Express-HBS ausprobieren - aber ich denke, dass es 2018 eine seltsame Sache ist, es nicht zu unterstützen. Ich weiß, dass es eine puristische Ansicht gibt, dass asynchrone Dinge nicht als Teil der Ansicht, sondern in einem magischen Ding namens Controller ausgeführt werden sollten (als ob MVC irgendwie unbestreitbar "korrekt" wäre) - aber das ist für zwei Schlüssel etwas kurzsichtig Gründe dafür

a) externe Bibliotheken - die meisten Leute schreiben jetzt komplett mit async/await - ich denke, in meinem Code sind mehr als 9 von 10 Funktionen asynchron ... in einigen Fällen "nur für den Fall". Das Fehlen einer asynchronen Funktion bedeutet, dass alle asynchronen Bibliotheken plötzlich vollständig unzugänglich sind

b) generische Controller-Funktionen. Ich würde behaupten, dass so etwas:

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

ist kürzer, sauberer, einfacher zu warten und im Grunde in jeder erdenklichen Weise überlegen im Vergleich zu einer maßgeschneiderten Controller-Funktion, die das Zeug in eine Variable steckt und es in eine Vorlage übergibt, die die Vorlage dann kennen und auf die sie zugreifen muss.

Alle 24 Kommentare

Dies ist in der Vergangenheit aufgetreten, aber wir haben nicht darauf reagiert, da der Anwendungsfall nicht klar war. Da Handlebars immer noch warten muss, bis alle Daten zum Rendern verfügbar sind, ist die Bereitstellung einer asynchronen Auswertung einfach eine Annehmlichkeit für etwas, das der Code, der den Kontext generiert, viel effizienter ausführen kann.

Grundsätzlich wäre das Hinzufügen einer asynchronen Auswertung an dieser Stelle sowohl in Bezug auf die Kompatibilität als auch auf die Laufzeitleistung ziemlich teuer, für die ich derzeit keinen Anwendungsfall sehe. Hast du ein konkretes Beispiel dafür, was du zu tun versuchst?

Ich stimme den Leistungsproblemen zu, aber ich denke, es wird besser, wenn wir es optional aysnc machen können, zum Beispiel RegisterHelper & RegisterHelperAsync oder so etwas.

Eigentlich habe ich über diese asynchronen Lenker nachgedacht, während ich mit node.js arbeite. Ich arbeite an einigen Anwendungen mit express.js und die Template-Engine, die ich verwende, ist Lenker. Wenn ich also beim Kompilieren der Ansicht einen Wert aus einem Datenbankaufruf abrufen muss, ist dies mit dieser synchronen Arbeitsweise nicht möglich.

Beispielsweise,

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

Das obige Beispiel funktioniert nicht und gibt nichts zurück. Folgendes ist mein Konzept. Und ich weiß nicht, ob es ganz richtig ist oder ob es umgesetzt werden kann oder nicht. Verwenden Sie einfach eine Callback-Funktion anstelle von return, im Falle einer asynchronen Methode.

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.
     });
});

Ich habe mehr Probleme wie das obige und kann mehr Beispiele für mein Szenario zeigen, falls jemand an dieser Funktion interessiert ist.

Danke

@robincsamuel , Wenn Sie Datenbanksuchen in die Ansichtsgenerierung einbeziehen, geht die ganze Idee der MVC-Trennung am Fenster vorbei. Ich denke, Sie argumentieren, dass Sie möglicherweise nicht wissen, dass Sie die Daten benötigen, bis die Ansicht gerendert wird, aber für mich schlägt dies eine Funktionalität vor, die eher auf Controllerebene als beim Generieren Ihrer Ansicht implementiert werden sollte. In Kombination mit den von @kpdecker erwähnten Leistungsüberlegungen scheinen asynchrone Helfer einfach falsch zu sein. -- mein 2c

Ich habe dieses Beispiel nur verwendet, um mein Problem zu vermitteln. Und ich versuche nicht zu argumentieren, sondern habe nur einen Vorschlag gemacht. Ich hoffe, es hilft, wenn wir eine Funktion mit Callback vom Helfer aufrufen. Wie auch immer, danke für deine Zeit :) @kpdecker @jwilm

An diesem Punkt ist die Haltung des Projekts, dass die Datenauflösung vor dem Aufrufen der Vorlage erfolgen sollte. Außerhalb der Geschäftslogik innerhalb oder außerhalb der Vorlagen betrifft die asynchrone Auflösung eher ein nützliches Verhalten, für das andere Bibliotheken wie async viel besser geeignet sind.

Ich möchte etwas kommentieren. Auch wenn es das DB-Beispiel aus dem Fenster werfen würde, könnte dies für Mutationsvorlagen wirklich hilfreich sein. Beispielsweise eine Vorlage mit „Unteransichten“ darin, die Sie nicht in mehrere andere Vorlagen aufteilen möchten. Ich möchte nur einen Teil in der Ansicht aktualisieren und dafür eine einfache Logik haben, anstatt entweder meine gesamte Ansicht neu zu zeichnen (Flackereffekt) oder meinen Controller das Ganze für all diese "Miniansichten" erstellen zu lassen.

Was denkst du?

@tomasdev das meine ich :)

Diese Funktion wird im Knoten mit Express verfügbar gemacht, wenn Sie https://github.com/barc/express-hbs verwenden. Die asynchrone Version von Helfern spielt jedoch nicht gut mit Unterausdrücken und einigen anderen Grenzfällen.

Ich würde gerne sehen, dass diese Funktion für die Aufnahme in Lenker überdacht wird, oder zumindest darüber nachdenke, wie der Lenkerkern diese Art von Verlängerung besser unterstützen kann.

Ich glaube, Ghost demonstriert einen klaren und gültigen (wenn auch vielleicht ungewöhnlichen) Anwendungsfall für asynchrone Helfer, da unsere Ansichtsebene anpassbar ist.

Auf dem Frontend werden alle Templates von Ghost vom Theme bereitgestellt. Das Theme ist eine sehr dünne Schicht aus Handlebars, CSS und Client-JS, die einzigen Daten, auf die es Zugriff hat, sind die, die wir im Voraus bereitstellen. Es hat keinen Zugriff auf einen Controller oder eine Verhaltensänderungslogik. Das ist sehr gewollt.

Um die Theme-API zu erweitern, möchten wir mit dem Hinzufügen von Helfern beginnen, die zusätzliche Datensammlungen definieren, die das Theme verwenden möchte. Zum Beispiel so etwas wie:

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

Ghost verfügt über eine JSON-API, die sowohl intern als auch extern verfügbar ist. Diese Abrufabfrage würde also unserer Browse-Tags-Funktion zugeordnet werden. Es muss nicht ajax/http für alle Endpunkte verwendet werden, stattdessen kann ein asynchroner Helfer diese Daten intern von der API abrufen und wie gewohnt fortfahren.

Ich behaupte nicht, dass dies ein häufiger Anwendungsfall ist, und ich akzeptiere, dass es das Standard-MVC-Modell verletzt, aber ich glaube, dass es gültig und nützlich ist.

@ErisDS Tolle Neuigkeiten! Und ich behaupte auch nicht, dass es ein gemeinsames Problem ist, aber es hilft.

In diesem Fall ist es erwähnenswert, dass viele der Operationen, für die wir derzeit asynchrone Helfer verwenden, unter der Haube synchron sind, aber als Versprechen strukturiert sind.

Um ein detailliertes Beispiel zu geben ...

Auf alle Daten in Ghost wird über eine interne API zugegriffen. Dazu gehören globale Informationen wie Einstellungen. Anfragen an die Einstellungs-API treffen auf einen vorbelegten In-Memory-Cache, bevor sie auf die Datenbank treffen, also geben wir eigentlich nur eine Variable zurück, aber indem wir dies als Versprechen strukturieren, ist es einfach, bei Bedarf zur Datenbank zu gehen.

Es stellt auch sicher, dass alles konsistent ist – andernfalls wäre die Einstellungs-API synchron und alle anderen internen Datenanforderungen asynchron, was keinen Sinn machen würde.

Ich weiß, dass es anfangs ziemlich verwirrend sein kann, alles mit Versprechungen zu strukturieren, aber es ist eines dieser Dinge, von denen man nicht versteht, wie man ohne sie gelebt hat, wenn man sie erst einmal hat. Mit Generatoren, die in ES6 kommen, wird die Unterstützung für die asynchrone Auflösung von Funktionen direkt in JavaScript integriert – und dieses ähnliche Problem: https://github.com/wycats/handlebars.js/issues/141 erwähnt, dass es schön wäre, Lenker zu machen Arbeit mit Ertrag.

Ich bin mir nicht sicher, wie sich die kommende Version von HTMLbars darauf auswirken könnte, aber ich denke, dass es zumindest eine weitere Diskussion rechtfertigt.

Beim Versuch, einen Helfer für die ACL-Auflösung zu erstellen, stieß ich auf einen anderen Anwendungsfall. Das würde gut in meine Vorlagen passen:

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

Die eigentliche isAllowed -Methode von node-acl ist jedoch asynchron (und ermöglicht beispielsweise ein Datenbank-Backend).

Eine Problemumgehung besteht darin, alle Benutzerberechtigungen vorher abzurufen ( allowPermissions ), aber das ist etwas juckend

@kpdecker Irgendwelche weiteren Gedanken zu diesen Anwendungsfällen?

@ErisDS Ich verstehe den Wunsch hier, aber ich bezweifle stark, dass dies jemals in Rückruf- oder Versprechungsform in die Sprache gelangen wird. Dies ist aus API-Perspektive sehr schwer sauber zu bewerkstelligen und erfordert effektiv, dass wir große Teile der Vorlagen-Engine neu schreiben, um dies zu unterstützen. Meine Empfehlung ist, dass all dies behandelt wird, bevor der Rendering-Zyklus von der vorgelagerten Modell-/Datenquelle eingegeben wird.

Die Yield-Idee ist interessant, aber wenn jemand einen Blick darauf werfen wollte, was dort erforderlich wäre, wäre das ein erstaunliches Forschungsprojekt, aber die Browserunterstützung dafür scheint mir sehr weit entfernt zu sein, und ich habe mich ehrlich gesagt nicht damit beschäftigt keine dieser Funktionen noch in einem meiner Projekte.

Nur meine "zwei" (naja, paar) Cent, die Sie vielleicht in Betracht ziehen sollten:

  • MVC ist nicht sakrosankt. Die Dinge sind nicht falsch, nur weil sie MVC zu widersprechen scheinen. Man muss abwägen, ob die Alternativen keine positiven Nettovorteile gegenüber der strikten Befolgung von MVC bieten.
  • Wenn die Ansicht den Controller nach Daten fragt, nicht direkt nach Modellen, ist das sowieso kein Verstoß gegen die MVC, oder?
  • Man könnte vielleicht argumentieren, dass es eine Duplizierung von Informationen ist, wenn der Controller alles weiß, was die Ansicht benötigt (dh die Informationen „X, Y, Z, W sind erforderlich“ werden in Ansichten und Controllern dupliziert). Mit anderen Worten, unser Strom Praxis kann eine Verletzung des DRY-Prinzips sein, das meiner Meinung nach viel wichtiger ist als MVC.
  • Der Leistungseinbruch von asynchronen Helfern zum Zweck des Ladens nur der Modelle, die für die gerenderten Ansichten erforderlich sind, kann leicht durch das Laden von weniger Daten aus der Datenbank kompensiert werden.

Ich könnte vielleicht ein besseres Beispiel bieten, wo es nützlich wäre, es zu haben.

Wir arbeiten viel mit Cordova für mobile Anwendungen und müssen für viele Sprachen lokalisieren. Cordova bietet Funktionen, die beim Formatieren von Datumsangaben, Zahlen, Währungen usw. helfen.
Das Problem ist, dass sie alle einen asynchronen Rückruf erfordern.

Beispiel:

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

Das wäre toll, imo zu haben.

Ich habe das handlebars-async- Paket auf npm gefunden. Aber es ist etwas älter und ich weiß nicht, ob es mit der aktuellen Handlebars-Version funktioniert.

Ich habe auch gerade etwas Ähnliches für Versprechungen geschrieben. Das Paket versprochen-Lenker ermöglicht es Ihnen, Versprechungen von Helfern zurückzugeben. Ich habe vor, es in einem meiner Projekte zu verwenden, aber bisher wurde es nicht in einer Produktionsumgebung verwendet. Aber es gibt Komponententests für einige Randfälle (z. B. das Aufrufen von asynchronen Helfern aus asynchronen Blockhelfern) und sie sind alle grün ...

@nknapp das klingt toll! express-hbs hat Async-Unterstützung, und das Async funktioniert für Blockhelfer, aber das Verschachteln von asynchronen Helfern funktioniert nicht - daher bin ich wirklich daran interessiert, dass dies funktioniert - bedeutet, dass es noch Hoffnung für express-hbs gibt :+1:

@ErisDS , denkst du, ich sollte es dort posten? Ich wusste jetzt nicht, dass express-hbs keinen asynchronen Helfer verschachteln konnte. Mein Hauptaugenmerk liegt nicht express , sondern auf einem README-Generator, an dem ich gerade arbeite. Ich würde mich sehr freuen, wenn andere Leute, die es versuchen ( versprochener Lenker ), Feedback geben.

Was ist, um die gültigen Anwendungsfälle zu ergänzen, was ist, wenn Sie Werte aus einer Übersetzungsdatenbank basierend auf dem aktuellen Gebietsschema abrufen müssen?

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

Was ist außerdem mit dem Hinzufügen eines CMS-Blocks aus einem DB-Eintrag mit einer dynamischen ID wie:

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

Dies ist besonders nützlich für das serverseitige Rendern (dh die Verwendung von express-handlebars ).

Hier ist ein weiterer Anwendungsfall: Ich schreibe einen Dokumentationsgenerator für Swagger ( simple-swagger ), der externe Schemadefinitionen zulässt. Ich möchte einen Handlebars-Helfer schreiben, der erkennt, wenn ein Schema extern definiert wird, zu der bereitgestellten URL geht, in der sich dieses Schema befindet, es abruft und diese Daten verwendet, um diesen Teil der Handlebars-Vorlage zu rendern. Wenn ich diese Daten abrufen müsste, bevor ich die Kompiliermethode von Handlebars aufrufe, müsste ich rekursiv durch ein JSON-Dokument iterieren, dessen Struktur ich nicht im Voraus kenne, alle Instanzen externer Schemas finden, sie abrufen und in die JSON.

Grundsätzlich wäre immer dann, wenn eine Handlebars-Vorlage zum Rendern von JSON-Schemadaten ( json-schema.org ) verwendet wird, eine asynchrone Rendermethode nützlich, da das JSON-Schema immer die externe Definition von Unterteilen eines Schemas zulässt.

@dwhieb hast du dir bootprint-swagger für den Dokumentationsgenerator angesehen? Es ist fast das, was Sie beschreiben (außer dass externe Schemas noch nicht implementiert sind, aber das wäre eine großartige Funktion). Wenn Sie Feedback haben, öffnen Sie dort bitte ein Problem.

Und ich denke, dass versprochene Lenker recht gut mit asynchronen Helfern funktionieren.

Ich habe einen Anwendungsfall, in dem es nützlich wäre, Versprechungen in Helfern verwenden zu können. Ich verwende Handlebars, um den HTML-Code für mein Blog zu generieren. Um gültige strukturierte Daten für jeden Artikel zu erstellen, muss ich die Abmessungen erhalten, die ich für das Artikelbild verwende. Momentan mache ich es so:

{{#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}}

Der Helfer imageSize funktioniert, weil er die Datei synchron liest, aber idealerweise sollte er in der Lage sein, dies asynchron zu tun, damit das Rendern von Seiten nicht durch I/O verlangsamt wird. Außerdem ist es unmöglich, dies für ein Bild unter einer URL statt im Dateisystem zu tun.

Ich werde mich mit der Verwendung von Promised-Lenkers und Express-HBS befassen, aber ich denke, die Möglichkeit, Promises in Hilfsfunktionen zu verwenden, wäre eine großartige Ergänzung zu Handlebars!

FWIW, ich habe viel asynchrones HTML-Rendering mit Hyperscript, hyperscript-helpers , async/await von ES7 gemacht, und es war eine wahre Freude. Aber natürlich funktioniert diese Lösung nur für HTML. Eine asynchrone Lösung mit Handlebars würde es uns ermöglichen, andere Arten von Dateien asynchron zu generieren ... Aber für HTML denke ich, dass ich nie zurückblicke!

Ich werde Express-HBS ausprobieren - aber ich denke, dass es 2018 eine seltsame Sache ist, es nicht zu unterstützen. Ich weiß, dass es eine puristische Ansicht gibt, dass asynchrone Dinge nicht als Teil der Ansicht, sondern in einem magischen Ding namens Controller ausgeführt werden sollten (als ob MVC irgendwie unbestreitbar "korrekt" wäre) - aber das ist für zwei Schlüssel etwas kurzsichtig Gründe dafür

a) externe Bibliotheken - die meisten Leute schreiben jetzt komplett mit async/await - ich denke, in meinem Code sind mehr als 9 von 10 Funktionen asynchron ... in einigen Fällen "nur für den Fall". Das Fehlen einer asynchronen Funktion bedeutet, dass alle asynchronen Bibliotheken plötzlich vollständig unzugänglich sind

b) generische Controller-Funktionen. Ich würde behaupten, dass so etwas:

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

ist kürzer, sauberer, einfacher zu warten und im Grunde in jeder erdenklichen Weise überlegen im Vergleich zu einer maßgeschneiderten Controller-Funktion, die das Zeug in eine Variable steckt und es in eine Vorlage übergibt, die die Vorlage dann kennen und auf die sie zugreifen muss.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen