5

I actualized Magento to version 2.1 and my buttons stopped working. I'm not sure what I've changed. Actual version of files below.

My problem is to send save request to proper action (before it were working by ajax.) to controller action. Now if it even redirect page. I can't get post data on it. (is empty array).

settings_form.xml

<argument name="data" xsi:type="array">
    <item name="js_config" xsi:type="array">
        <item name="provider" xsi:type="string">settings_form.correctemail_settings_form_data_source</item>
        <item name="deps" xsi:type="string">settings_form.correctemail_settings_form_data_source</item>
    </item>
    <item name="label" xsi:type="string" translate="true">Sample Form</item>
    <item name="layout" xsi:type="array">
        <item name="type" xsi:type="string">tabs</item>
    </item>
    <item name="buttons" xsi:type="array">
        <item name="save" xsi:type="string">iDesign\Correctemail\Block\Adminhtml\Buttons\Save</item>
        <item name="update" xsi:type="array">
            <item name="name" xsi:type="string">update</item>
            <item name="label" xsi:type="string">Save Settings</item>
            <item name="class" xsi:type="string">primary</item>
            <item name="url" xsi:type="string">*/*/save</item>
        </item>
    </item>
</argument>

<dataSource name="correctemail_settings_form_data_source">
    <argument name="dataProvider" xsi:type="configurableObject">
        <argument name="class" xsi:type="string">iDesign\Correctemail\Model\Settings\DataProvider</argument>
        <argument name="name" xsi:type="string">correctemail_settings_form_data_source</argument>
        <argument name="primaryFieldName" xsi:type="string">settings_id</argument>
        <argument name="requestFieldName" xsi:type="string">settings_id</argument>
        <argument name="data" xsi:type="array">
            <item name="js_config" xsi:type="array">
                <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item>
            </item>
            <item name="config" xsi:type="array">
                <item name="submit_url" xsi:type="url" path="*/*/*"/>
            </item>
        </argument>
    </argument>
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="component"     xsi:type="string">Magento_Ui/js/form/provider</item>
        </item>
    </argument>
</dataSource>
<fieldset>
  ...
</fieldset>

Block/Adminhtml/Buttons/Save.php

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

class Save implements ButtonProviderInterface
{
  public function getButtonData()
  {
    return [
        'label' => 'Save',
        'class' => 'save primary',
        'data_attribute' => [
            'mage-init' => [
                'button' => ['event' => 'save'],
            ],
        ],
        'sort_order' => 80,
    ];
  }
}

GenericButton.php

class GenericButton
{
  /**
   * @var Context
   */
  protected $context;

  /**
   * @param Context $context
   */
  public function __construct(Context $context)
  {
    $this->context = $context;
  }

  /**
   * Return Settings ID
   *
   * @return int
   */
  public function getSettingsId()
  {
    return 1;
  }

  /**
   * Generate url by route and parameters
   *
   * @param   string $route
   * @param   array $params
   * @return  string
   */
  public function getUrl($route = '', $params = [])
  {
    return $this->context->getUrlBuilder()->getUrl($route, $params);
  }
}

When I Inspect button html I have inside

onclick="location.href=... myModuleName/index/index ... (why?)

Other thing is. Why after reload my url gets at the end /undefined string

mage.dev/index.php/admin/ModuleName/settings/index/settings_id/1/key/9a71fd4a4f84367a354a134e399addc67dd95868a29651b37f6a4083ecd0fc05/undefined

6 Answers 6

18

Yo, whats up guys. I just had to wrestle with this in 2.1.9 and think I may have a legit answer.

All of these answers are close, but none seem to be completely correct so far. I'll explain how I figured this out.

Unfortuantely, for any of us that have worked with any depth in Magento 2, likely we've come to realize that if you want to do anything... you need to be able to trace core code. Which... is difficult- with all the knockout , xml... fun.

Enough nonsense. Lets get to it.

