Was sind die Quellelemente in den UI-Komponentendateien?

16

In den Konfigurationsdateien für die UI-Formularkomponente von Magento 2 wird häufig ein itemAttribut mit dem folgenden source- <item name="source" xsi:type="string">block</item>angezeigt.

#File: vendor/magento/module-cms/view/adminhtml/ui_component/cms_block_form.xml
<field name="title">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="dataType" xsi:type="string">text</item>
            <item name="label" xsi:type="string" translate="true">Block Title</item>
            <item name="formElement" xsi:type="string">input</item>
            <item name="source" xsi:type="string">block</item>
            <item name="sortOrder" xsi:type="number">20</item>
            <item name="dataScope" xsi:type="string">title</item>
            <item name="validation" xsi:type="array">
                <item name="required-entry" xsi:type="boolean">true</item>
            </item>
        </item>
    </argument>
</field>    

Wofür sind diese Felder? Ich frage, weil es scheint, als wären sie nicht notwendig. Zum Beispiel das Modul in dieser GitHub - Repository konfiguriert eine Arbeits UI Component Form , aber diese nicht verwendet name="source"Elemente.

Weiß jemand, wofür diese name="source"Gegenstände sind? Mir ist die UI-Komponenten-Mechanik bekannt, die das XML als x-magento-initJSON konfiguriert

"block_id": {
    "type": "form.input",
    "name": "block_id",
    "dataScope": "block_id",
    "config": {
        "component": "Magento_Ui\/js\/form\/element\/abstract",
        "template": "ui\/form\/field",
        "visible": false,
        "dataType": "text",
        "formElement": "input",
        "source": "block"
    }
},

Wird in ein uiElementbasiertes Knockout-Ansichtsmodellobjekt eingespeist . Es ist jedoch nicht klar, wie die verschachtelte Baumstruktur von uiElementObjekten des Knockout-Ansichtsmodells diese sourceFelder auf Feldebene verwendet.

Wenn ich auf das Aussehen uiElement‚s initModulesMethode

    initModules: function () {
        _.each(this.modules, function (name, property) {
            if (name) {
                this[property] = this.requestModule(name);
            }
        }, this);

        if (!_.isFunction(this.source)) {
            this.source = registry.get(this.provider);
        }

        return this;
    },

Ich sehe, dass das Objekt auf eine sourceEigenschaft verweist und, wenn es nicht festgelegt ist, in die Registrierung nach einem Objekt greift, wobei die providerEigenschaft als Zeichenfolge / Schlüsselkennung verwendet wird. Es scheint, als würde der Wert dieser sourceGegenstände nicht verwendet. Es ist jedoch möglich, dass sie vom PHP-Code oder einem anderen Javascript-Code verwendet werden. Daher meine Frage.

Alan Storm
quelle

Antworten:

7

Das sourceist oder sollte der Datenprovider sein. Soweit ich das beurteilen kann, macht der <item name="source">Knoten in dem von Ihnen angegebenen XML-Beispiel keinen messbaren Unterschied und kann ohne Konsequenz entfernt werden.

Hier ist, wie ich dazu gekommen bin: In der initModules()Methode von elements/element.jsgibt es eine Überprüfung, ob this.sourcees eine aufrufbare Funktion gibt:

if (!_.isFunction(this.source)) {
    this.source = registry.get(this.provider);
}

Wenn this.sourcees sich nicht um eine aufrufbare Funktion handelt, wird sie mit einer UI-Komponente aus der Registrierung überschrieben . Auch dies ist das , aber nicht das . Wenn die Quelle zu diesem Zeitpunkt keine aufrufbare Funktion ist, lädt sie einfach den Provider und das Original geht den Weg des Windes.this.sourcethis.providerprovidersourcethis.source

this.sourceOft ist leer, aber im Falle der cms_block_form, this.sourcewäre es 'block'am Anfang. Da dies eine Zeichenfolge und keine aufrufbare Funktion ist, wird sie einfach überschrieben.

Beachten Sie auch, dass eine UI-Komponente leicht eine Logik hinzufügen kann, um this.sourceeine aufrufbare Funktion basierend auf einer Zeichenfolge aus XML festzulegen, bevor sie ausgeführt initModules()wird.


Warum gibt es diese Quelle überhaupt? Ich weiß nicht, warum es im XML ist, aber es dient einem Zweck im Javascript. Zum Beispiel habe ich gezogen grid/columns/column.js. In defaults: {}gibt es folgendes:

modules: {
    source: '${ $.provider }'
}

Zurück in elements/element.js, dies wird ausgewertet in initModules():

_.each(this.modules, function (name, property) {
    if (name) {
        this[property] = this.requestModule(name);
    }
}, this);

Hier ist die requestModule()Methode:

requestModule: function (name) {
    var requested = this._requesetd;
    if (!requested[name]) {
        requested[name] = registry.async(name);
    }
    return requested[name];
},

Die async()Methode wird von der Registrierung zurückgegeben und in initModules()der angegebenen Eigenschaft zugewiesen. In diesem Fall this.sourcewird die async()Methode aus der Registrierung festgelegt. Dies würde für alles innerhalb passieren modules:{}, nicht nur source, sondern gibt Aufschluss darüber, was mit den sourcein einigen Komponenten passiert . Die von zurückgegebene async()Funktion ist - nicht überraschend - eine aufrufbare Funktion. Infolgedessen wird dies als falsch ausgewertet und übersprungen:

initModules: function () {
    ...

    if (!_.isFunction(this.source)) {
        this.source = registry.get(this.provider);
    }

    return this;
}, 

Back in grid/columns/column.js, sourcewird verwendet, um die Sortierung des Rasters zu ändern.

