UI5引导程序登录片段显示的最佳实践

时间:2016-11-12 11:08:23

标签: sapui5

问题:OpenUI5中独立应用程序的登录页面模式是什么,包括在UI5引导程序和component.js执行时显示占位符页面内容?

我正在尝试为我的独立UI5应用程序的登录过程提供令人满意的用户体验。我想要的事件顺序是:

  1. 用户导航到UI5网址
  2. 在UI5启动时显示赏心悦目的背景/占位符
  3. 显示登录对话框,用户输入详细信息点击登录按钮等...
  4. 我目前的问题:

    1. 我在index.html的主体中放了一些文本,它立即显示用户导航到页面(好),但是在UI5应用程序启动后,文本仍然位于页面顶部(错误)。
    2. 将index.html主体留空,有一个空页面' UI5启动时滞后(坏ux)....
    3. ...我有一个第一个UI5视图,它充当占位符和相关的登录片段,但片段在被视图内容(坏ux)隐藏之前会暂时闪烁。
    4. 总的来说,我认为基于了解启动时生命周期事件的顺序,可能存在登录的最佳实践模式,但到目前为止我还没有在UI5文档中找到简洁的解释。

      有人能指出我有用的方向或提供建议吗?

      编辑:经过更多搜索后,我找到了this post并链接了JSBin,这对某一点很有帮助。本质上,该技术使用普通的html和脚本来延迟应用程序的负载,直到加载UI5脚本,在等待时显示令人愉快的进度微调器。当'设备准备好了#39;事件发生时,脚本启动应用程序加载并删除进度指示器的html元素。结果是UI5的无闪烁启动,具有令人满意的用户体验并且没有工件HTML。可以通过将隐藏进度元素html'函数在模型加载等的afterModelLoaded()过程中。

      我还重新启动了事件,以便在第一页的onAfterRendering()函数中触发登录对话框的打开 - 我以前在onDisplay()函数中有这个函数,当路由匹配时,它被连接到触发器。这是基于其他经验,我打算在打开页面时启动进一步的数据获取,但我认为在这种情况下页面已经呈现,因此对话闪烁不会发生或发生在屏幕外。在这种情况下触发onAfterRendering()中的对话已停止对话闪烁问题。

2 个答案:

答案 0 :(得分:2)

最近,在深入研究UI5手册之后,我使用OpenUI5 1.86+和现代的严格JS(async / await,箭头函数,对象)使用登录片段功能重构了旧代码库速记,异步UI5加载等),可以按照UI5代码重用最佳做法进行操作。最后,我可以分享经过全面测试,可以正常使用且随时可用的交钥匙成果。

index.html

使用Content-Security-Policy (CSP)进行引导:

<!DOCTYPE html>
<html lang = "en">
<head>
    <meta content = "default-src https: 'self' https://*.hana.ondemand.com 'unsafe-eval' 'unsafe-inline'; child-src 'none'; object-src 'none';"
          http-equiv = "Content-Security-Policy" />
    <meta charset = "utf-8" />
    <title>Certfys TMS</title>
    <!--suppress JSUnresolvedLibraryURL -->
    <script data-sap-ui-appCachebuster="./"
            data-sap-ui-async = "true"
            data-sap-ui-compatVersion = "edge"
            data-sap-ui-excludejquerycompat = "true"
            data-sap-ui-onInit = "module:sap/ui/core/ComponentSupport"
            data-sap-ui-resourceroots = '{"webapp": "./"}'
            data-sap-ui-theme = "sap_fiori_3"
            data-sap-ui-xx-componentpreload = "off"
            id = "sap-ui-bootstrap"
            src = "https://openui5nightly.hana.ondemand.com/resources/sap-ui-core.js">
    </script>
</head>
<body class = "sapUiBody"
      id = "content">
<div data-id = "rootComponentContainer"
     data-name = "webapp"
     data-sap-ui-component
     data-settings = '{"id" : "webapp"}'></div>
</body>
</html>

Component.js

// eslint-disable-next-line strict
"use strict";

// eslint-disable-next-line no-undef
sap.ui.define([
    "sap/ui/core/UIComponent",
    "sap/ui/Device",
    "./controller/AuthDialog"
// eslint-disable-next-line max-params
], (UIComponent, Device, AuthDialog) => UIComponent.extend("webapp.Component", {

    exit() {

        this._authDialog.destroy();

        delete this._authDialog;

    },

    getContentDensityClass() {

        if (!this._sContentDensityClass) {

            if (Device.support.touch) {

                this._sContentDensityClass = "sapUiSizeCozy";

            } else {

                this._sContentDensityClass = "sapUiSizeCompact";

            }

        }

        return this._sContentDensityClass;

    },

    init(...args) {

        UIComponent.prototype.init.apply(this, args);

        this.getRouter().initialize();

        this._authDialog = new AuthDialog(this.getRootControl(0));

    },

    metadata: {
        manifest: "json"
    },

    openAuthDialog() {

        this._authDialog.open();

    }

}));

