SAP UI5和确认窗口:关于上下文

奇迹发生了-任何程序员都会告诉你。(代替题词)

下午好!

一个例子很简单。

有一个视图:

<mvc:View 
    controllerName="MyController" 
    xmlns="sap.m" 
    xmlns:core="sap.ui.core" 
    xmlns:mvc="sap.ui.core.mvc">
    <Button text="!" press="handlePress" />
    <Table 
        items="{view>/list/items}" 
        mode="MultiSelect" 
        selectionChange="handleTableSelection">
        <columns>
            <Column>
                <Text text="" />
            </Column>
            <Column>
                <Text text="" />
            </Column>
        </columns>
        <items>
            <ColumnListItem>
                <cells>
                    <ObjectIdentifier title="{view>id}" />
                    <Text text="{view>description}" />
                </cells>
            </ColumnListItem>
        </items>
    </Table>
</mvc:View>

和相同的“剔除”控制器:

sap.ui.controller("MyController", {
        
    onInit: function () {
        this.__oViewModel = new sap.ui.model.json.JSONModel({
            list: {
                items: [
                    { id: 1, description: 'one' },
                    { id: 2, description: 'two' },
                    { id: 3, description: 'three' }
                ],
                selected: []
            }
        });
        this.getView().setModel(this.__oViewModel, "view");
    },

    handlePress: function (oEvent) {
        sap.m.MessageToast.show(this.__oViewModel.getProperty('/list/selected').join(', '));
    },

    handleTableSelection: function (oEvent) {
        const aSelectedCtx = oEvent.getSource().getSelectedContexts(),
            aSelected = aSelectedCtx.map(o => o.getObject().id);
        this.__oViewModel.setProperty('/list/selected', aSelected);
    }

});

在外观和行为上,所有这些都是可以预期的:

图片

通过单击按钮,将弹出用逗号列出的选定标识符(我们将非空数组和其他ui琐事的检查留在本文范围之外)。

但是想像一下,通过单击“开始吧”按钮。您不仅需要像现在一样显示所选元素的标识符,还需要从数据库中删除它们。

当然,只有在用户额外确认后才需要执行这些操作。

好了,这很重要-为确认对话框添加一个视图片段:

ConfirmDialog.fragment.xml

<core:FragmentDefinition
    xmlns='sap.m'
    xmlns:core='sap.ui.core' >
    <Dialog
        id='confirmDialog'
        title=''
        type='Message'
        state='Warning'>
        <content>
            <Label text='  ?' />
        </content>
        <beginButton>
            <Button
                text=''
                press='handleConfirmBtn'/>
        </beginButton>
        <endButton>
            <Button
                text=''
                press='handleCancelBtn'/>
        </endButton>
    </Dialog>
</core:FragmentDefinition>

以及它的控制器:

ConfirmDialog.controller.js

sap.ui.define([
    "sap/ui/base/ManagedObject"
], function (ManagedObject) {
    "use strict";

    return ManagedObject.extend("project.ConfirmDialog", {

        constructor: function (oView, fnConfirmBtn) {
            this.__oView = oView;
            this.__fnConfirmBtn = fnConfirmBtn;
        },

        exit: function () {
            delete this.__oView;
            delete this.__fnConfirmBtn;
        },

        open: function () {
            const oView = this.__oView;
            let oDialog = oView.byId("confirmDialog");

            if (!oDialog) {
                const oFragmentController = {
                    handleConfirmBtn: () => {
                        this.__fnConfirmBtn();
                        oDialog.close();
                    },
                    handleCancelBtn: () => {
                        oDialog.close();
                    }
                };
                oDialog = sap.ui.xmlfragment(oView.getId(), "project.view.fragment.ConfirmDialog", oFragmentController);
                oView.addDependent(oDialog);
            }
            oDialog.open();
        }

    });
});

如您所见,在SAP UI5 SDK的示例级别上,一切都非常经典。
现在,要调用此对话框,我们重新定义主按钮的处理程序,如下所示:

handlePress: function (oEvent) {
    const aSelected = this.__oViewModel.getProperty('/list/selected');
    this.__confirmDialog = new ConfirmDialog(this.getView(), () => {
        aSelected.forEach(o => {
            //    
        });
        this.__confirmDialog.exit();
    });
    this.__confirmDialog.open();
}

就是说,在这里我们创建对话,将其将位于其上的“主”视图以及确认处理函数作为参数传递。

在这里,我们来到了最有趣的地方。

在继续之前,请再次检查对话框控制器代码。

看到奇怪的东西了吗?

没有?

然后,打开跟踪模式,并选择了前两个元素,然后随时单击按钮。

        handlePress: function (oEvent) {
            const aSelected = this.__oViewModel.getProperty('/list/selected');  
            // aSelected = [1,2], ,  
            this.__confirmDialog = new ConfirmDialog(this.getView(), () => {
                aSelected.forEach(o => {
                    //   aSelected = [1,2]
                });
                this.__confirmDialog.exit();
            });
            this.__confirmDialog.open();
        }

万岁,一切正常!看来,有什么收获?用户可能会感到满意,并想要删除其他内容。让我们尝试预测其动作:选择第三个元素,然后单击“ Let's Go!”:

        handlePress: function (oEvent) {
            const aSelected = this.__oViewModel.getProperty('/list/selected');  
            // aSelected = [3]
            this.__confirmDialog = new ConfirmDialog(this.getView(), () => {
                aSelected.forEach(o => {
                    //   aSelected = [1,2] ???

为什么[1,2],问我你。对您有好处,有人要问。

当我第一次看到口译员的这种丑陋举动时,我开始默默地怀疑自己。我使用各种框架已有很长时间了,但是我从未遇到过类似的东西:众所周知,const不能保证对象和数组在存在时始终不变,但是aSelected甚至在其他任何地方都没有提及。于是他出现了,被挪用了,现在他被转移到一个回调中。

但我不会长时间折磨你。

这是所有js程序员都喜欢的上下文和闭包。

也就是说,实际上,在第一次执行handleConfirmBtn时,我们仍然具有指向其处理程序的链接以及整个上下文(包括aSelected)。在随后的删除确认中,是她被叫。

纠正错误的方法并非如此简单和明确。仅移动oFragmentController声明是不够的(在第一次调用之后,上下文丢失了)。以下是最简洁的方法(我将仅提供open方法的代码):

open: function () {
    const oView = this.__oView;
    let oDialog = oView.byId("confirmDialog");

    if (!oDialog) {
        const oFragmentController = {
            handleConfirmBtn: function () {
                this.__fnConfirm();
                oDialog.close();
            },
            handleCancelBtn: function () {
                oDialog.close();
            }
        };
        oDialog = sap.ui.xmlfragment(oView.getId(), "project.view.fragment.ConfirmDialog", oFragmentController);
        oDialog.controller = oFragmentController;
        oView.addDependent(oDialog);
    }
    oDialog.controller.__fnConfirm = this.__fnConfirmBtn.bind(this);
    oDialog.open();
}

注意最后4条可执行代码行:这样,我将“指针”“滚动”到当前处理程序以及正确的上下文。

总的来说,只需要在原始版本中添加几行即可。

有可能见到一个
oDialog.setBeginButton(new sap.m.Button({ text: '', press: this.__fnConfirmBtn }));

但是在这里,您知道,每次打开对话框时,都会创建一个新按钮,这不行。

我还考虑了通过oDialog.getBeginButton()。AttachPress更新回调的选项,但是它只是挂起了一个附加处理程序,因此讨厌通过.detachPress删除所有可用的内容。

在这里,这种冒险几乎是出乎意料的(远非第一次... ah5,ui5!)

PS将函数传递给对象的构造函数,其中一个方法有一个控制器对象,该对象包含对处理函数的引用。看来可能出什么问题了?

总的来说,所描述的情况是该语言的特征,并且UI5框架根本不允许对其进行精美的解决。

All Articles