First, save button:

<argument name="data" xsi:type="array">
    <item name="js_config" xsi:type="array">
        <item name="provider" xsi:type="string">educationfiles_edit.educationfiles_edit_data_source</item>
        <item name="deps" xsi:type="string">educationfiles_edit.educationfiles_edit_data_source</item>
    </item>

    <item name="layout" xsi:type="array">
        <item name="type" xsi:type="string">tabs</item>
    </item>

    <item name="buttons" xsi:type="array">
        <item name="manage" xsi:type="array">
            <item name="name" xsi:type="string">manage</item>
            <item name="label" xsi:type="string" translate="true">Manage</item>
            <item name="class" xsi:type="string">manage</item>
            <item name="url" xsi:type="string">*/*/</item>
        </item>
        <item name="save" xsi:type="array">
            <item name="name" xsi:type="string">save</item>
            <item name="label" xsi:type="string" translate="true">save</item>
            <item name="class" xsi:type="string">primary</item>
        </item>
    </item>
</argument>

Within your top data block, shown above, you have the buttons section. Here your button is defined, note that there is no route defined on it... for some reason you don't need it. Meaning you DO NOT need:

<item name="url" xsi:type="string">*/*/save</item>

Like the manage button has above it. I'm guessing that the form expects the item named save to fire the submit_url. Whats the submit_url, right, onto that.

Within your datasource:

<dataSource name="educationfiles_edit_data_source">
    <argument name="dataProvider" xsi:type="configurableObject">
        <argument name="class" xsi:type="string">Augustash\EducationFiles\Model\Upload\DataProvider</argument>
        <argument name="name" xsi:type="string">educationfiles_edit_data_source</argument>
        <argument name="primaryFieldName" xsi:type="string">entity_id</argument>
        <argument name="requestFieldName" xsi:type="string">id</argument>
    </argument>
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
            <item name="submit_url" path="*/*/save" xsi:type="url" />
        </item>
    </argument>
</dataSource>

Take note of:

<item name="submit_url" path="*/*/save" xsi:type="url" />

This will cause your save button to fire the defined path.

That solves the problem, for extra credit continue on.

How the hell did I figure this out? This is the most important part. Take note of the js_config definition within our datasource block:

<item name="js_config" xsi:type="array">
    <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
    <item name="submit_url" path="*/*/save" xsi:type="url" />
</item>

This means that Magento is using 'Magento_Ui/js/form/provider' to process this form block. Where is that file...?

'Magento_Ui' is a core module. It is located where all the core files are within your 'vendor' folder at the web root. Within 'vendor' the directory will be named 'module-[modulename]', because of '_Ui'.

Magento_Ui

vendor/magento/module-ui

Knockout files are within 'view', then the respective area- meaning 'base', 'adminhtml', or 'frontend', then the 'web' directory.

vendor/magento/module-ui/view/[area]

From there the path is 'vendor/magento/module-ui/view/base/web/js/form/provider.js'.

Magento_Ui/js/form/provider

vendor/magento/module-ui/view/base/web/js/form/provider.js

Meaning you have to know knockout files are in 'view/[area]/web', then you can piece together the actual file path.

Within provider.js you can find what Magento is looking for, to give yourself a clue as to why/what you are defining in your xml. In this case:

defaults: {
    clientConfig: {
        urls: {
            save: '${ $.submit_url }',
            beforeSave: '${ $.validate_url }'
        }
    }
},

This tells us that provider.js save is looking for a 'submit_url' definition. Which means the 'name' of an xml definition should be that, in our case:

<item name="submit_url" path="*/*/save" xsi:type="url" />

The astute among you will realize that this means we can also define a validation url with 'validate_url'. I haven't tested that... we probly shouldn't assume that it works- but its intended to work, lol. Magento.

Then we know this is javascript, so we can guess that this definition should be set within the 'js_config':

