Le composant de la grille Magento ne trie pas correctement

16

J'ai configuré un composant de grille dans Magento - et le comportement de tri semble rompu. Où puis-je déboguer cela au niveau javascript, et / ou quelqu'un d'autre a-t-il une idée pourquoi cela pourrait se produire?

Si je trie la grille une fois, une demande ajax est effectuée et tout est correctement trié.

entrez la description de l'image ici

Cependant, le deuxième tri, sans demande ajax, rend la grille avec tous les mêmes ID.

entrez la description de l'image ici

Le comportement n'est pas répété sur les grilles de base de Magento, donc je suis sûr que c'est quelque chose que je fais. Je ne connais pas assez bien le système de composants ui pour savoir par où commencer le débogage.

Alan Storm
la source

Réponses:

21

D'accord, je ne peux pas prétendre comprendre pourquoi encore, mais le problème était l' dataargument de mon dataProviderargument.

<!-- ... -->
<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>
<!-- ... -->

Lorsque j'ai comparé cela à quelques-unes des grilles centrales, l' dataargument manquait un storageConfignœud, avec un indexFieldsous-nœud avec la clé primaire de mon modèle.

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

Lorsque j'ai ajouté ces nœuds, la fonctionnalité de tri a été restaurée.

Alan Storm
la source
Je viens de rencontrer le même problème, j'imagine qu'il recule ou charge des valeurs du stockage par l'index de ligne plutôt que l'ID de ligne de données, bien que cela ne comprenne pas pourquoi les données sont dupliquées. Merci pour la réponse.
LM_Fielding
7

TL; DR

Il s'agit en effet d'un problème intéressant.

Voici comment j'ai compris le système, mais je n'ai peut-être pas 100% raison.

Comme vous l'avez vu, un clic sur la colonne d'en-tête génère une requête AJAX vers l'itinéraire suivant: /admin_key/mui/index/renderavec les paramètres suivants:

  • filtres [espace réservé]
  • isAjax
  • espace de noms
  • paging [actuel]
  • pagination [pageSize]
  • chercher
  • tri [direction]
  • tri [champ]

Le dernier est le champ sur lequel vous triez votre grille.

Cette route est déclarée par défaut dans 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>

Mais dans une liste ui_component XML, il est également déclaré:

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

Cette route est gérée par en app/code/Magento/Ui/Controller/Adminhtml/Index/Render.phpfonction du paramètre namespace (qui est normalement le nom de votre composant d'interface utilisateur)

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());
}

Lorsque la prepareComponentméthode est récursive sur les composants enfants:

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

Lorsque le composant de colonne est préparé, le tri des colonnes est géré par 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();
}

Où la applySorting()méthode est basée sur le paramètre de tri et ajoute simplement l'ordre au fournisseur de données:

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'])
        );
    }
}

Une fois que chaque composant est préparé, la classe d'actions affiche le composant (à nouveau de manière récursive) pour la réponse:

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

Je pense que ce sont les étapes PHP importantes de ce qui se passe pendant le tri.

Maintenant, pour le JS, les URL de rendu et de mise à jour (déclarées definition.xmlci-dessus) sont attribuées à l'élément dans 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 }'
        }
    }

Toujours dans ce fichier, il y a une requestDataméthode qui est utilisée pour récupérer les données AJAX:

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

Vous pouvez voir que cette méthode est appelée lorsque la render()méthode est appelée:

        $.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);
        });

Une fois cela fait, une méthode de rappel est appelée pour appliquer les données. C'est onRender():

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

Je pense que c'est là que le nouveau contenu est appliqué.

Raphael chez Digital Pianism
la source
Code Columbo ...
LM_Fielding