O que são os itens de "origem" nos arquivos de componentes da interface do usuário

17

Nos arquivos de configuração do UI Form Component do Magento 2, você verá um itematributo com o mesmo source- <item name="source" xsi:type="string">block</item>abaixo.

#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 que servem esses campos? Eu pergunto porque parece que eles não são necessários. Por exemplo, o módulo neste repositório GitHub configura um formulário de componente de interface do usuário em funcionamento , mas não usa esses name="source"itens.

Alguém sabe para que name="source"servem esses itens? Estou ciente da mecânica do componente de interface do usuário que pega o XML e o cofigura 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 é alimentado em um uiElementobjeto de modelo de exibição Knockout baseado. No entanto, não está claro como a árvore aninhada dos uiElementobjetos de modelo de exibição Knockout baseados usa esses campos no nível do sourcecampo.

Se eu olhar para o 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;
    },

Vejo que o objeto faz referência a uma sourcepropriedade e, se não estiver definido, alcançará o registro de um objeto usando a providerpropriedade como um identificador de cadeia / chave. Ele parece ser o valor desses sourceitens não é usado. No entanto, é possível que eles sejam usados ​​pelo código PHP ou algum outro código javascript. Portanto, minha pergunta.

Alan Storm
fonte

Respostas:

7

O sourceé, ou deveria ser, o provedor de dados. Pelo que sei, no entanto, o <item name="source">nó no exemplo XML que você deu não faz diferença mensurável e pode ser removido sem conseqüências.

Aqui está como cheguei a isso: no initModules()método de elements/element.js, há uma verificação para ver se this.sourceé uma função que pode ser chamada:

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

Se this.sourcenão for uma função que pode ser chamada, ela substitui this.source um componente de interface do usuário do registro usando this.provider. Novamente, este é o provider, porém, e não o source. Como tal, se a fonte não for uma função que pode ser chamada nesse ponto, ela simplesmente carrega o provedor e o original this.sourcesegue o caminho do vento.

this.sourcegeralmente está vazio, mas no caso do cms_block_form, this.sourceseria 'block'para começar. Como essa é uma string e não uma função que pode ser chamada, ela é simplesmente substituída.

Observe também que um componente de interface do usuário pode facilmente adicionar alguma lógica para definir this.sourceuma função que pode ser chamada, com base em uma string do XML, antes da initModules()execução.


Agora, por que essa fonte está lá em primeiro lugar? Não sei por que está no XML, mas serve a um propósito no Javascript. Por exemplo, eu parei grid/columns/column.js. Em defaults: {}, há o seguinte:

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

De volta elements/element.js, isso é avaliado em initModules():

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

Aqui está o requestModule()método:

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

O async()método é retornado do registro e initModules(), atribuído à propriedade fornecida. Nesse caso, this.sourceestá definido como o async()método do registro. Isso aconteceria com qualquer coisa dentro modules:{}, não apenas source, mas lança luz sobre o que acontece com sourcealguns componentes. A async()função retornada é - não surpreendentemente - uma função que pode ser chamada. Como resultado, isso é avaliado como falso e é ignorado:

initModules: function () {
    ...

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

    return this;
}, 

De volta grid/columns/column.js, sourceé usado para alterar a classificação da grade.

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

O async()método lida com a funcionalidade, mas aqui, ele está chamando o set()método this.source(). A fonte, ou, dataProvideré grid/provider.jse não possui um set()método. É pai element/element.js, porém:

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

O conceito com set()é um pouco simples, pois atualiza valores e notifica os assinantes. Portanto, como resultado de columns.jsdeclarar um source, ele tem acesso direto para executar métodos nele dataProvider.


Conclusão: a fonte parece ser o que é usado, pelo menos nas classes Javascript, como provedor de dados. Se uma fonte é definida em uma classe Javascript e é uma função que pode ser chamada, ela pode ser usada para executar métodos diretamente no dataProvider.

Isso me deixa com algumas perguntas ainda, no entanto:

  • É possível usar sourceem XML para fazer proxy de uma classe dataProvider?
  • Deveria servir a um propósito em XML, mas ser preterido em algum momento?
  • Existem classes principais que examinam this.source(de XML) e fazem algo interessante com ela antes de initModules()serem executadas?
bassplayer7
fonte
1
+1 para informações úteis, mas acabar com a mesma pergunta que eu tenho - o que está sourcefazendo nesses arquivos XML :)
Alan Storm
7

Fui para "a fonte" (gemido) deste e parece que esses <item name="source"/>nós são, de fato, redundantes. Ou, o engenheiro Magento atualmente no comando deles se acha redundante, então isso é o mais próximo da verdade que chegaremos.

Alan Storm
fonte
3

A fonte é a chave com a qual o componente da interface do usuário pode ler os dados fornecidos pela classe " DataProvider ". É muito útil quando existem várias guias e conjuntos de campos.

Por exemplo: consulte 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>

O getData()método na classe DataProvider retornará uma matriz com as chaves 'customer' e 'address' e os campos correspondentes nos conjuntos de campos serão mapeados a partir dele. A captura de tela exibe o resultado do getData()método.

Saída do método getData () da classe DataProvider

Depois, quando o getDataSourceData()método em Magento \ Ui \ Component \ Form é chamado, ele processa os dados acima.

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
fonte
Obrigado por responder. No entanto, você tem certeza disso? Não sei se você está correto. Sim, no formulário do cliente, os dados JSON possuem uma chave chamada customer e essa chave coincidentemente usa o nome name como o <item name="sourcenó. No entanto, não vejo nenhum código PHP que faça referência aos dados no nó de origem. Além disso, o formulário Página do CMS possui um <item name="source" xsi:type="string">page</item>nó e seus dados da fonte de dados não possuem uma pagechave. Finalmente, minha pesquisa indica name="dataScope"que determina onde um campo obtém seus valores.
Alan Storm
1
Sim, você está certo, Alan. Durante a depuração, também vi a mesma coisa (sobre o dataScope). Obrigado pelo esclarecimento. Se eu tiver mais alguma coisa sobre "fonte", publicarei.
Pankaj Bhope