集中导航的简洁设计?

Clean design for centralized navigation?

上下文

单页/ajax 网络应用程序

基本代码结构

LocationManager(负责更新浏览器哈希并将应用程序位置切换到不同的磁贴)

Page/Tile流量

基本信息>家庭信息>车辆信息>购买选项>查看订单>输入付款并提交

问题

当用户从“购买选项”导航到“查看订单”时,将进行长时间(5-8 秒)的服务调用以计算订单详细信息。在呼叫解决后,回调旨在将用户导航到查看订单页面。问题是,如果用户在那段时间点击返回并返回到家庭信息,一旦呼叫解决,他们将被 "automatically" 带到审查订单。非常尴尬的用户体验。

限制

取消通话不可行。需要一个解决方案来处理导航。

当前提议的实现

在调用 calculateOrder 之前保存 "currentLocation"。 将回调中的 "currentLocation" 作为 intendedStartingPoint 传递给 setLocation 方法。 setLocation 方法内部 if(intendedStartingPoint === Locationmanager.currentLocation) {//Navigate}

总而言之,如果用户在通话过程中更改位置,则在通话解决后,我们将不会导航,因为此时用户不希望被导航到查看订单。

这行得通吧?

赶上

我们在应用程序中有很多地方在长运行 调用的回调中调用 setLocation。这意味着我必须使用新参数 intendedStartingPoint 更新所有 setLocation 调用。虽然这对我来说很有意义,但它似乎确实有可能变得有点混乱。

关于如何清理和集中它有什么想法吗?

因此,现在用户可以单击“购买选项”页面上的“计算”按钮。然后您显示某种加载指示器(希望如此) 并向服务器发送异步请求,并在延续中附加 setLocation('ReviewOrder')。应用程序中有很多地方使用了这种模式。

存在意外(从用户的角度来看)重定向问题,因为使用这种方法服务器数据检索和 UI 导航是耦合的。想到的一个解决方案是将它们解耦并删除 setLocation 调用 来自所有 long-运行 请求延续。它可以按以下方式工作。

当用户单击“计算”按钮时,您会启动一个异步请求,同时立即导航到“查看订单”页面(这从用户体验的角度来看很重要,因为用户现在清楚地了解 Calculate 按钮导航到 Review Order)。在查看订单页面上,显示一个加载指示器,上面写着类似 'please wait, about 10 seconds remaining...' 请求完成时,隐藏加载指示器并显示数据。

这样您的用户将拥有一致的用户体验,知道无论何时他们在您的应用程序中单击一个按钮都会发生同样的事情(他们导航到一个视图),并且没有令人惊讶的自动重定向。

首先,如果用户能够返回并更改之前页面上输入的任何信息,则必须使任何待处理的服务调用无效。如果基于过时信息的服务调用 returns,则必须将其丢弃。

这意味着,if(intendedStartingPoint === Locationmanager.currentLocation) {//Navigate} 是不够的。你必须做类似 if(intendedStartingPoint === Locationmanager.currentLocation && /* no information altered in the meantime*/) {//Navigate}.

的事情

现在回答你的设计问题:如果没有任何具体代码,构建它有点困难,但你可以执行以下操作:

  • 提供在LocationManager
  • 中注册和管理长运行调用的方法
  • Long-运行 调用应始终使用 LocationManager
  • 注册
  • LocationManager 应该确保在某一时刻最多有一个 long-运行 调用处于活动状态
  • 如果发生任何位置变化,所有(或一个活动的)长运行呼叫必须无效
  • 长运行 调用的回调应该检查它是否已经失效,并且只有在这种情况下才导航。 LocationManager 可以对所有回调以统一的方式执行此操作。
  • 新的长运行 来电可以 replace/invalidate 已经 运行 来电或被拒绝,如您所愿。

我希望这对您的具体情况有意义。

首先,通过异步调用计算订单详细信息,并通过 javascript 向最终用户发送进度条 show/simulate。

第二件事:不要在您的服务回调函数中强制 ReviewOrder 磁贴打开。当服务完成计算时,您的回调函数会检查当前图块,如果它不是 ReviewOrder 个图块,则它将计算的信息存储在会话或本地存储中。

当用户浏览 ReviewOrder 磁贴时,将来自用户的订单详细信息与存储的订单详细信息进行比较(例如,通过散列函数)。

如果用户订单详情和存储的订单详情的哈希码相同,则显示保存的订单信息,否则重新调用该服务。

重要提示:为了防止订单伪造,可以考虑以下方式:

在服务器上计算订单详情后,生成唯一的订单id,返回给用户。然后将计算出的订单详细信息与此 ID 一起存储在服务器数据库中。如果用户没有更改订单详细信息,您的脚本将 post 仅将此订单 ID 发送给服务器作为标志,表示该订单已被接受。然后读取您的数据库并通过此 ID 处理订单。

如果订单未完成,则使用计划任务,清理数据库中未完成的订单(例如,24 小时前计算但仍未完成的订单)。

鉴于您无法阻止用户在磁贴之间导航,通知她计算延迟不会解决整个问题。您可以告诉用户完成的预计时间,可以显示进度条,并且可以立即将她带到 Review Order 磁贴等待结果,但是如果她离开磁贴,您将只剩下您的原来的问题。

如果用户在看到所有这些信息后选择离开,她一定是有意识地决定中断进程。将她送回 Review Order 会是糟糕的 UX。现在怎么办?

您提议,通过 calculateOrder 发送的回调函数应该将 intendedStartingPoint 参数传递给 setLocation,这非常合理。您担心这会要求您修改对 setLocation 的每次调用以适应新参数。不要害怕,JavaScript 提供了一个解决这个难题的巧妙方法。

您可以在不修改现有调用的情况下向 setLocation 添加新参数。这仅要求 intendedStartingPointsetLocation 参数列表中的最后一个参数。然后你的新版本 setLocation 可以检查 intendedStartingPoint 的值,看看它是否是 undefined.

如果 intendedStartingPointundefined,您知道 setLocation 正在接收旧呼叫之一,即未通过 intendedStartingPoint 的呼叫。在这些情况下,您忽略 intendedStartingPoint 并像以前一样继续。否则,你将 intendedStartingPoint 与当前位置进行比较,然后根据比较的结果进行处理。

更好的方法是使新参数不是 intendedStartingPoint,而是一个名为 options 的对象,其中包含 intendedStartingPoint 作为其属性之一。这允许您在将来需要时将更多可选值传递给 setLocation

setLocation 的新行为非常简单。在设置新位置之前,您检查 intendedStartingPoint 是否等于当前位置。如果是,则您无需执行任何操作,因为用户已经在她打算去的地方。但是,如果 intendedStartingPoint 与当前位置不同,则用户已导航离开,因此您可以这样做:

if (LocationManager.currentLocation !== options.intendedStartingPoint) {
  // Tell the user that the calculation has finished.
  // Ask her if she wants to return to Review Order now.
}