exportSorting: function () {
    ...
    this.source('set', 'params.sorting', {
        field: this.index,
        direction: this.sorting
    });
},

Die async()Methode behandelt die Funktionalität, ruft hier jedoch die set()Methode auf this.source(). Die Quelle oder dataProviderist grid/provider.jsund hat keine set()Methode. Ihr Elternteil element/element.jstut es jedoch:

set: function (path, value) {
    var data = this.get(path),
        diffs;

    diffs = !_.isFunction(data) && !this.isTracked(path) ?
        utils.compare(data, value, path) :
        false;

    utils.nested(this, path, value);

    if (diffs) {
        this._notifyChanges(diffs);
    }

    return this;
},

Das Konzept mit set()ist insofern etwas einfach, als es Werte aktualisiert und Abonnenten benachrichtigt. Als Ergebnis der columns.jsDeklaration von a sourcehat es direkten Zugriff auf die Ausführung von Methoden dataProvider.


Fazit: Die Quelle scheint das zu sein, was zumindest in Javascript-Klassen als Datenprovider verwendet wird. Wenn eine Quelle in einer Javascript-Klasse festgelegt ist und eine aufrufbare Funktion ist, kann sie verwendet werden, um Methoden direkt in der Klasse auszuführen dataProvider.

Dies lässt mich jedoch noch ein paar Fragen offen:

  • Ist es möglich, sourcein XML einen Proxy für eine dataProvider-Klasse zu erstellen?
  • Sollte es in XML einen Zweck erfüllen, aber irgendwann veraltet sein?
  • Gibt es Kernklassen, die sich this.source(aus XML) ansehen und etwas Interessantes damit anfangen, bevor sie initModules()ausgeführt werden?
Bassist7
quelle
1
+1 für nützliche Informationen, aber am Ende mit der gleichen Frage, die ich habe - was sourcein diesen XML-Dateien zu tun :)
Alan Storm
7

Ging zu "der Quelle" (Stöhnen) für diese und es sieht so aus, als ob diese <item name="source"/>Knoten in der Tat redundant sind. Oder der Magento-Ingenieur, der derzeit für sie zuständig ist, glaubt, dass sie überflüssig sind.

Alan Storm
quelle
3

Die Quelle ist der Schlüssel, mit dem die UI-Komponente die von der Klasse " DataProvider " bereitgestellten Daten lesen kann . Dies ist sehr nützlich, wenn mehrere Registerkarten und Feldsätze vorhanden sind.

Zum Beispiel: siehe module-customer/view/base/ui_component/customer_form.xml

<fieldset name="customer">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="label" xsi:type="string" translate="true">Account Information</item>
        </item>
    </argument>
    <field name="entity_id">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="visible" xsi:type="boolean">false</item>
                <item name="dataType" xsi:type="string">text</item>
                <item name="formElement" xsi:type="string">input</item>

                **<item name="source" xsi:type="string">customer</item>**

            </item>
        </argument>
    </field>
. 
. 
.

<fieldset name="address">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="is_collection" xsi:type="boolean">true</item>
            <item name="label" xsi:type="string" translate="true">Addresses</item>
            <item name="removeMessage" xsi:type="string" translate="true">Are you sure you want to delete this item?</item>
        </item>
    </argument>
    <field name="parent_id">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="visible" xsi:type="boolean">false</item>
                <item name="dataType" xsi:type="string">number</item>
                <item name="formElement" xsi:type="string">input</item>

                **<item name="source" xsi:type="string">address</item>**

            </item>
        </argument>
    </field>

Die getData()Methode in der DataProvider- Klasse gibt ein Array mit den Schlüsseln 'customer' und 'address' zurück und die entsprechenden Felder in den Feldsätzen werden daraus zugeordnet. Der Screenshot zeigt das Ergebnis der getData()Methode.

Ausgabe der getData () -Methode der DataProvider-Klasse

Anschließend verarbeitet die getDataSourceData()Methode in Magento \ Ui \ Component \ Form die oben genannten Daten.

public function getDataSourceData()
{
    $dataSource = [];

    $id = $this->getContext()->getRequestParam($this->getContext()->getDataProvider()->getRequestFieldName(), null);
    $filter = $this->filterBuilder->setField($this->getContext()->getDataProvider()->getPrimaryFieldName())
        ->setValue($id)
        ->create();
    $this->getContext()->getDataProvider()
        ->addFilter($filter);

    $data = $this->getContext()->getDataProvider()->getData();

    if (isset($data[$id])) {
        $dataSource = [
            'data' => $data[$id]
        ];
    } elseif (isset($data['items'])) {
        foreach ($data['items'] as $item) {
            if ($item[$item['id_field_name']] == $id) {
                **$dataSource = ['data' => ['general' => $item]];**
            }
        }
    }
    return $dataSource;
}
Pankaj Bhope
quelle
Danke für die Antwort. Sind Sie sich jedoch sicher? Ich bin mir nicht sicher, ob du recht hast. Ja, auf dem Kundenformular haben die JSON-Daten einen Schlüssel mit dem Namen customer, und dieser Schlüssel verwendet zufällig den Namen name als <item name="sourceKnoten. Es wird jedoch kein PHP-Code angezeigt, der auf die Daten im Quellknoten verweist. Das CMS-Seitenformular verfügt außerdem über einen <item name="source" xsi:type="string">page</item>Knoten und die Datenquellendaten haben keinen pageSchlüssel. Schließlich gibt meine Forschung an, name="dataScope"dass es bestimmt, wo ein Feld seine Werte bekommt.
Alan Storm
1
ja, du hast recht Alan. Während des Debuggens habe ich auch dasselbe gesehen (über dataScope). Danke für die Klarstellung. Wenn ich mehr über "source" erfahre, werde ich posten.
Pankaj Bhope