Handlebars.js: Unterstützung für Maps, Sets und benutzerdefinierte Iterables im integrierten „each“-Helfer?

Erstellt am 10. Jan. 2018  ·  6Kommentare  ·  Quelle: handlebars-lang/handlebars.js

Bei der Verwendung von Handlebars in einer ES6-Umgebung wird die Einschränkung des integrierten each -Hilfsprogramms, nur Arrays und generische Objekte zu unterstützen, unpraktisch. Um dies zu umgehen, habe ich begonnen, meine eigene Version des each -Hilfsprogramms zu registrieren, das Arrays, Maps, Sets, benutzerdefinierte Iterables und generische Objekte unterstützt. Dieser Helfer ist unten.

Gibt es einen Plan oder die Bereitschaft, Unterstützung für diese Arten von Listen im integrierten each -Hilfsprogramm einzuführen? Ich frage, weil ich verstehe, dass Handlebars darauf abzielt, Polyfills zu vermeiden, und ich mir vorstelle, dass die einzige Möglichkeit, den neuen Helfer zum Laufen zu bringen, ohne die Browserunterstützung zu beeinträchtigen, darin besteht, die Unterstützung für die verschiedenen Listentypen schrittweise zu aktivieren, abhängig von der nativen oder vorab polyfillierten Unterstützung der Umgebung für Set , Map und Symbol .

Handlebars.registerHelper("each", function (contexts, options) {

    // Throw a runtime exception if options were not supplied.
    if (!options) {
        throw new Handlebars.Exception("Must pass iterator to #each");
    }

    // If the "list of contexts" is a function, execute it to get the actual list of contexts.
    if (typeof contexts === "function") {
        contexts = contexts.call(this);
    }

    // If data was supplied, frame it.
    const data = options.data ? Object.assign({}, options.data, { _parent: options.data }) : undefined;

    // Create the string into which the contexts will be handled and returned.
    let string = "";

    // Create a flag indicating whether or not string building has begun.
    let stringExtensionStarted = false;

    // Create a variable to hold the context to use during the next string extension. This is done to
    // allow iteration through the supplied list of contexts one step out of sync as they are looped
    // through later in this helper, ensuring a predictable sequence of value retrieval, string
    // extension, value retrieval, string extension...
    let nextContext;

    // Create a function responsible for expanding the string.
    const extendString = (final = false) => {

        // If other contexts have been encountered...
        if (nextContext) {

            // Expand the string using the block function.
            string += options.fn(nextContext.value, {
                data: data ? Object.assign(data, {
                    index: nextContext.index,
                    key: nextContext.key,
                    first: !stringExtensionStarted,
                    last: final
                }) : undefined,
                blockParams: [nextContext.key, nextContext.value]
            });

            // Note that string extension has begun.
            stringExtensionStarted = true;

        // If no contexts have been encountered and this is the final extension...
        } else if (final) {

            // Expand the string using the "else" block function.
            string += options.inverse(this);

        }

    };

    // If a list of contexts was supplied...
    if (contexts !== null && typeof contexts !== "undefined") {

        // Start a counter.
        let index = 0;

        // If an array list was supplied...
        if (Array.isArray(contexts)) {

            // For each of the possible indexes in the supplied array...
            for (const len = contexts.length; index < len; index++) {

                // If the index is in the supplied array...
                if (index in contexts) {

                    // Call the string extension function.
                    extendString();

                    // Define the context to use during the next string extension.
                    nextContext = {
                        index: index,
                        key: index,
                        value: contexts[index]
                    };

                }

            }

        // If a map list was supplied...
        } else if (contexts instanceof Map) {

            // For each entry in the supplied map...
            for (const [key, value] of contexts) {

                // Call the string extension function.
                extendString();

                // Define the context to use during the next string extension.
                nextContext = {
                    index: index,
                    key: key,
                    value: value
                };

                // Increment the counter.
                index++;

            }

        // If an iterable list was supplied (including set lists)...
        } else if (typeof contexts[Symbol.iterator] === "function") {

            // Get an iterator from the iterable.
            const iterator = contexts[Symbol.iterator]();

            // Create a variable to hold the iterator's next return.
            let next;

            // Do the following...
            do {

                // Iterate and update the variable.
                next = iterator.next();

                // If there is anything left to iterate...
                if (!next.done) {

                    // Call the string extension function.
                    extendString();

                    // Define the context to use during the next string extension.
                    nextContext = {
                        index: index,
                        key: index,
                        value: next.value
                    };

                    // Increment the counter.
                    index++;

                }

            // ... until there is nothing left to iterate.
            } while (!next.done);

        // If a list other than an array, map, or iterable was supplied...
        } else {

            // For each key in the supplied object...
            for (const key of Object.keys(contexts)) {

                // Call the string extension function.
                extendString();

                // Define the context to use during the next string extension.
                nextContext = {
                    index: index,
                    key: key,
                    value: contexts[key]
                };

                // Increment the counter.
                index++;

            }

        }

    }

    // Call the string extension a final time now that the last supplied context has been encountered.
    extendString(true);

    // Return the fully-extended string.
    return string;

});

Hilfreichster Kommentar

@karlvr könnten Sie ein neues Problem für den Kartensupport starten. Teile dieses Problems sind bereits gelöst und ich möchte einen sauberen Start haben.

Alle 6 Kommentare

Sollte jetzt möglich sein, mit #1557

@nknapp es scheint, dass die Implementierung in #1557 Map nicht richtig unterstützt. Es erzeugt derzeit ein iteriertes Element, das der _entry_ in Map ist, was ein Tupel von [key, value] ist, während der obige Beispielcode das iterierte Element zu value macht und @key , was ich _denke_ ist vorzuziehen. Es ist mir vorzuziehen!

Außerdem scheint es, dass Ausdrücke derzeit Map nicht unterstützen, sodass Sie nicht {{person.myMap.myMapKey}} sagen können. Ich beschäftige mich jetzt intensiver mit diesem Thema.

Mit einem Zusatz in lookupProperty in runtime.js können wir Eigenschaften in Map nachschlagen:

    lookupProperty: function(parent, propertyName) {
      if (parent instanceof Map) {
        return parent.get(propertyName)
      }

Gibt es Lust, Unterstützung wie diese hinzuzufügen?

@karlvr Ich denke, Ihr Vorschlag ist es wert, geprüft zu werden. Aber ich möchte darüber diskutieren.

@karlvr könnten Sie ein neues Problem für den Kartensupport starten. Teile dieses Problems sind bereits gelöst und ich möchte einen sauberen Start haben.

@nknapp vielen Dank für deine schnelle Antwort; Ich habe gerade eine PR mit den vorgeschlagenen Änderungen gemacht. Könnten wir da diskutieren? #1679

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen