Magento 2 - Alan Storm 博客 - 介绍 UI 组件

超级版主组 前端小威 1月前 111

来源: https://alanstorm.com/magento_2_introducing_ui_components/


返回文章列表


UI 组件是组建用户界面元素的一个强有力的方法,并且很多新的后台控制台都建立在这个功能之上。今天我们将深入了解 UI 组件系统的目标,包括尽可能地深入了解执行细节,并且最后结束会用 pestle 生成 grid/listing UI 组件。


从理论上来讲

理解 UI 组件目的最简单的方法是讨论 Magento 1 的后台用户界面形成的代码。这是一个 Magento 1 布局更新 XML 代码的例子。

<!-- #File: app/design/adminhtml/default/default/layout/catalog.xml -->
<adminhtml_catalog_product_new>
    <update handle="editor"/>
    <reference name="content">
        <block type="adminhtml/catalog_product_edit" name="product_edit"></block>
    </reference>
    <reference name="left">
        <block type="adminhtml/catalog_product_edit_tabs" name="product_tabs"></block>
    </reference>
    <reference name="js">
        <block type="adminhtml/catalog_product_edit_js" template="catalog/product/js.phtml" name="catalog_product_js"></block>
        <block type="core/template" template="catalog/wysiwyg/js.phtml"/>
    </reference>
</adminhtml_catalog_product_new>


那是 4 个添加到产品编辑表单中的单独的布局更新 XML 节点。如果你考虑可重用的 <update handle="editor"> 节点之后布局更新 XML。
<!-- #File: app/design/adminhtml/default/default/layout/main.xml -->
<editor>
    <reference name="head">
        <action method="setCanLoadExtJs"><flag>1</flag></action>
        <action method="addJs"><script>mage/adminhtml/variables.js</script></action>
        <action method="addJs"><script>mage/adminhtml/wysiwyg/widget.js</script></action>
        <action method="addJs"><script>lib/flex.js</script></action>
        <action method="addJs"><script>lib/FABridge.js</script></action>
        <action method="addJs"><script>mage/adminhtml/flexuploader.js</script></action>
        <action method="addJs"><script>mage/adminhtml/browser.js</script></action>
        <action method="addJs"><script>prototype/window.js</script></action>
        <action method="addItem"><type>js_css</type><name>prototype/windows/themes/default.css</name></action>
        <action method="addCss"><name>lib/prototype/windows/themes/magento.css</name></action>
    </reference>
</editor>

你看给一个页面添加产品编辑表单甚至更复杂。


UI 组件背后的目的就是想隐藏这种复杂。Magento 2 为它的布局句柄 XML 文件引入了一个新的 <uiComponent/> 标签(Magento 2 句柄 XML 文件和 Magento 1 布局更新 XML 文件相似)。在 Magento 2 中,你可以按照以下配置给一个页面添加产品编辑表单。

<uiComponent name="product_form"/>
通过引入 <uiComponent> 这一概念,Magento 2 让在不同的地方重用不同的组件变得更容易。虽然可以在不同地方放置 Mangento 1 UI 表单和栅格,但是你需要知道这个特定的组件由哪个 block 和 JavaScript 文件组成的。Mangento 1 的方法偶然间让设置一个网格列表和表单变的容易,所以组件几乎就生效了。


Magento 2 的 UI 组件打算解决这个难题,并且大大地简化了每个人的布局句柄 XML 文件。


实际情况
虽然刚我们说到一切已经足够了,但现实中的 UI 组件系统并没有蓝图中那么美好。这是因为 UI 组件系统还有其他几个目的,并且这些目标带来了相当大的复杂度。
就我所知,UI 组件系统
  • 简化了布局句柄 XML 文件
  • 把管理用户界面元素从 HTML+JavaScript 变成了“纯 JavaScript” 自定义组件系统
  • 是由更小的组件建造更复杂的 UI 组件的系统
  • 作为 JSON 为 UI 组件预渲染数据,跟 Mangento 后台数据对象紧密地绑定在一起
  • 使用 AJAX 来更新数据
  • 引入一个新的 DSL 来创建以上所有
