Quels sont les éléments «source» dans les fichiers de composants de l'interface utilisateur

17

Dans les fichiers de configuration du composant de formulaire UI de Magento 2, vous verrez souvent un itemattribut avec le même de source- <item name="source" xsi:type="string">block</item>ci - dessous.

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

À quoi servent ces champs? Je demande parce qu'il semble que ce ne soit pas nécessaire. Par exemple, le module de ce référentiel GitHub configure un formulaire de composant d'interface utilisateur fonctionnel , mais n'utilise pas ces name="source"éléments.

Quelqu'un sait-il à quoi name="source"servent ces articles? Je connais le mécanisme du composant UI qui prend le XML et le cofigure en x-magento-initJSON

"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"
    }
},

Qui est introduit dans un uiElementobjet de modèle de vue Knockout basé. Cependant, il n'est pas clair comment l'arborescence imbriquée des uiElementobjets de modèle de vue Knockout basée utilise ces champs au niveau du sourcechamp.

Si je regarde le uiElementde » initModulesméthode

    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;
    },

Je vois que l'objet fait référence à une sourcepropriété, et s'il n'est pas défini, atteindra dans le registre un objet utilisant la providerpropriété comme identifiant de chaîne / clé. Il semble que la valeur de ces sourceéléments ne soit pas utilisée. Cependant, il est possible qu'ils soient utilisés par le code PHP ou un autre code javascript. D'où ma question.

Alan Storm
la source

Réponses:

7

C'est sourceou devrait être le fournisseur de données. D'après ce que je peux dire, cependant, le <item name="source">nœud dans l'exemple XML que vous avez donné fait ne aucune différence mesurable et peut être supprimé sans conséquence.

Voici comment j'en suis arrivé à cela: dans la initModules()méthode de elements/element.js, il y a une vérification pour voir s'il this.sourcey a une fonction appelable:

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

S'il this.sourcene s'agit pas d'une fonction appelable, elle remplace this.source un composant d'interface utilisateur du registre à l'aide de this.provider. Encore une fois, c'est le provider, mais pas lesource . En tant que tel, si la source n'est pas une fonction appelable à ce stade, elle charge simplement le fournisseur et l'original this.sourceva dans le sens du vent.

this.sourceest souvent vide, mais dans le cas du cms_block_form, ce this.sourceserait 'block'pour commencer. Comme il s'agit d'une chaîne et non d'une fonction appelable, elle est simplement remplacée.

Notez également qu'un composant d'interface utilisateur pourrait facilement ajouter de la logique à définir this.sourcesur une fonction appelable, basée sur une chaîne XML, avant initModules()son exécution.


Maintenant, pourquoi cette source est-elle là en premier lieu? Je ne sais pas pourquoi c'est dans le XML, mais ça sert à quelque chose dans le Javascript. Pour un exemple, je me suis arrêté grid/columns/column.js. Dans defaults: {}, ce qui suit est là:

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

De retour elements/element.js, cela est évalué dans initModules():

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

Voici la requestModule()méthode:

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

La async()méthode est renvoyée par le registre et initModules()affectée à la propriété donnée. Dans ce cas, this.sourceest défini comme étant la async()méthode du registre. Cela se produirait pour tout ce qui se trouve à l'intérieur modules:{}, pas seulement source, mais éclaire ce qui se passe avec sourcecertains composants. La async()fonction renvoyée par est, sans surprise, une fonction appelable. En conséquence, cela est évalué à faux et est ignoré:

initModules: function () {
    ...

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

    return this;
}, 

De retour grid/columns/column.js, sourceest utilisé pour modifier le tri de la grille.

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

La async()méthode gère la fonctionnalité, mais ici, elle appelle la set()méthode this.source(). La source, ou, dataProviderest grid/provider.jset n'a pas de set()méthode. C'est le parent element/element.js, cependant:

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;
},

Le concept avec set()est quelque peu simple en ce qu'il met à jour les valeurs et informe les abonnés. Ainsi, à la suite de la columns.jsdéclaration d'un source, il a un accès direct pour exécuter des méthodes sur lui dataProvider.


Conclusion: la source semble être ce qui est utilisé, au moins dans les classes Javascript, comme fournisseur de données. Si une source est définie dans une classe Javascript et est une fonction appelable, elle peut être utilisée pour exécuter des méthodes directement sur le dataProvider.

Cela me laisse encore quelques questions:

  • Est-il possible d'utiliser source en XML pour proxy une classe dataProvider?
  • Était-il censé servir un objectif en XML mais devenir obsolète à un moment donné?
  • Existe-t-il des classes de base qui regardent this.source(à partir de XML) et font quelque chose d'intéressant avant de initModules()s'exécuter?
bassplayer7
la source
1
+1 pour des informations utiles, mais je me retrouve avec la même question que moi - qu'est-ce qui se sourcepasse dans ces fichiers XML :)
Alan Storm
7

Je suis allé à "la source" (gémir) pour celui-ci et il semble que ces <item name="source"/>nœuds soient, en effet, redondants. Ou, l'ingénieur Magento actuellement en charge d'eux pense qu'ils sont redondants, donc c'est aussi proche de la vérité que nous le ferons.

Alan Storm
la source
3

La source est la clé à l'aide de laquelle le composant ui peut lire les données fournies par " DataProvider classe ". Il est très utile lorsqu'il existe plusieurs onglets et ensembles de champs.

Par exemple: se référer 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>

La getData()méthode dans la classe DataProvider renverra un tableau avec les clés «client» et «adresse» et les champs correspondants dans les ensembles de champs en seront mappés. La capture d'écran affiche le résultat de la getData()méthode.

Sortie de la méthode getData () de la classe DataProvider

Ensuite, lorsque la getDataSourceData()méthode dans Magento \ Ui \ Component \ Form est appelée, elle traite les données ci-dessus.

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
la source
Merci d'avoir répondu. Mais en êtes-vous sûr? Je ne suis pas sûr que vous ayez raison. Oui, sur le formulaire client, les données JSON ont une clé nommée client, et cette clé utilise par coïncidence le nom du nom comme <item name="sourcenœud. Cependant, je ne vois aucun code PHP faisant référence aux données dans le nœud source. En outre, le formulaire de page CMS possède un <item name="source" xsi:type="string">page</item>nœud et ses données de source de données n'ont pas de pageclé. Enfin, ma recherche indique son name="dataScope"qui détermine où un champ obtient ses valeurs.
Alan Storm
1
oui, tu as raison Alan. Pendant le débogage, j'ai également vu la même chose (à propos de dataScope). Merci pour la clarification. Si je reçois quelque chose de plus sur "source", je posterai.
Pankaj Bhope