¿Cuáles son los elementos "fuente" en los archivos de componentes de la interfaz de usuario

17

En los archivos de configuración de componentes de formulario de IU de Magento 2, a menudo verá un item atributo con el mismo de source- <item name="source" xsi:type="string">block</item>a continuación.

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

¿Para qué son estos campos? Pregunto porque parece que no son necesarios. Por ejemplo, el módulo en este repositorio de GitHub configura un formulario de componente de interfaz de usuario que funciona , pero no usa estos name="source"elementos.

¿Alguien sabe lo que estos name="source" sirven artículos? Soy consciente de la mecánica del componente UI que toma el XML y lo configura como 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"
    }
},

Que se alimenta a un uiElementobjeto de modelo de vista Knockout basado. Sin embargo, no está claro cómo el árbol anidado de los uiElementobjetos del modelo de vista Knockout basado utiliza estos campos de nivel de sourcecampo.

Si miro el uiElement 's initModulesmétodo

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

Veo que el objeto hace referencia a una sourcepropiedad, y si no está configurado, alcanzará el registro de un objeto usando la providerpropiedad como un identificador de cadena / clave. Se parece que el valor de estos sourceno se utiliza artículos. Sin embargo, es posible que los use el código PHP o algún otro código javascript. Por lo tanto, mi pregunta.

Alan Storm
fuente

Respuestas:

7

El sourcees, o debería ser, el proveedor de datos. Sin embargo, por lo que puedo decir, el <item name="source">nodo en el ejemplo XML que proporcionó no hace una diferencia apreciable y puede eliminarse sin consecuencias.

Así es como llegué a eso: en el initModules()método de elements/element.js, hay una comprobación para ver si this.sourcees una función invocable:

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

Si this.sourceno es una función invocable, se anula this.source con un componente UI del registro mediante this.provider. De nuevo, este es el provider, sin embargo, y no el source. Como tal, si la fuente no es una función invocable en ese punto, simplemente carga el proveedor y el originalthis.source sigue el camino del viento.

this.sourcea menudo está vacío, pero en el caso de cms_block_form, this.sourcesería 'block'para empezar. Como se trata de una cadena y no de una función invocable, simplemente se anula.

También tenga en cuenta que un Componente de IU podría agregar fácilmente algo de lógica para establecer this.sourceuna función invocable, basada en una cadena de XML, antes de initModules()ejecutarse.


Ahora, ¿por qué esta fuente está allí en primer lugar? No sé por qué está en el XML, pero tiene un propósito en el Javascript. Por ejemplo, me detuve grid/columns/column.js. En defaults: {}, lo siguiente está ahí:

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

De regreso elements/element.js, esto se evalúa en initModules():

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

Aquí está el requestModule()método:

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

El async()método se devuelve desde el registro y initModules(), en , se asigna a la propiedad dada. En este caso, this.sourceestá configurado para ser el async()método del registro. Esto sucedería para cualquier cosa dentro modules:{}, no solo source, sino que arroja luz sobre lo que sucede con sourcealgunos componentes. La async()función devuelta desde es, no sorprendentemente, una función invocable. Como resultado, esto se evalúa como falso y se omite:

initModules: function () {
    ...

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

    return this;
}, 

De nuevo grid/columns/column.js, sourcese usa para cambiar la clasificación de la cuadrícula.

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

El async()método maneja la funcionalidad, pero aquí, está llamando al set()método this.source(). La fuente, o, dataProvideres grid/provider.jsy no tiene un set()método. Sin element/element.jsembargo, es padre :

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

El concepto con set()es algo simple, ya que actualiza los valores y notifica a los suscriptores. Entonces, como resultado de columns.jsdeclarar a source, tiene acceso directo para ejecutar métodos en él dataProvider.


Conclusión: la fuente parece ser la que se utiliza, al menos en las clases de Javascript, como proveedor de datos. Si una fuente se establece en una clase Javascript y es una función invocable, se puede usar para ejecutar métodos directamente endataProvider .

Sin embargo, esto me deja con algunas preguntas:

  • ¿Es posible usar sourceen XML para proxy una clase dataProvider?
  • ¿Se suponía que debía cumplir un propósito en XML pero quedar obsoleto en algún momento?
  • ¿Hay alguna clase básica que mire this.source(desde XML) y haga algo interesante antes de initModules()ejecutarla?
bassplayer7
fuente
1
Haga clic en +1 para obtener información útil, pero termine con la misma pregunta que tengo: qué está sourcehaciendo en esos archivos XML :)
Alan Storm
7

Fui a "la fuente" (gemido) para este y parece que estos <item name="source"/>nodos son, de hecho, redundantes. O, el ingeniero de Magento actualmente a cargo de ellos piensa que son redundantes, así que eso es lo más cercano a la verdad que obtendremos.

Alan Storm
fuente
3

La fuente es la clave que utiliza el componente ui para leer los datos proporcionados por " DataProvider clase ". Es muy útil cuando hay múltiples pestañas y conjuntos de campos.

Por ejemplo: referir 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>

El getData()método en la clase DataProvider devolverá una matriz con las claves 'cliente' y 'dirección' y los campos correspondientes en los conjuntos de campos se asignarán a partir de ella. La captura de pantalla muestra el resultado del getData()método.

Salida del método getData () de la clase DataProvider

Luego, cuando getDataSourceData()se llama al método en Magento \ Ui \ Component \ Form, procesa los datos anteriores.

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
fuente
Gracias por responder. Sin embargo, ¿estás seguro de esto? No estoy seguro de que tengas razón. Sí, en el formulario del cliente, los datos JSON tienen una clave llamada customer, y esa clave utiliza casualmente el nombre name como <item name="sourcenodo. Sin embargo, no veo ningún código PHP que haga referencia a los datos en el nodo de origen. Además, el formulario de página CMS tiene un <item name="source" xsi:type="string">page</item>nodo y su fuente de datos no tiene una pageclave. Finalmente, mi investigación indica name="dataScope"que determina dónde obtiene sus valores un campo.
Alan Storm
1
Sí, tienes razón, Alan. Durante la depuración también vi lo mismo (sobre dataScope). Gracias por la aclaración. Si obtengo algo más sobre "fuente", publicaré.
Pankaj Bhope