App.controller.js

// eslint-disable-next-line strict
"use strict";

// eslint-disable-next-line no-undef
sap.ui.define([
    "sap/ui/core/mvc/Controller"
// eslint-disable-next-line max-params
], (Controller) => Controller.extend("webapp.controller.App", {

    onInit() {

        this.getView().addStyleClass(this.getOwnerComponent().getContentDensityClass());

    }

}));

Login.view.xml

<mvc:View
    xmlns:mvc = "sap.ui.core.mvc"
    controllerName = "webapp.controller.Login">
</mvc:View>

Login.controller.js

// eslint-disable-next-line strict
"use strict";

// eslint-disable-next-line no-undef
sap.ui.define([
    "sap/ui/core/mvc/Controller",
    "sap/ui/core/UIComponent",
    "webapp/controller/BaseController"
// eslint-disable-next-line max-params
], (Controller, UIComponent, BaseController) => Controller.extend("webapp.controller.login", {

    async onInit() {

        const session = {
            sessionID: sessionStorage.getItem("SessionId"),
            userID: sessionStorage.getItem("UserId")
        };

        const isAuthorized = await BaseController.isAuthorized(session);

        if (isAuthorized) {

            const oRouter = UIComponent.getRouterFor(this);
            oRouter.navTo("overview");

        } else {

            this.getOwnerComponent().openAuthDialog();

        }

    }

}));

AuthDialog.fragment.xml

<core:FragmentDefinition
    xmlns:core = "sap.ui.core"
    xmlns = "sap.m">
    <Dialog
        id = "authDialog"
        title = "{i18n>AUTH_DIALOG_DIALOG_TITLE}"
        type = "Message"
        escapeHandler = ".escapeHandler">
        <Label
            labelFor = "username"
            text = "{i18n>AUTH_DIALOG_LAB_USERNAME}" />
        <Input
            id = "username"
            liveChange = ".onLiveChange"
            placeholder = "{i18n>AUTH_DIALOG_PH_USERNAME}"
            type = "Text" />
        <Label
            labelFor = "password"
            text = "{i18n>AUTH_DIALOG_LAB_PASSWORD}" />
        <Input
            id = "password"
            liveChange = ".onLiveChange"
            placeholder = "{i18n>AUTH_DIALOG_PH_PASSWORD}"
            type = "Password" />
        <beginButton>
            <Button
                enabled = "false"
                id = "btnLogin"
                press = ".onPressLogin"
                text = "{i18n>AUTH_DIALOG_BTN_SUBMIT}"
                type = "Emphasized" />
        </beginButton>
    </Dialog>
</core:FragmentDefinition>

AuthDialog.js

// eslint-disable-next-line strict
"use strict";

const _resetForm = function _resetForm(oView) {

    oView.byId("username").setValue("");

    oView.byId("password").setValue("");

    oView.byId("btnLogin").setEnabled(false);

};

const _loginUser = async function _loginUser(oView, httpProtocol) {

    const userCredentials = {
        password: oView.byId("password").getValue(),
        username: oView.byId("username").getValue()
    };

    let authData;

    try {

        const authResponse = await fetch(`${httpProtocol}://${location.host}/login`, {
            body: JSON.stringify(userCredentials),
            cache: "no-cache",
            credentials: "same-origin",
            headers: {
                "Content-Type": "application/json"
            },
            method: "POST",
            mode: "same-origin",
            redirect: "follow",
            referrerPolicy: "no-referrer"
        });

        authData = await authResponse.json();

    } catch (err) {

        authData = {
            message: err.message,
            result: false
        };

    }

    return authData;

};

const _authUser = async function _authUser(UIComponent, MessageToast, oView, httpProtocol) {

    const authData = await _loginUser(oView, httpProtocol);

    sessionStorage.setItem("SessionId", authData.sessionID);
    sessionStorage.setItem("UserId", authData.userID);

    if (authData.result) {

        const oRouter = UIComponent.getRouterFor(oView);
        oRouter.navTo("overview");

    } else {

        const oBundle = oView.getModel("i18n").getResourceBundle();

        const sMsg = oBundle.getText(authData.message);

        MessageToast.show(sMsg);

        _resetForm(oView);

    }

};

