Magento Grid-Komponente wird nicht richtig sortiert

16

Ich habe in Magento eine Grid-Komponente konfiguriert - und das Sortierverhalten scheint gestört zu sein. Wo kann ich dies auf der Javascript-Ebene debuggen, und / oder hat jemand eine Idee, warum dies passieren könnte?

Wenn ich das Raster einmal sortiere, wird eine Ajax-Anfrage gestellt und alles wird korrekt sortiert.

Bildbeschreibung hier eingeben

Bei der zweiten Sortierung wird das Raster jedoch ohne Ajax-Anforderung mit denselben IDs gerendert.

Bildbeschreibung hier eingeben

Das Verhalten wird in Magento-Kerngittern nicht wiederholt, daher bin ich mir ziemlich sicher, dass ich das tue. Ich kenne das UI-Komponentensystem einfach nicht gut genug, um zu wissen, wo ich mit dem Debuggen beginnen soll.

Alan Storm
quelle

Antworten:

21

Okay, ich kann noch nicht so tun, als ob ich verstehe warum, aber das Problem war das dataArgument meines dataProviderArguments.

<!-- ... -->
<argument name="dataProvider" xsi:type="configurableObject">
    <!-- ... --->
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="update_url" xsi:type="url" path="mui/index/render"/>
        </item>
    </argument>
    <!-- ... -->
</argument>
<!-- ... -->

Beim Vergleich mit einigen der Kerngitter datafehlte dem Argument ein storageConfigKnoten mit einem indexFieldUnterknoten mit dem Primärschlüssel meines Modells.

<argument name="data" xsi:type="array">
    <item name="config" xsi:type="array">
        <item name="update_url" xsi:type="url" path="mui/index/render"/>
        <item name="storageConfig" xsi:type="array">
            <item name="indexField" xsi:type="string">pulsestorm_commercebug_log_id</item>
        </item>                    

    </item>                          
</argument>

Als ich diese Knoten hinzufügte, wurde die Sortierfunktion wiederhergestellt.

Alan Storm
quelle
Ich stelle mir gerade vor, dass es sich um ein Zurückgreifen oder Laden von Werten aus dem Speicher durch den Zeilenindex handelt und nicht um eine Datenzeilen-ID, obwohl es keinen Sinn macht, warum die Daten dupliziert werden. Danke für die Antwort.
LM_Fielding
8

TL; DR

Dies ist in der Tat ein interessantes Problem.

So habe ich das System verstanden, aber möglicherweise habe ich nicht 100% Recht.

Wie Sie gesehen haben, generiert das Klicken auf die Kopfspalte eine AJAX-Anforderung an die folgende Route: /admin_key/mui/index/rendermit den folgenden Parametern:

  • Filter [Platzhalter]
  • isAjax
  • Namespace
  • paging [aktuell]
  • paging [pageSize]
  • Suche
  • sortieren [richtung]
  • Sortierung [Feld]

Das letzte Feld ist das Feld, nach dem Sie Ihr Raster sortieren.

Diese Route ist standardmäßig deklariert in app/code/Magento/Ui/view/base/ui_component/etc/definition.xml:

<insertListing class="Magento\Ui\Component\Container">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="component" xsi:type="string">Magento_Ui/js/form/components/insert-listing</item>
            <item name="update_url" xsi:type="url" path="mui/index/render"/>
            <item name="render_url" xsi:type="url" path="mui/index/render"/>
            <item name="autoRender" xsi:type="boolean">false</item>
            <item name="dataLinks" xsi:type="array">
                <item name="imports" xsi:type="boolean">true</item>
                <item name="exports" xsi:type="boolean">false</item>
            </item>
            <item name="realTimeLink" xsi:type="boolean">true</item>
        </item>
    </argument>
</insertListing>

In einer Auflistung von ui_component-XML wird jedoch auch Folgendes deklariert:

        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item>
                <item name="update_url" xsi:type="url" path="mui/index/render"/>
                <item name="storageConfig" xsi:type="array">
                    <item name="indexField" xsi:type="string">page_id</item>
                </item>
            </item>
        </argument>

Diese Route wird app/code/Magento/Ui/Controller/Adminhtml/Index/Render.phpbasierend auf dem Namespace-Parameter (normalerweise der Name Ihrer UI-Komponente) behandelt.

public function execute()
{
    if ($this->_request->getParam('namespace') === null) {
        $this->_redirect('admin/noroute');
        return;
    }

    $component = $this->factory->create($this->_request->getParam('namespace'));
    $this->prepareComponent($component);
    $this->_response->appendBody((string) $component->render());
}

