【问题标题】:How to control order of view activation in Durandal如何控制 Durandal 中的视图激活顺序
【发布时间】:2013-11-14 09:23:37
【问题描述】:

我正在使用 Durandal 2.0 构建应用程序。
我的 shell 视图如下所示:

<div>
    <header id="nav" data-bind="compose: 'viewmodels/nav', activate: false">
    </header>
     <section id="content" class="main container-fluid" data-bind="router: { transition: 'entrance' }, activate: false" style="overflow: auto">
     </section>
    <footer>
        <!--ko compose: {view: 'footer'} --><!--/ko-->
    </footer>
</div>

在导航部分,我想要我的选项卡和用户的下拉列表(两者都是从 Web 服务中检索的)。从下拉列表中选择用户将导航到将更新内容部分的新 URL。 (路由看起来像 localhost/#user1/tab2)。

  • 问题:我需要先从导航部分了解所选用户,然后才能检索内容部分的数据,但内容部分在我检索用户之前已激活。

这实际上只是初始页面加载的问题,因为用户列表只检索一次。

  • 有没有办法告诉内容部分等到导航部分完成加载?
  • 有没有比我现在做的更好的方法来解决这个问题?

导航激活函数如下所示:

function activate(context) {        
    return dataservice.getUsers().then(function () {
        //do stuff
    });
}); 

首先调用此激活函数,然后调用 dataservice.getUsers(),然后在“做事”部分发生之前调用内容模块的激活函数(并且在 getUsers 调用的数据返回之前)数据服务)。也许我的承诺有问题?

编辑
我已经将 dFiddle 与一些实际代码放在一起,这些代码显示了我在说什么:http://jerrade.github.io/dFiddle-2.0/#test/dashboard
代码在这里:https://github.com/jerrade/dFiddle-2.0

导航.js

 function activate(context) {
     console.log('Nav View Activated');
     if (vm.impersonateUsername == undefined)
         vm.impersonateUsername = getUsernameFromWindowLocation();

     return dataservice.getPageDetailForEmployee(vm.loggedInUsername, vm).then(function () {
         console.log("Page details retrieved");

         // I want to do something here before the dashboard activates.
     });
  }

dashboard.js

 function activate(username) {
        console.log('Dashboard View Activated');
        //vm.username = nav.selectedImpersonateEmployee().Username;        
        return dataservice.getDashboard(nav.impersonateUsername, dashboard);
    }

打开页面并观察控制台。你会看到(除其他外)

Nav View Activated  
Dashboard View Activated  
Page details retrieved 

我真正想要的是在仪表板视图激活之前检索页面详细信息。我实际上已经重新调整了一些事情,以便目前这不再是一个问题,但它可能会再次出现。

看起来我想做的事情不应该这么复杂。除非我在这里用方钉敲打圆孔?

【问题讨论】:

  • 不,我认为你的承诺很好...... nav 的activate 方法与内容模块的activate 方法无关。有关如何使它们彼此“关联”的建议,请参阅下面的更新答案!
  • 我已经向你的分叉提交了一个拉取请求。现在的问题是您将 ajax 请求的延迟作为您的 activate 方法返回,该方法会在 ajax 请求返回后立即解决(而不是在您的处理完成后)。我无法测试代码,因为我无法访问您的 API,但我很确定它现在应该可以工作了。
  • 我更改了代码以返回延迟,但它仍然在做同样的事情。一旦代码执行 dataservice.getPageDetailForEmployee,它就会切换到仪表板激活。我已经模拟了我的 api 调用,所以如果你愿意,你应该可以在你的机器上运行它。
  • 好的;比我想象的要复杂,但下面答案的本质仍然是正确的。在员工加载之前,您必须阻止仪表板的激活。路由器仍将激活仪表板(因为您已经告诉它有关路由的信息),但如果您愿意,您可以使用if 绑定处理程序(答案的第一部分)阻止它绑定。希望更新的拉取请求有所帮助!
  • 这行得通,谢谢。不过,感觉要添加很多东西。我认为要点是,最好尝试以一种一切都可以异步发生的方式构建事物。

标签: durandal


【解决方案1】:

最简单的做法是在您的视图模型上添加一个名为isUserLoaded 的可观察对象。然后,在内容部分周围应用if 绑定:

<div>
    <header id="nav" data-bind="compose: 'viewmodels/nav', activate: false">
    </header>
    <!-- ko if: isUserLoaded -->
     <section id="content" class="main container-fluid" data-bind="router: { transition: 'entrance' }, activate: false" style="overflow: auto">
     </section>
    <!-- /ko -->
    <footer>
        <!--ko compose: {view: 'footer'} --><!--/ko-->
    </footer>
</div>

加载用户后,您可以将 observable 更新为 true,您的内容绑定应该会触发。

编辑

如果这不起作用,听起来您的路由器必须仍在解析内容视图模型(这可能指向路由问题?很难说没有看到您的整个解决方案)。

无论如何,如果它不是路由问题,那么您可以采用一种解决方案,让您的内容模型从其激活方法返回一个未解决的承诺;然后在加载用户时解决该承诺。例如,大致如下:

userModel.js:

define([], function () {
    // single instance to track the loaded user
    var userModel = {
        selectedUser: ko.observable()
    };

    return userModel;
});

navModel.js:

define(["dataService", "userModel"], function(dataService, userModel) {
    // view model for the nav bar
    var navModel = {        
        // model definition
    };

    // load the user
    navModel.activate = function() {
        return dataService.getUsers().then(function(response) {
            // Push the user to the userModel
            userModel.selectedUser(response.user);
        });
    };

    return navModel;
});

contentModel.js:

define(["userModel"], function (userModel) {
    var contentModel = {
        // model definition
    };

    contentModel.activate = function () {
        // Prevent activation until user is resolved
        var deferred = $.Deferred();

        var subscription = userModel.selectedUser.subscribe(function (user) {
            if (!user)
                return;

            subscription.dispose();
            deferred.resolve();
        });

        return deferred.promise();
    };

    return contentModel;
});

如果您不想为 userModel.selectedUser 属性使用 observable,则可以改用另一个 deferred,或在加载用户时触发事件等。无论您的船如何漂浮!

【讨论】:

  • 那不行。即使我从 shell 视图中完全删除了该内容部分,内容视图模型仍然会激活(我假设是因为无论 shell 中发生了什么,路由都会激活它)。
  • 好的;那可能是路由问题?但是我添加了一个替代解决方案,您可以尝试一下,希望对您有所帮助。
  • 那也没用。我已经用一些实际代码更新了我的原始问题。我现在已经解决了这个问题。我只是想了解我此时是否误用了 Durandal。
猜你喜欢
  • 1970-01-01
  • 2014-04-08
  • 2013-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-02
  • 2015-12-19
  • 2022-01-08
相关资源
最近更新 更多