// eslint-disable-next-line no-undef
sap.ui.define([
    "sap/ui/base/ManagedObject",
    "sap/ui/core/Fragment",
    "sap/ui/core/UIComponent",
    "sap/m/MessageToast",
    "webapp/controller/BaseController"
// eslint-disable-next-line max-params
], (ManagedObject, Fragment, UIComponent, MessageToast, BaseController) => ManagedObject.extend("webapp.controller.AuthDialog", {

    constructor: function constructor(oView) {

        this._oView = oView;

    },

    exit() {

        delete this._oView;

    },

    async open() {

        const oView = this._oView;

        if (!this._oDialog) {

            // noinspection JSUnusedGlobalSymbols
            const fragmentController = {

                escapeHandler(pEscapeHandler) {

                    pEscapeHandler.reject();

                },

                onLiveChange() {

                    const minRequiredLen = 3;

                    const username = oView.byId("username").getValue();

                    const password = oView.byId("password").getValue();

                    oView.byId("btnLogin").setEnabled((minRequiredLen <= username.length) && (minRequiredLen <= password.length));

                },

                async onPressLogin() {

                    const httpProtocol = BaseController.getHTTPProtocol();

                    await _authUser(UIComponent, MessageToast, oView, httpProtocol);

                }

            };

            this._oDialog = await Fragment.load({
                controller: fragmentController,
                id: oView.getId(),
                name: "webapp.view.AuthDialog"
            });

            this._oDialog.onsapenter = (async () => {

                if (oView.byId("btnLogin").getEnabled()) {

                    const httpProtocol = BaseController.getHTTPProtocol();

                    await _authUser(UIComponent, MessageToast, oView, httpProtocol);

                }

            });

            oView.addDependent(this._oDialog);

        }

        _resetForm(oView);

        this._oDialog.open();

    }

}));

BaseController.js

// eslint-disable-next-line strict
"use strict";

// eslint-disable-next-line no-undef
sap.ui.define([], () => ({

    getHTTPProtocol() {

        return ("localhost" === location.hostname)
            ? "http"
            : "https";

    },

    async isAuthorized(session) {

        const httpProtocol = this.getHTTPProtocol();

        let authData;

        try {

            const authResponse = await fetch(`${httpProtocol}://${location.host}/checkSession`, {
                body: JSON.stringify(session),
                cache: "no-cache",
                credentials: "same-origin",
                headers: {
                    "Content-Type": "application/json"
                },
                method: "POST",
                mode: "same-origin",
                redirect: "follow",
                referrerPolicy: "no-referrer"
            });

            authData = await authResponse.json();

        } catch (err) {

            authData = {
                message: err.message,
                result: false
            };

        }

        return authData.result;

    }

}));

manifest.json

相关的清单片段:

"routes": [
    {
        "name": "login",
        "pattern": "",
        "target": "login"
    }
],
"targets": {
    "login": {
        "viewId": "login",
        "viewName": "Login"
    }
}

最终结果:

UI5 login form using reusable Dialog fragment

希望,这将帮助某人了解现代UI5处理片段的方式。

答案 1 :(得分:1)

关于您当前的问题:

  1. 您的整个UI5应用程序都存在于index.html文件中。每当您使用UI5应用程序时,您实际上始终只停留在那个HTML页面上。因此,无论您放入HTML文件中的任何内容都将出现在应用程序的任何位置。所以不要放任何东西。只需使用它来引导UI5库。

  2. 那个空页&#39;滞后无法避免(更新:是的,可以 - 见下文)。从服务器下载资源后,UI5应用程序需要额外的几秒钟才能加载。没有办法解决这个问题; UI5是一个庞大的库,它将花费额外的时间让浏览器准备就绪。因为库本身在此期间已经准备好了,所以不能设置一种启动画面(例如,想想 this.getView()。setBusy())。但是,因为基本上您的整个网站将在此延迟后存在于浏览器中,您的网站导航将比在大多数其他框架中编写的应用程序中的导航更快,其中每次需要从删除服务器下载以下页面你离开了一个页面。

  3. 我不知道你扮演占位符的观点是什么意思。我可能会删除它。另外,我不建议使用片段来处理登录。相反,为它创建一个专用视图。这是执行此操作的标准方法。

  4. 回顾一下:

    1. 用户点击网址 -
    2. 库加载
    3. 第一个视图是登录视图。用户提交表格;登录视图与服务器通信(当它执行此操作时,您可以将视图设置为忙碌以获得愉快的用户体验)。
    4. 如果用户已通过身份验证,请导航至其他视图。
    5. 我希望这会让事情更加清晰。如果您有任何问题,请告诉我。

      更新:很好找!!我没有想到这一点。是的,你当然可以在你的HTML页面等待下载脚本时做一些事情,然后当调用ondeviceready方法时,将所有内容交给UI5应用程序。