Wenn die prepareComponentMethode für die untergeordneten Komponenten rekursiv ist:

protected function prepareComponent(UiComponentInterface $component)
{
    foreach ($component->getChildComponents() as $child) {
        $this->prepareComponent($child);
    }
    $component->prepare();
}

Wenn die Spaltenkomponente vorbereitet ist, erfolgt die Sortierung der Spalten wie folgt app/code/Magento/Ui/Component/Listing/Columns/Column.php:

public function prepare()
{
    $this->addFieldToSelect();

    $dataType = $this->getData('config/dataType');
    if ($dataType) {
        $this->wrappedComponent = $this->uiComponentFactory->create(
            $this->getName(),
            $dataType,
            array_merge(['context' => $this->getContext()], (array) $this->getData())
        );
        $this->wrappedComponent->prepare();
        $wrappedComponentConfig = $this->getJsConfig($this->wrappedComponent);
        // Merge JS configuration with wrapped component configuration
        $jsConfig = array_replace_recursive($wrappedComponentConfig, $this->getJsConfig($this));
        $this->setData('js_config', $jsConfig);

        $this->setData(
            'config',
            array_replace_recursive(
                (array)$this->wrappedComponent->getData('config'),
                (array)$this->getData('config')
            )
        );
    }

    $this->applySorting();

    parent::prepare();
}

Wenn die applySorting()Methode auf dem Sortierparameter basiert und dem Datenprovider einfach die Reihenfolge hinzufügt:

protected function applySorting()
{
    $sorting = $this->getContext()->getRequestParam('sorting');
    $isSortable = $this->getData('config/sortable');
    if ($isSortable !== false
        && !empty($sorting['field'])
        && !empty($sorting['direction'])
        && $sorting['field'] === $this->getName()
    ) {
        $this->getContext()->getDataProvider()->addOrder(
            $this->getName(),
            strtoupper($sorting['direction'])
        );
    }
}

Sobald jede Komponente vorbereitet ist, rendert die Aktionsklasse die Komponente (erneut rekursiv) für die Antwort:

$this->_response->appendBody((string) $component->render());

Ich gehe davon aus, dass dies die wichtigen PHP-Schritte für die Sortierung sind.

Jetzt werden der JS die Render- und Update-URLs (oben deklariert definition.xml) dem Element zugewiesen in app/code/Magento/Ui/view/base/web/js/form/components/insert.js:

return Element.extend({
    defaults: {
        content: '',
        template: 'ui/form/insert',
        showSpinner: true,
        loading: false,
        autoRender: true,
        visible: true,
        contentSelector: '${$.name}',
        externalData: [],
        params: {
            namespace: '${ $.ns }'
        },
        renderSettings: {
            url: '${ $.render_url }',
            dataType: 'html'
        },
        updateSettings: {
            url: '${ $.update_url }',
            dataType: 'json'
        },
        imports: {},
        exports: {},
        listens: {},
        links: {
            value: '${ $.provider }:${ $.dataScope}'
        },
        modules: {
            externalSource: '${ $.externalProvider }'
        }
    }

In dieser Datei befindet sich noch eine requestDataMethode zum Abrufen der AJAX-Daten:

    requestData: function (params, ajaxSettings) {
        var query = utils.copy(params);

        ajaxSettings = _.extend({
            url: this['update_url'],
            method: 'GET',
            data: query,
            dataType: 'json'
        }, ajaxSettings);

        this.loading(true);

        return $.ajax(ajaxSettings);
    }

Sie können sehen, dass diese Methode aufgerufen wird, wenn die render()Methode aufgerufen wird:

        $.async({
            component: this.name,
            ctx: '.' + this.contentSelector
        }, function (el) {
            self.contentEl = $(el);
            self.startRender = true;
            params = _.extend({}, self.params, params || {});
            request = self.requestData(params, self.renderSettings);
            request
                .done(self.onRender)
                .fail(self.onError);
        });

Sobald dies erledigt ist, wird eine Rückrufmethode aufgerufen, um die Daten anzuwenden. Es ist onRender():

    onRender: function (data) {
        this.loading(false);
        this.set('content', data);
        this.isRendered = true;
        this.startRender = false;
    }

Ich gehe davon aus, dass dort die neuen Inhalte angewendet werden.

Raphael bei Digital Pianism
quelle
Code Columbo ...
LM_Fielding