UI 组件系统是一个有雄心的系统,像 Magento 2 中的很多东西一样,他还没有特别完善。虽然你可能想要远离一个没有完全成型的系统,但大多数的核心网格和很多表单使用 UI 组件系统来渲染他们的界面元素,并且其他的使用传统 block 和 JavaScript 文件混合完成的。如果你想要建立一个功能完全齐全的模块,你将需要使用 UI 组件系统来实现。
这篇文章剩下的部分代表了我此时对 UI 组件的最好的理解(Magento 2.1 时代)。许多细节在未来可能改变,但是希望核心思想会保持一样。对于想要(或需要)开发后台用户界面元素的开发者来说,没有好的标准措施,和往常一样最好是去看核心团队用他们自己的组件在做什么,模仿它,并且无论何时 Magneto 版本有更新,都密切关注你的模块或者扩展代码。

除非你对复杂的执行细节感兴趣,不然你可能想要直接跳到最后我们用 pestle 创建一个 UI 组件的地方。


纯 JavaScript
在 Magento 2 的后台,如果你去到 Content -> Block ,你会看到一个在你 Magento 系统中的所有 CMS Block 的网格列表。如果你对 Block 不熟悉,他们是为你的店铺创建有用的大块 HTML 的方式。Block 信息使用 CRUD 模块存储在 Magneto 的后台。
你看到的列表是一个 UI 组件,用以下布局句柄 XML 配置的:
<!-- File: vendor/magento/module-cms/view/adminhtml/layout/cms_block_index.xml -->
<!-- ... -->
<referenceContainer name="content">
    <uiComponent name="cms_block_listing"/>
</referenceContainer>
<!-- ... -->
如果你对 Magento 的布局 XML 是个新手,那么对于上面内容的平白解读:获取已创建的名为 content 的容器的引用,并且给它添加 cms_block_listing UI 组件。


如果你查看未解析的 HTML 页面资源,<uiComponent> 标签代表渲染以下 HTML。

<div class="admin__data-grid-outer-wrap" data-bind="scope: 'cms_block_listing.cms_block_listing'">
    <div data-role="spinner" data-component="cms_block_listing.cms_block_listing.cms_block_columns" class="admin__data-grid-loading-mask">
        <div class="spinner">
            <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
        </div>
    </div>
    <!-- ko template: getTemplate() --><!-- /ko -->        
    <script type="text/x-magento-init">
        {"*": {"Magento_Ui/js/core/app": {...very large js object...}}}    
    </script>
</div>
如果你完成 Magento 2 JavaScript 进阶 系列,尤其是 初始化 JavaScript 教程,你会知道 x-magneto-init script 标签将会把 Magento_Ui/js/core/app RequireJS 模块作为程序调用,把非常大的对象作为实参传入程序。
不深入执行细节(其中一些你可以在 Stack Exchange 回答中查看),JavaScript 代码最终创建一系列 JavaScript 构造函数对象, Magento 将其作为 KnockoutJS view model 来使用。
在浏览器中渲染界面元素的实际上是 KnockoutJS 。这个 HTML 骨架的外面的 div 使用 Magento 的自定义 KnockoutJS scop 绑定 来绑定一个由 text/x-magento-init JavaScript 创建的 view model 。

<div ... data-bind="scope: 'cms_block_listing.cms_block_listing'"></div>
然后,UI 组件的渲染由 KnockoutJS “无标签”模板绑定实现。
<!-- ko template: getTemplate() --><!-- /ko -->
getTemplate 的调用实际上触发了一些 KnockoutJS 嵌套的模板绑定 —— 从名为 collection.html 的文件开始。通过在浏览器的 XHR 调试窗口中查找 .html 文件,你可以找到这所有模板。如果你对 Magento 的扩展 KnockoutJS 模板到 XHR 或者任何其他 KnockoutJS 代码不熟悉,尝试阅读 Magento 2 的 JavaScript 进阶系列中的 KnockoutJS 集成的文章。同时要牢记 Magento 的核心团队用一些自定义的标签和属性改善了 KnockoutJS ,这可能有点迷惑。


总之,Magento 1 用 HTML 中渲染了一个列表,然后用 JavaScript 增强了用户界面的功能。然而 Magento 2 仍然使用一些 HTML 骨架,也把大部分的用户元素的渲染转换成了 RequireJS 模块和 KnockoutJS 模板。


