I try to turn on inline edit in catalog grid as described in DevDocs: http://devdocs.magento.com/guides/v2.0/ui-components/ui-secondary-inline.html
The main problem is how to override this block of configuration in
\vendor\magento\module-catalog\view\adminhtml\ui_component\product_listing.xml
<item name="childDefaults" xsi:type="array">
<item name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">product_listing.product_listing.product_columns.actions</item>
<item name="target" xsi:type="string">applyAction</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">edit</item>
<item name="1" xsi:type="string">${ $.$data.rowIndex }</item>
</item>
</item>
</item>
with custom "params" required for inline edit in
\app\code\MyVendor\MyModule\view\adminhtml\ui_component\product_listing.xml
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">${ $.$data.rowIndex }</item>
<item name="1" xsi:type="boolean">true</item>
</item>
It throws error while loading configuration by the core framework function
**Magento\Ui\Model\Manager->evaluateComponentArguments()**
I don't know how to cure it except overriding framework components such as
Magento\Ui\Model\Manager or Magento\Framework\View\Layout\Generator\UiComponent,
but it's a bad idea
Another decision is to comment "fieldAction" item in the original Magento product_listing.xml
With this modification inline edit works.
Here is the full sample code :
\app\code\MyVendor\MyModule\etc\adminhtml\di.xml
<preference for="Magento\Catalog\Ui\Component\Listing\Columns" type="MyVendor\MyModule\Magento\Catalog\Ui\Component\Listing\Columns" />
\app\code\MyVendor\MyModule\etc\adminhtml\routes.xml
<router id="admin">
<route id="my_product" frontName="my_product">
<module name="MyVendor_MyModule"/>
</route>
</router>
Modified original magento config:
\vendor\magento\module-catalog\view\adminhtml\ui_component\product_listing.xml
------
<item name="childDefaults" xsi:type="array">
<!--<item name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">product_listing.product_listing.product_columns.actions</item>
<item name="target" xsi:type="string">applyAction</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">edit</item>
<item name="1" xsi:type="string">${ $.$data.rowIndex }</item>
</item>
</item>-->
</item>
--------
Custom config:
\app\code\MyVendor\MyModule\view\adminhtml\ui_component\product_listing.xml
<dataSource name="product_listing_data_source"></dataSource>
<listingToolbar name="listing_top"></listingToolbar>
<columns name="product_columns" class="Magento\Customer\Ui\Component\Listing\Columns">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="editorConfig" xsi:type="array">
<item name="selectProvider" xsi:type="string">product_listing.product_listing.product_columns.ids</item>
<item name="enabled" xsi:type="boolean">true</item>
<item name="indexField" xsi:type="string">entity_id</item>
<item name="clientConfig" xsi:type="array">
<item name="saveUrl" xsi:type="url" path="my_product/product/inlineEdit"/>
<item name="validateBeforeSave" xsi:type="boolean">false</item>
</item>
</item>
<item name="childDefaults" xsi:type="array">
<item name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">product_listing.product_listing.product_columns_editor</item>
<item name="target" xsi:type="string">startEdit</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">${ $.$data.rowIndex }</item>
<item name="1" xsi:type="boolean">true</item>
</item>
</item>
</item>
</item>
</argument>
<column name="name">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="editor" xsi:type="string">text</item>
</item>
</argument>
</column>
<column name="my_pku">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="editor" xsi:type="string">text</item>
</item>
</argument>
</column>
----- other columns ----
</columns>
\app\code\MyVendor\MyModule\Controller\Adminhtml\Product.php
namespace MyVendor\MyModule\Controller\Adminhtml;
use Magento\Backend\App\Action;
abstract class Product extends \Magento\Backend\App\Action {
const ADMIN_RESOURCE = 'Magento_Catalog::products';
public function __construct(
\Magento\Backend\App\Action\Context $context
) {
parent::__construct($context);
}
}
\app\code\MyVendor\MyModule\Controller\Adminhtml\Product\InlineEdit.php
namespace MyVendor\MyModule\Controller\Adminhtml\Product;
use Magento\Backend\App\Action;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
class InlineEdit extends \Magento\Backend\App\Action {
const ADMIN_RESOURCE = 'Magento_Catalog::products';
private $product;
protected $productRepository;
protected $resultJsonFactory;
protected $dataObjectHelper;
protected $logger;
public function __construct(
Action\Context $context, ProductRepositoryInterface $productRepository,
\Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
\Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
\Psr\Log\LoggerInterface $logger
) {
$this->productRepository = $productRepository;
$this->resultJsonFactory = $resultJsonFactory;
$this->dataObjectHelper = $dataObjectHelper;
$this->logger = $logger;
parent::__construct($context);
}
public function execute() {
$resultJson = $this->resultJsonFactory->create();
$postItems = $this->getRequest()->getParam('items', []);
if (!($this->getRequest()->getParam('isAjax') && count($postItems))) {
return $resultJson->setData([
'messages' => [__('Please correct the data sent.')],
'error' => true,
]);
}
foreach ($postItems as $productId => $productRow) {
$this->setProduct($this->productRepository->getById($productId));
$data = $this->product->getData();
// ---- formatting and validating
// $this->my_server_side_validator($data);
// $this->my_server_side_formatter($data);
// saving
$dataUpdated = array_replace($data, $productRow);
$this->product->setData($dataUpdated);
$this->product->save();
}
return $resultJson->setData([
'messages' => $this->getErrorMessages(),
'error' => $this->isErrorExists()
]);
}
protected function getErrorMessages() {
$messages = [];
foreach ($this->getMessageManager()->getMessages()->getItems() as $error) {
$messages[] = $error->getText();
}
return $messages;
}
protected function isErrorExists() {
return (bool) $this->getMessageManager()->getMessages(true)->getCount();
}
protected function setProduct(ProductInterface $product) {
$this->product = $product;
return $this;
}
protected function getProduct() {
return $this->product;
}
}
\app\code\MyVendor\MyModule\Magento\Catalog\Ui\Component\Listing\Columns.php
namespace MyVendor\MyModule\Magento\Catalog\Ui\Component\Listing;
class Columns extends \Magento\Catalog\Ui\Component\Listing\Columns {
// Array of attributes not included in
// \vendor\magento\module-catalog\view\adminhtml\ui_component\product_listing.xml
protected $additional_fields = [
'my_pku'
];
public function __construct(
\Magento\Framework\View\Element\UiComponent\ContextInterface $context,
\Magento\Catalog\Ui\Component\ColumnFactory $columnFactory,
\Magento\Catalog\Ui\Component\Listing\Attribute\RepositoryInterface $attributeRepository,
array $components = [], array $data = []
) {
parent::__construct($context, $columnFactory, $attributeRepository,
$components, $data);
}
/**
* {@inheritdoc}
*/
public function prepare() {
$columnSortOrder = self::DEFAULT_COLUMNS_MAX_ORDER;
foreach ($this->attributeRepository->getList() as $attribute) {
$attr_code = $attribute->getAttributeCode();
$config = [];
if (!isset($this->components[$attr_code]) || in_array($attr_code,
$this->additional_fields)) {
$config['sortOrder'] = ++$columnSortOrder;
if ($attribute->getIsFilterableInGrid()) {
$config['filter'] = $this->getFilterType($attribute->getFrontendInput());
}
// Copy editor configuration for additional attributes
if (isset($this->components[$attr_code]->_data['config']['editor'])) {
$config['editor'] = $this->components[$attr_code]->_data['config']['editor'];
}
$column = $this->columnFactory->create($attribute,
$this->getContext(), $config);
$column->prepare();
$this->addComponent($attribute->getAttributeCode(), $column);
}
}
parent::prepare();
}
}
I need to extend Column.php because custom product_listing.xml also can't add editor config for additional product attributes like "my_pku" code, that not included in original Magento product_listing.xml
Without this, I can inline edit only predefined fields in original Magento product_listing.xml
So, QUESTIONS are:
How to enable catalog inline edit without modifying \vendor\magento\module-catalog\view\adminhtml\ui_component\product_listing.xml ?
What is an acceptable method to replace original ui_component config XML files safely, when item types are different?
UPDATE:
to Pramod Kumar Sharma
In 2.1.6 I had to comment out this block of code in
\vendor\magento\module-catalog\view\adminhtml\ui_component\product_listing.xml
------
<item name="childDefaults" xsi:type="array">
<!--<item name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">product_listing.product_listing.product_columns.actions</item>
<item name="target" xsi:type="string">applyAction</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">edit</item>
<item name="1" xsi:type="string">${ $.$data.rowIndex }</item>
</item>
</item>-->
</item>
--------
In 2.2.1 it works without commenting out.
I have no separate extension for this, it's part of my module with many other improvements
Just do as described above
-
Have you completed the inline edit in product grid ?Pramod Kumar Sharma– Pramod Kumar Sharma2018年01月09日 05:54:05 +00:00Commented Jan 9, 2018 at 5:54
-
yes, it works. And after upgrade from 2.1.6 to 2.2.1 it works without modifying \vendor\magento\module-catalog\view\adminhtml\ui_component\product_listing.xmluser50138– user501382018年01月15日 01:22:01 +00:00Commented Jan 15, 2018 at 1:22
-
Can you please send me the extension so i can refer that extension.Pramod Kumar Sharma– Pramod Kumar Sharma2018年01月15日 05:02:19 +00:00Commented Jan 15, 2018 at 5:02
-
How you override the product_listing.xml in your module can you please explain me. It will be helpful for me. ThanksPramod Kumar Sharma– Pramod Kumar Sharma2018年01月19日 05:59:28 +00:00Commented Jan 19, 2018 at 5:59
-
Pramod Kumar Sharma, I've updated postuser50138– user501382018年01月23日 05:34:47 +00:00Commented Jan 23, 2018 at 5:34
1 Answer 1
I have found the way for it. I have overridden all the files.I have done inline edit for below attributes.
-- brand, sku, name, price, visibility, status
Also i have done it for below custom dropdown attributes which are
-- supplier , show on home page or not(boolean)
Please follow below steps
Step 1 Show attributes in grid
PackageName/ProductInlineEdit/view/adminhtml/ui_component/product_listing.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<listingToolbar name="listing_top">
<massaction name="listing_massaction" component="Magento_Ui/js/grid/tree-massactions" class="\Magento\Catalog\Ui\Component\Product\MassAction">
<action name="edit">
<settings>
<callback>
<target>editSelected</target>
<provider>product_listing.product_listing.product_columns_editor</provider>
</callback>
<type>edit</type>
<label translate="true">Edit</label>
</settings>
</action>
</massaction>
</listingToolbar>
<columns name="product_columns" class="Magento\Catalog\Ui\Component\Listing\Columns">
<settings>
<editorConfig>
<param name="clientConfig" xsi:type="array">
<item name="saveUrl" xsi:type="url" path="productinlineedit/product/inlineEdit"/>
<item name="validateBeforeSave" xsi:type="boolean">false</item>
</param>
<param name="indexField" xsi:type="string">entity_id</param>
<param name="enabled" xsi:type="boolean">true</param>
<param name="selectProvider" xsi:type="string">product_listing.product_listing.product_columns.ids</param>
</editorConfig>
<childDefaults>
<param name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">product_listing.product_listing.product_columns_editor</item>
<item name="target" xsi:type="string">startEdit</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">${ $.$data.rowIndex }</item>
<item name="1" xsi:type="boolean">true</item>
</item>
</param>
</childDefaults>
</settings>
<column name="name" sortOrder="30">
<settings>
<editor>
<editorType>text</editorType>
</editor>
</settings>
</column>
<column name="sku" sortOrder="60">
<settings>
<editor>
<editorType>text</editorType>
</editor>
</settings>
</column>
<column name="price" class="Magento\Catalog\Ui\Component\Listing\Columns\Price" sortOrder="70">
<settings>
<editor>
<editorType>text</editorType>
</editor>
</settings>
</column>
<column name="visibility" component="Magento_Ui/js/grid/columns/select" sortOrder="80">
<settings>
<editor>
<editorType>select</editorType>
</editor>
</settings>
</column>
<column name="status" component="Magento_Ui/js/grid/columns/select" sortOrder="90">
<settings>
<editor>
<editorType>select</editorType>
</editor>
</settings>
</column>
</columns>
</listing>
Step 2 Remove cursor over the fields
PackageName/ProductInlineEdit/view/adminhtml/layout/default.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<css src="PackageName_ProductInlineEdit::css/inlineedit.css"/>
</head>
</page>
Add css file in below path:-
PackageName/ProductInlineEdit/view/adminhtml/web/css/inlineedit.css
.catalog-product-index .admin__data-grid-wrap .data-row{cursor: default !important;}
Step 3 Inline edit fields with the use of plugin method
PackageName/ProductInlineEdit/etc/adminhtml/di.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
<type name="Magento\Catalog\Ui\Component\ColumnFactory">
<plugin name="packagename.product.inlineedit" type="PackageName\ProductInlineEdit\Plugin\Component\Listing\ColumnFactory" sortOrder="10" disabled="false" />
</type>
</config>
Create plugin file in path :-
PackageName\ProductInlineEdit\Plugin\Component\Listing\ColumnFactory
<?php
namespace PackageName\ProductInlineEdit\Plugin\Component\Listing;
/**
* {@inheritdoc}
*/
class ColumnFactory {
/**
* @var array
*/
protected $editorMap = [
'default' => 'text',
'select' => 'select',
'boolean' => 'select',
'multiselect' => 'select',
'date' => 'dateRange',
];
/**
* Add Inline Edit for custom Attributes
*
* @param $subject
* @param $attribute
* @param $context
* @param $config
*/
public function beforeCreate(\Magento\Catalog\Ui\Component\ColumnFactory $subject, $attribute, $context, array $config = [])
{
$editorType = $attribute->getFrontendInput();
if(isset($this->editorMap[$editorType])){
$editorType = $this->editorMap[$editorType];
}
$config['editor'] = ['editorType'=> $editorType];
return [$attribute, $context, $config];
}
}
Step 4 Create js file to control columns
PackageName/ProductInlineEdit/view/adminhtml/requirejs-config.js
var config =
{
map:
{
'*':
{
'Magento_Ui/js/grid/controls/columns':'PackageName_ProductInlineEdit/js/grid/controls/columns'
}
}
};
PackageName/ProductInlineEdit/view/adminhtml/web/js/grid/controls/columns.js
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
/**
* @api
*/
define([
'underscore',
'mageUtils',
'mage/translate',
'uiCollection'
], function (_, utils, $t, Collection) {
'use strict';
return Collection.extend({
defaults: {
template: 'ui/grid/controls/columns',
minVisible: 1,
maxVisible: 30,
viewportSize: 18,
displayArea: 'dataGridActions',
columnsProvider: 'ns = ${ $.ns }, componentType = columns',
imports: {
addColumns: '${ $.columnsProvider }:elems'
},
templates: {
headerMsg: $t('${ $.visible } out of ${ $.total } visible')
}
},
/**
* Resets columns visibility to theirs default state.
*
* @returns {Columns} Chainable.
*/
reset: function () {
this.elems.each('applyState', 'default', 'visible');
return this;
},
/**
* Applies last saved state of columns visibility.
*
* @returns {Columns} Chainable.
*/
cancel: function () {
//this.elems.each('applyState', '', 'visible');
return this;
},
/**
* Adds columns whose visibility can be controlled to the component.
*
* @param {Array} columns - Elements array that will be added to component.
* @returns {Columns} Chainable.
*/
addColumns: function (columns) {
columns = _.where(columns, {
controlVisibility: true
});
this.insertChild(columns);
return this;
},
/**
* Defines whether child elements array length
* is greater than the 'viewportSize' property.
*
* @returns {Boolean}
*/
hasOverflow: function () {
return this.elems().length > this.viewportSize;
},
/**
* Helper, checks
* - if less than one item choosen
* - if more then viewportMaxSize choosen
*
* @param {Object} elem
* @returns {Boolean}
*/
isDisabled: function (elem) {
var visible = this.countVisible();
return elem.visible ?
visible === this.minVisible :
visible === this.maxVisible;
},
/**
* Counts number of visible columns.
*
* @returns {Number}
*/
countVisible: function () {
return this.elems.filter('visible').length;
},
/**
* Compile header message from headerMessage setting.
*
* @returns {String}
*/
getHeaderMessage: function () {
return utils.template(this.templates.headerMsg, {
visible: this.countVisible(),
total: this.elems().length
});
}
});
});
Step 5 Save inline data with the use of custom controller. We have already defined controller path in product_listing.xml file
Create router file to define controller name:-
PackageName/ProductInlineEdit/etc/adminhtml/routes.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="admin">
<route id="productinlineedit" frontName="productinlineedit">
<module name="PackageName_ProductInlineEdit" />
</route>
</router>
</config>
Create controller file in below path :-
PackageName/ProductInlineEdit/Controller/Adminhtml/Product/InlineEdit.php
<?php
namespace PackageName\ProductInlineEdit\Controller\Adminhtml\Product;
class InlineEdit extends \Magento\Backend\App\Action
{
/**
* @var \Magento\Framework\Controller\Result\JsonFactory
*/
protected $jsonFactory;
/**
* @var \Magento\Catalog\Model\ProductFactory $productFactory
*/
protected $productFactory;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Controller\Result\JsonFactory $jsonFactory
* @param \Magento\Catalog\Model\ProductFactory $productFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Controller\Result\JsonFactory $jsonFactory,
\Magento\Catalog\Model\ProductFactory $productFactory
) {
parent::__construct($context);
$this->jsonFactory = $jsonFactory;
$this->_productFactory = $productFactory;
}
/**
* @return \Magento\Framework\Controller\Result\JsonFactory
*/
public function execute()
{
$resultJson = $this->jsonFactory->create();
$error = false;
$messages = [];
$postItems = $this->getRequest()->getParam('items', []);
if (!($this->getRequest()->getParam('isAjax') && count($postItems))) {
return $resultJson->setData([
'messages' => [__('Please correct the data sent.')],
'error' => true,
]);
}
foreach ($postItems as $value)
{
if ($value) {
$productObj = $this->_productFactory->create();
if ($value['entity_id']) {
$productObj->load($value['entity_id']);
}
$productObj->setStoreId(0);
if(array_key_exists('name', $value)) {
$productObj->setName($value['name']);
}
if(array_key_exists('sku', $value)) {
$productObj->setSku($value['sku']);
}
if(array_key_exists('price', $value)) {
$productObj->setPrice($value['price']);
}
if(array_key_exists('visibility', $value)) {
$productObj->setVisibility($value['visibility']);
}
if(array_key_exists('status', $value)) {
$productObj->setStatus($value['status']);
}
if(array_key_exists('brand', $value)) {
$productObj->setBrand($value['brand']);
}
if(array_key_exists('showinhome', $value)) {
$productObj->setShowinhome($value['showinhome']);
}
if(array_key_exists('supplier_id', $value)) {
$productObj->setSupplierId($value['supplier_id']);
}
try {
$productObj->save();
} catch (\Magento\Framework\Model\Exception $e) {
$this->messageManager->addError($e->getMessage());
}
}
}
return $resultJson->setData([
'messages' => $messages,
'error' => $error
]);
}
}
Explore related questions
See similar questions with these tags.