SAP UI5 dan Confirmation Windows: Lagi Tentang Konteks

Mukjizat terjadi - setiap programmer akan memberi tahu Anda hal itu. (Bukan epigraf)

Selamat siang!

Contohnya akan sederhana.

Ada tampilan:

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

dan pengontrol "menipu" yang sama:

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

});

Untuk melihat dan berperilaku, semua ini akan sangat diharapkan:

gambar

Dengan mengklik tombol, pengidentifikasi yang dipilih yang terdaftar dengan koma akan muncul (kami akan meninggalkan pemeriksaan untuk array yang tidak kosong dan ui-trifles lainnya di luar ruang lingkup artikel).

Tapi bayangkan itu dengan mengklik tombol "Ayo pergi!" Anda tidak hanya perlu menampilkan pengidentifikasi dari elemen yang dipilih, seperti yang terjadi sekarang, tetapi menghapusnya dari database.

Hal-hal seperti itu, tentu saja, perlu dilakukan hanya setelah konfirmasi tambahan oleh pengguna.

Nah, masalah sepele - tambahkan fragmen tampilan untuk dialog konfirmasi:

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>

Dan controllernya:

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

    });
});

Seperti yang Anda lihat, semuanya cukup klasik, pada tingkat contoh SAP UI5 SDK.
Sekarang, untuk memanggil kotak dialog ini, kita mendefinisikan kembali pengendali tombol utama kita sebagai berikut:

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

Yaitu, di sini kita membuat dialog, dengan parameter sebagai tampilan "utama" di mana ia akan ditempatkan, dan fungsi pengendali konfirmasi.

Di sini kami datang ke yang paling menarik.

Sebelum beralih, tinjau kode pengontrol dialog sekali lagi.

Lihat sesuatu yang aneh?

Tidak?

Baiklah, nyalakan mode jejak dan, setelah memilih dua elemen pertama, silakan klik tombol.

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

Hore, semuanya bekerja! Tampaknya, apa masalahnya? Mungkin saja pengguna menjadi tidak pernah puas dan ingin menghapus sesuatu yang lain. Mari kita coba memprediksi tindakannya: pilih elemen ketiga dan klik "Ayo Pergi!":

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

Mengapa [1,2], tanya saya kepada Anda. Itu baik untuk Anda, ada seseorang untuk bertanya.

Dan pertama kali saya melihat perilaku penerjemah yang jelek itu, saya mulai meragukan diri saya sendiri. Saya telah bekerja dengan berbagai kerangka kerja untuk waktu yang lama, tetapi saya belum pernah menemukan yang seperti ini: diketahui bahwa const tidak menjamin kekekalan objek dan array sepanjang keberadaannya - tetapi Terpilih bahkan tidak disebutkan di tempat lain. Jadi dia muncul, disesuaikan, dan sekarang dia dipindahkan ke panggilan balik.

Tapi saya tidak akan menyiksamu untuk waktu yang lama.

Ini semua tentang konteks dan penutupan yang disukai semua programmer js.

Pada kenyataannya, pada eksekusi pertama handleConfirmBtn, kami masih memiliki tautan ke penangannya beserta seluruh konteks (termasuk yang dipilih). Dan dengan konfirmasi penghapusan berikutnya, itu yang disebut.

Cara untuk memperbaiki kesalahan itu tidak begitu sederhana dan tidak ambigu. Tidak cukup hanya dengan memindahkan deklarasi oFragmentController (setelah panggilan pertama, konteksnya hilang). Dan berikut ini adalah cara yang paling ringkas (saya akan memberikan kode metode terbuka saja):

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

Perhatikan 4 baris terakhir yang dapat dieksekusi: dengan cara ini saya "roll" pointer ke handler saat ini bersama dengan konteks yang benar.

Pada umumnya, hanya beberapa baris yang harus ditambahkan ke versi aslinya.

Itu mungkin untuk bertemu satu
oDialog.setBeginButton(new sap.m.Button({ text: '', press: this.__fnConfirmBtn }));

tetapi di sini, Anda tahu, setiap kali Anda membuka dialog, tombol baru akan dibuat, ini tidak ok.

Saya juga mempertimbangkan opsi untuk memperbarui callback melalui oDialog.getBeginButton (). AttachPress, tetapi hanya hang penangan tambahan, dan itu menjijikkan untuk menghapus semua yang tersedia melalui .detachPress dalam satu baris.

Di sini petualangan seperti itu keluar hampir tiba-tiba (jauh dari yang pertama ... ah, ui5!)

PS Melewati fungsi ke fungsi konstruktor objek, di mana salah satu metode memiliki objek pengontrol yang berisi referensi ke fungsi handler. Tampaknya apa yang salah?

Pada umumnya, situasi yang dideskripsikan adalah fitur bahasa, dan kerangka kerja UI5 tidak memungkinkan untuk dipecahkan dengan indah.

All Articles