<argument name="data" xsi:type="array">
    <item name="js_config" xsi:type="array">
        <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
        <item name="submit_url" path="*/*/save" xsi:type="url" />
    </item>
</argument>

In total, our form data and datasource xml:

<argument name="data" xsi:type="array">
    <item name="js_config" xsi:type="array">
        <item name="provider" xsi:type="string">educationfiles_edit.educationfiles_edit_data_source</item>
        <item name="deps" xsi:type="string">educationfiles_edit.educationfiles_edit_data_source</item>
    </item>

    <item name="layout" xsi:type="array">
        <item name="type" xsi:type="string">tabs</item>
    </item>

    <item name="buttons" xsi:type="array">
        <item name="manage" xsi:type="array">
            <item name="name" xsi:type="string">manage</item>
            <item name="label" xsi:type="string" translate="true">Manage</item>
            <item name="class" xsi:type="string">manage</item>
            <item name="url" xsi:type="string">*/*/</item>
        </item>
        <item name="save" xsi:type="array">
            <item name="name" xsi:type="string">save</item>
            <item name="label" xsi:type="string" translate="true">save</item>
            <item name="class" xsi:type="string">primary</item>
        </item>
    </item>
</argument>

<dataSource name="educationfiles_edit_data_source">
    <argument name="dataProvider" xsi:type="configurableObject">
        <argument name="class" xsi:type="string">Augustash\EducationFiles\Model\Upload\DataProvider</argument>
        <argument name="name" xsi:type="string">educationfiles_edit_data_source</argument>
        <argument name="primaryFieldName" xsi:type="string">entity_id</argument>
        <argument name="requestFieldName" xsi:type="string">id</argument>
    </argument>
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
            <item name="submit_url" path="*/*/save" xsi:type="url" />
        </item>
    </argument>
</dataSource>

Obviously you'll have to replace various details within this code for it to work.

3
  • Wow thank you for your very detailed answer that goes far beyond the usual "trust me and copy/paste that random piece of code".
    – bfontaine
    Commented Apr 20, 2021 at 9:39
  • Underrated reply. Thank you man, this fixed my Issue. I had the "suubmit_url" thing in my XML and it didn't work. But I had that in the "config" array, instead of the "js_config" array.
    – Zankar
    Commented Oct 6, 2023 at 11:09
  • Wow, 7 years later and this is still useful. I'm not sure thats a good thing, haha!
    – KazaJhodo
    Commented Oct 7, 2023 at 16:47
3