子组件
如果你进一步查看 x-magento-init JSON 对象,你将会看到有一些嵌套的子 JavaScript 对象。
{
    "*": {
            "Magento_Ui/js/core/app": {
                  "types": /*...*/
                  "components": {
                             "cms_block_listing": {
                                   "children": {
                                            "cms_block_listing": {
                                                 /*...*/
                                                 "children": {
                                                     "listing_top": {
                                                             "type": "container",                                    
                                                             "name": "listing_top",                                    
                                                             "children": {                                        
                                                             "bookmarks": {/*...*/},                                        
                                                             "columns_controls": {/*...*/},                                        
                                                             "fulltext": {/*...*/},                                        
                                                             "listing_filters": {/*...*/},                                        
                                                             "listing_massaction": {/*...*/},                                        
                                                             "listing_paging": {/*...*/}
                                                    },
老程序员注意到返回的节点中名为 children 的节点 将会迷惑 —— 我们认为这是 Magento 1 留下来的。这些子元素他们本身都是功能完整的 UI 组件。cms_block_listing 组件由名为 listing_top,bookmarks 等组成。


正如我们之前提到的,最初的 getTemplate 的调用最终会渲染很多子组件。第一个 KnockoutJS 模板之所以命名为 collection.html 是因为它是很多不同的 UI 组件的集合。不过今天我们涉及这整个渲染过程。


今天我们涉及的是 PHP 开发人员如何控制在 JavaScript 树中渲染什么。如果我们回到 <uiComponent> 标签。

<!-- #File: vendor/magento/module-cms/view/adminhtml/layout/cms_block_index.xml -->
<uiComponent name="cms_block_listing"/>
Magento 使用 uiComponent 的名字来查找一个名为 cms_block_listing.xml 的新 XML 文件。
#File: vendor/magento//module-cms/view/adminhtml/ui_component/cms_block_listing.xml<?xml version="1.0" encoding="UTF-8"?><!--/**
 * Copyright © 2016 Magento. 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">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">cms_block_listing.cms_block_listing_data_source</item>
            <item name="deps" xsi:type="string">cms_block_listing.cms_block_listing_data_source</item>
        </item>
        <item name="spinner" xsi:type="string">cms_block_columns</item>
        <item name="buttons" xsi:type="array">
            <item name="add" xsi:type="array">
                <item name="name" xsi:type="string">add</item>
                <item name="label" xsi:type="string" translate="true">Add New Block</item>
                <item name="class" xsi:type="string">primary</item>
                <item name="url" xsi:type="string">*/*/new</item>
            </item>
        </item>
    </argument>
    <!-- ... we'll get to this in a second ... --></listing>


这些 UI 组件 XML 文件是一个新的 DSL(domain specific language)。以上的命令告诉 Magento 要:
  1. 为根级别的 listing 节点查询 PHP 类名和默认参数
  2. 用 argument 节点作为构造函数的参数来实例化这个类
Magento 将会查询下面这个文件中的 PHP 类名和默认参数:

#File: vendor/magento/module-ui/view/base/ui_component/etc/definition.xml
<components xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_definition.xsd">
    <!-- ... -->
    <listing sorting="true" class="Magento\Ui\Component\Listing">
        <argument name="data" xsi:type="array">
            <item name="template" xsi:type="string">templates/listing/default</item>
            <item name="save_parameters_in_session" xsi:type="string">1</item>
            <item name="client_root" xsi:type="string">mui/index/render</item>
            <item name="config" xsi:type="array">
                <item name="component" xsi:type="string">uiComponent</item>
            </item>
        </argument>
    </listing>    
    <!-- ... -->
</components>
所以,当 Magento <uiComponent name="cms_block_listing"> 作为 JSON 渲染的时候,它以运行像这样的代码(过于简单的)开始:
$uiComponent = new Magento\Ui\Component\Listing(
    $context, $components, [
            'template'=>'templates/listing/default',
             'save_parameters_in_session'=>'1',
             'client_root'=>'mui/index/render',        
             'config'=>[
                         'component'=>'uiComponent'
              ],        
              'js_config'=>[
                          'provider'=>'',            
                          'deps'=>''
             ],        
             'spinner'=>'cms_block_columns',        
             'buttons'=>[
                         'add'=>[ 
                                        'name'=>'add',                
                                        'label'=>'Add New Block',                
                                        'class'=>'primary',                
                                        'url'=>'*/*/new'
            ]
        ],
    ]
)

以上参数的数据来自于合并在一起的

#File: vendor/magento//module-ui/view/base/ui_component/templates/listing/default.xhtml
<div    class="admin__data-grid-outer-wrap"
    data-bind="scope: '{{getName()}}.{{getName()}}'"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../../../../../../Ui/etc/ui_template.xsd">
    <div data-role="spinner" data-component="{{getName()}}.{{getName()}}.{{spinner}}" class="admin__data-grid-loading-mask">
        <div class="spinner">
            <span/><span/><span/><span/><span/><span/><span/><span/>
        </div>
    </div>
    <!-- ko template: getTemplate() --><!-- /ko -->
</div>

这个 XHTML 模板通过一个完整不同于标准的 Magento 模板的渲染引擎进行渲染的。
Magento 通过调用 PHP 方法替换 {{...}} 这个文字在 UI 组件对象(getName()),或者直接获取一些对象的数据属性({{spinner}})。
聪明的你可能注意到在模板中没有列出 x-magento-init 。UI 组件的渲染 x-magento-init 的部分仍然由 XHTML 渲染引擎处理——这里尤其是 appendLayoutConfiguration 方法的调用:
#File: vendor/magento/module-ui/TemplateEngine/Xhtml/Result.phppublic function __toString()
{    try {        //...
        $this->appendLayoutConfiguration();        $result = $this->compiler->postprocessing($this->template->__toString());
    } catch (\Exception $e) {        $this->logger->critical($e->getMessage());        $result = $e->getMessage();
    }    return $result;
}//...public function appendLayoutConfiguration()
{    $layoutConfiguration = $this->wrapContent(        json_encode(            $this->structure->generate($this->component)
        )
    );    $this->template->append($layoutConfiguration);
}//...protected function wrapContent($content)
{    return '<script type="text/x-magento-init"><![CDATA['
    . '{"*": {"Magento_Ui/js/core/app": ' . str_replace(['<![CDATA[', ']]>'], '', $content) . '}}'
    . ']]></script>';
}
Magento 将 UI 组件对象的结构作为 JSON 字符串渲染,然后把这个字符串添加到模板上。
你问什么是 UI 组件的结构?还记得我们的另一个方法吗?

#File: vendor/magento//module-cms/view/adminhtml/ui_component/cms_block_listing.xml<?xml version="1.0" encoding="UTF-8"?><listing 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="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">cms_block_listing.cms_block_listing_data_source</item>
            <item name="deps" xsi:type="string">cms_block_listing.cms_block_listing_data_source</item>
        </item>
        <item name="spinner" xsi:type="string">cms_block_columns</item>
        <item name="buttons" xsi:type="array">
            <item name="add" xsi:type="array">
                <item name="name" xsi:type="string">add</item>
                <item name="label" xsi:type="string" translate="true">Add New Block</item>
                <item name="class" xsi:type="string">primary</item>
                <item name="url" xsi:type="string">*/*/new</item>
            </item>
        </item>
    </argument>
    <!-- ... we'll get to this in a second ... --></listing>
如果你看到这些节点的实际内容
#File: vendor/magento//module-cms/view/adminhtml/ui_component/cms_block_listing.xml
<listingToolbar name="listing_top">
    <argument name="data" xsi:type="array">
        <!-- ... -->
    </argument>    </listingToolbar><columns name="cms_block_columns">
    <argument name="data" xsi:type="array">
        <!-- ... -->
    </argument>    
</columns>


我们看到更多的配置的 UI 组件。UI 组件中任何名字不为 argument 的子节点被当做父对象的子节点。比如,当 Magento 渲染 listing 组件时,它也在 definitions.xml 中查找 listingToolbar,columns 等的类名和参数。

#File: vendor/magento/module-ui/view/base/ui_component/etc/definition.xml
<components xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_definition.xsd">
    <listingToolbar class="Magento\Ui\Component\Container"><!--...--></listingToolbar>
    <columns class="Magento\Ui\Component\Listing\Columns"><!--...--></columns>
</components>
并且,我们之前用的伪代码像下面这样:
$uiComponent = new Magento\Ui\Component\Listing(...);
$listingToolbar = new Magento\Ui\Component\Container(...);
$columns        = new Magento\Ui\Component\Listing\Columns(...);
$uiComponent->addComponent($listingToolbar);     
$uiComponent->addComponent($columns);
值得注意的是:这些子组件使用 RequireJS 模块名配置的。
#File: vendor/magento//module-cms/view/adminhtml/ui_component/cms_block_listing.xml
<columns class="Magento\Ui\Component\Listing\Columns">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="component" xsi:type="string">Magento_Ui/js/grid/listing</item>
            <!-- ... -->
        </item>
    </argument>
</columns>


Magento 把这些 RequireJS 模块转化成了 KnockoutJS view model 。如果你查找这些 KnockoutJS view models 的来源——你将会发现是在 view model 构造函数里配置了 KnockoutJS 模板。
#File: vendor/magento//module-ui/view/base/web/js/grid/listing.js
define([    
    'ko',    
    'underscore',    
    'Magento_Ui/js/lib/spinner',    
    'uiLayout',    
'uiCollection'], function (ko, _, loader, layout, Collection) {    'use strict';    return Collection.extend({
        defaults: {
            template: 'ui/grid/listing',
        }        //...
    });
});


数据来源节点
最后,有一个更特殊的 UI 组件子节点 —— <dataSource /> 节点。
#File: vendor/magento//module-cms/view/adminhtml/ui_component/cms_block_listing.xml<listing 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="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">cms_block_listing.cms_block_listing_data_source</item>
            <item name="deps" xsi:type="string">cms_block_listing.cms_block_listing_data_source</item>
        </item>
        <item name="spinner" xsi:type="string">cms_block_columns</item>
        <item name="buttons" xsi:type="array">
            <item name="add" xsi:type="array">
                <item name="name" xsi:type="string">add</item>
                <item name="label" xsi:type="string" translate="true">Add New Block</item>
                <item name="class" xsi:type="string">primary</item>
                <item name="url" xsi:type="string">*/*/new</item>
            </item>
        </item>
    </argument>
    <!-- ... -->
    <dataSource name="cms_block_listing_data_source">
        <!-- ... -->
    </dataSource></listing>
名为 DataSource 的节点仍然是 UI 组件,但是得到了特别待遇。当 Magento 渲染 UI 组件 JSON 的时候,dataSource 节点会获取子结构,并且 Magento 沿着主线渲染他们,第一级组件(使用组件名加上字符串 _data_source 作为 key)。
{
    "*": {
        "Magento_Ui/js/core/app": {
             "types": {/*...*/},
                     "components": {
                            "cms_block_listing": {
                                   "children": {
                                          "cms_block_listing": {/*...*/},
                                                "cms_block_listing_data_source": {
                                                        "type": "dataSource",
                                                         "name": "cms_block_listing_data_source",
                                                         "dataScope": "cms_block_listing",
                                                         "config": {
                                                               "data": {     
                                                                      "items": [],                                    
                                                                      "totalRecords": 0
                                                                },                                
                                                                "component": "Magento_Ui\/js\/grid\/provider", 
                                                                "update_url": "http:\/\/magento-2-1-0.dev\/admin\/mui\/index\/render\/key\/e628fdf18db9219474935e85ab3f25b445287503a00a230704b4168c566f8059\/",                                
                                                                "storageConfig": {                                    
                                                                     "indexField": "block_id"
                                                                 },                               
                                                                 "params": {
                                                                      "namespace": "cms_block_listing"
                                                                 }
                                                          }
                                                  }
                                        }
                              }
                    }
           }
    }
}


Magento 将在 dataSource 组件中查询组成 UI 组件的实际数据(比如,model 已渲染的数据集合)。


UI 组件渲染 DSL 的总结
好的,信息量很大哈。我写完了,我甚至都不确定我能不能跟上,所以如果你感觉晕头转向也不要担心。这里是高度概括。
  1. UI 组件渲染 x-magento-init 脚本,这个脚本填入了 KnockoutJS view models 的全局注册表。
  2. UI 组件也渲染 HTML 骨架,HTML 使用 KnockoutJS 和自定义 scope 绑定来渲染构成组件的 DOM 节点。
  3. ui_component XML 文件是一个特定领域的语言来表示 UI 组件对象的嵌套的等级,最终,Magento 将会用它来渲染 x-magento-init 脚本的 JSON 。
  4. ui_component 的 XML 节点 name 用来查询 PHP 类以示例化。
  5. Magento 把所以子 <agument /> 节点作为那个类的构造函数的参数。
  6. Magento 使用任何名为 <dataSource> 的子节点来渲染用在 UI 组件中的实际数据(比如,网格列表信息)。
  7. 其他任何子节点将会被用在渲染子 UI 组件,这些子 UI 组件将遵循父级的规则。
  8. 顶层 UI 节点配置一个 XHTML 模板,Magento 通过 PHP 渲染它。
  9. UI 组件节点配置 RequireJS 模块,Magento 把 RequireJS 模块当做 KnockoutJS view model 构造函数。

就像你看到的,虽然 UIComponent 标签很好地简化了 Magento 2 的布局句柄 XML 文件,他们仍然隐藏了很多更复杂的 UI 渲染系统,包括前端和后台的 Magento 系统代码,并且要求开发人员理解 Magneto 的自定义 RequireJS 和 KnockoutJS 。


用 Pestle 创建一个 网格列表
从上面可看出,UI 组件系统和 Magento 1 的 XML 系统在复杂度和缺乏使用指导上不相上下。换句话说,实际上像 pestle 那样的代码生成工具对于工作在 Magento 的开发人员来说,可以让事情变得更好。最近一个版本的 pestle 包含了一个 magento2:generate:ui:grid 命令为创建 UI 列表,还有更多的命令即将到来。
我们会通过运行 pestle 来创建 UI 表格。我们假设你已经完成了用于数据库访问的 CRUD 模块的教程,并且用过 Pulsestorm_ToDoCrud 模块。我们同样认为你已经可以用布局句柄 XML 文件来创建一个管理断点,并且有一个可浏览的后台网页。
为了创建一个栅格列表,调用传入以下参数的 pestle 的 magento2:generate:ui:grid 命令。
$ pestle.phar magento2:generate:ui:grid
Which Module? (Pulsestorm_Gridexample)] Pulsestorm_ToDoCrud
Create a unique ID for your Listing/Grid! (pulsestorm_gridexample_log)] pulsestorm_todo_listing
What Resource Collection Model should your listing use? (Magento\Cms\Model\ResourceModel\Page\Collection)] Pulsestorm\ToDoCrud\Model\ResourceModel\TodoItem\Collection
What's the ID field for you model? (pulsestorm_gridexample_log_id)] pulsestorm_todocrud_todoitem_id

Which Module 参数告诉 pestle 你想要创建你栅格列表的 Magento 模块。一般来说,跟 collection 文件是同一个模块,但是在这个惯例上没有硬性规定。之前的教程里我们已经指定了 Pulsestorm_ToDoCrud 模块。


Create a unique ID for your Listing/Grid! 这个参数是我们想要的 UI 组件 的名称。这将是在 <uiComponent /> 标签中属性 name="" ,也是在磁盘上的 UI 组件 XML 文件的基本文件名。


What Resource Collection Model should your listing use? 这个参数是 model 集合用的类名。我们想要栅格列表展示 Pulsestorm_ToDoCrud model,所以我们使用 Pulsestorm\ToDoCrud\Model\ResourceModel\TodoItem\Collection 这个集合。


What’s the ID field for you model? 这个参数是一个 model 的数据库表的主键数据库列。


在运行了以上命令之后,添加以下 <uiComponent /> 到你模块的布局句柄 XML 文件。
<!-- File: app/code/Pulsestorm/ToDoCrud/view/adminhtml/layout/pulsestorm_admin_todocrud_index_index.xml -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="content">                
            <uiComponent name="pulsestorm_todo_listing"/> 
        </referenceBlock>
    </body>
</page>
完成上述步骤后,清缓存,并且你应该有一个单独简单列出每个Pulsestorm\ToDoCrud\Model\ToDoItem model 的 model ID 的 UI Grid 。如果你想要给 model 的标题属性添加一列,就把下面的 <column /> 节点添加到形成的 UI 组件 XML 文件中。
<!-- File: app/code/Pulsestorm/ToDoCrud/view/adminhtml/ui_component/pulsestorm_todo_listing.xml -->
<!-- ... -->
<columns>

    <!-- ... --->

    <column name="title">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="filter" xsi:type="string">text</item>
                <item name="label" xsi:type="string" translate="true">Item Title</item>
                <item name="sortOrder" xsi:type="number">20</item>
            </item>
        </argument>
    </column>

    <!-- ... --->
</columns>
<!-- ... -->

除了生成 UI 组件的 pulsestorm_todo_listing.xml 文件,pestle 也生成了一个 “提供数据” 类和一个“操作页面” 类。


这个“数据提供”类包括了 collection 资源 model 。
#File: app/code/Pulsestorm/ToDoCrud/Ui/Component/Listing/DataProviders/Pulsestorm/Todo/Listing.php
<?php
namespace Pulsestorm\ToDoCrud\Ui\Component\Listing\DataProviders\Pulsestorm\Todo;
class Listing extends \Magento\Ui\DataProvider\AbstractDataProvider{    
    public function __construct(
        $name,        
        $primaryFieldName,        
        $requestFieldName,
        \Pulsestorm\ToDoCrud\Model\ResourceModel\TodoItem\CollectionFactory $collectionFactory,        
        array $meta = [],        
        array $data = []
    ) {        
        parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);        
        $this->collection = $collectionFactory->create();
    }
}
并且“页面操作”类负责渲染在最后一列的编辑链接。
#File: app/code/Pulsestorm/ToDoCrud/Ui/Component/Listing/Column/Pulsestormtodolisting/PageActions.php
<?php
    namespace Pulsestorm\ToDoCrud\Ui\Component\Listing\Column\Pulsestormtodolisting;
    class PageActions extends \Magento\Ui\Component\Listing\Columns\Column{
        public function prepareDataSource(array $dataSource)
        {
                if (isset($dataSource["data"]["items"])) {   
                         foreach ($dataSource["data"]["items"] as & $item) {   
                                      $name = $this->getData("name");                
                                      $id = "X";                
                                      if(isset($item["pulsestorm_todocrud_todoitem_id"]))
                                      {              
                                            $id = $item["pulsestorm_todocrud_todoitem_id"];
                                      }                
                                      $item[$name]["view"] = [              
                                            "href"=>$this->getContext()->getUrl(                        
                                            "adminhtml/pulsestorm_todo_listing/viewlog",["id"=>$id]),                    
                                            "label"=>__("Edit")
                                      ];
                      }
                 }        
        
                return $dataSource;
       }    

}
虽然没有功能全面完善,但是 magento2:generate:ui:grid 命令可以让你以一个基础的栅格列表配置开始。从那里,应该可以检查 Magento 的核心栅格的类,并且可以复制你在核心模块看到的任何功能。


最新回复 (5)
  • 超级版主组 前端小威 1月前
    0 引用 2
    已翻译完成,但是有一些真的太口语化了,实在不知道咋翻译,要是有需要修改的,大家可以评论哈
  • 二级用户组 29天前
    0 引用 3
    66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 谢谢小威
  • 超级版主组 我就是那个老邹 29天前
    0 引用 4
    66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 谢谢小威
  • 一级用户组 stelazjcn 18天前
    0 引用 5
    我来给你改进下标题看看:
    从正面来讲(我也不知道咋翻译)——UI组件,似乎让一切变得更简单
    实际情况(真不知道它在表达啥)——但是,现实仍然存在很多挑战
  • 超级版主组 前端小威 18天前
    0 引用 6
    stelazjcn 我来给你改进下标题看看: 从正面来讲(我也不知道咋翻译)——UI组件,似乎让一切变得更简单 实际情况(真不知道它在表达啥)——但是,现实仍然存在很多挑战

    谢谢你给了我启发,这个短语本意是正向旋转,咱们是不是可以理解成——从理论上讲,因为下面的详细介绍好像在说这样做的缘由,你真棒,谢谢你哦

  • 游客
    登录 | 注册 方可回帖
返回
发帖