first remove this part in xml you have write every button block. you can see vendor/magento/module-cms/view/adminhtml/ui_component/cms_block_form.xml

    <item name="update" xsi:type="array">
        <item name="name" xsi:type="string">update</item>
        <item name="label" xsi:type="string">Save Settings</item>
        <item name="class" xsi:type="string">primary</item>
        <item name="url" xsi:type="string">*/*/save</item>
    </item>

second you block should extend with

class Save extends GenericButton implements ButtonProviderInterface 

in dataSource you have to add submit url

<argument name="data" xsi:type="array"> 
  <item name="config" xsi:type="array"> 
     <item name="submit_url" xsi:type="url" path="test/test/save"/> 
  </item> 

7
  • Yes my button implements ButtonProviderInterface but not extends GenericButton must I create any? In vendor/magento/module-cms/ I saw the request is supported with redirect in controller. It isn't ajax request? Commented Oct 6, 2016 at 9:49
  • yes you have to extends and after your are redirect too save controller. Commented Oct 6, 2016 at 10:00
  • Still same problem undefined at the end of url ... location.href in button with adress index/index (non-existent controller) How magento know to proceed methods in GenericButton? I'm still not redirected to save controller Commented Oct 6, 2016 at 10:23
  • i didn't change the url after pressing the it's redirect to save. it always show the current page url it is not a error. Commented Oct 6, 2016 at 10:41
  • index/index it isn't actual page(controller) even. So should be settings/index? I'm curious mostly why it ads "/undefinded" after reload page. What magento is looking for? What should I configure further. Commented Oct 6, 2016 at 10:55
1

Add to SaveButton.php return data:

'on_click' => ''

and to settings_form.xml:

<argument name="dataProvider" xsi:type="configurableObject">
   ...
   // Other Child nodes
   ...
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="submit_url" xsi:type="url" path="path/to/controller/save"/>
        </item>
    </argument>
</argument>
1
  • This is the most relevant answer. I would flag it as accepted.
    – zhartaunik
    Commented Apr 12, 2022 at 18:16
1

Step-1: Create Generic.php at app/code/VendoreName/ModuleName/Block/Adminhtml/Edit/Button

<?php

namespace VendoreName\ModuleName\Block\Adminhtml\Edit\Button;

use Magento\Backend\Block\Widget\Context;
use Magento\Cms\Api\PageRepositoryInterface;

class Generic
{
    protected $context;
    protected $pageRepository;

    public function __construct(
        Context $context,
        PageRepositoryInterface $pageRepository
    ) {
        $this->context = $context;
        $this->pageRepository = $pageRepository;
    }

    public function getUrl($route = '', $params = [])
    {
        return $this->context->getUrlBuilder()->getUrl($route, $params);
    }
}

Step-2: Create Save.php at app/code/VendoreName/ModuleName/Block/Adminhtml/Edit/Button

<?php

namespace VendoreName\ModuleName\Block\Adminhtml\Edit\Button;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
use Magento\Ui\Component\Control\Container;

class Save extends Generic implements ButtonProviderInterface
{
    public function getButtonData()
    {
        return [
            'label' => __('Save'),
            'class' => 'save primary',
            'data_attribute' => [
                'mage-init' => [
                    'buttonAdapter' => [
                        'actions' => [
                            [
                                'targetName' => 'your_form_ui_component_name.your_form_ui_component_name',
                                'actionName' => 'save',
                                'params' => [
                                    false,
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ];
    }

}

Step-3: In your Form Ui-Component file at app/code/VendoreName/ModuleName/view/adminhtml/ui_component add below code:

<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <argument name="data" xsi:type="array">
        ............................................................................................................
        <item name="buttons" xsi:type="array">
            <item name="save" xsi:type="string">VendoreName\ModuleName\Block\Adminhtml\Edit\Button\Save</item>
        </item>
        ............................................................................................................
    </argument>
    <dataSource name="your_form_data_source_name">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">VendoreName\ModuleName\Model\DataProvider</argument>
            <argument name="name" xsi:type="string">your_form_data_source_name</argument>
            <argument name="primaryFieldName" xsi:type="string">id</argument>
            <argument name="requestFieldName" xsi:type="string">id</argument>
            <argument name="data" xsi:type="array">
              <item name="config" xsi:type="array">
                 <item name="submit_url" xsi:type="url" path="*/*/save"/>
              </item>
            </argument>
        </argument>
        <argument name="data" xsi:type="array">
            <item name="js_config" xsi:type="array">
                <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
            </item>
        </argument>
    </dataSource>
    ................................................................................................................
</form>

After adding above files run below command:

php bin/magento s:up
php bin/magento c:c
0

I also encountered a similar problem. After a lot of trial and error what helped me is to shorten the names of the provider and to get rid of redundant underscore (not sure which one of them helped). In your case, I would suggest changing

settings_form.correctemail_settings_form_data_source to settings_form.correctemailsettings_form_data_source

if that doesn't help, try shortening it to something like settings_form.cesettings_form_data_source

Hope that I helped somebody.

0

I was too facing the issue that despite doing everything as per core still my save button wasn't working.

While googling I found this link : https://www.pierrefay.com/magento2-training/form-component-backend-crud-admin.html

And when I did exactly like this page told me. The save button worked.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.