杜兰达/淘汰赛-全球可观察 [英] Durandal / Knockout - global observable

查看:91
本文介绍了杜兰达/淘汰赛-全球可观察的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

上下文

在我的shell.html中,我在标题部分放入了语言选择.看起来像这样:

In my shell.html I have put a language selection into the header section. It looks like this:

    <select class="form-control select2"
      data-bind="options:languages,
      optionsText:'label',
      optionsValue:'code',
      value:selectedLanguage">
    </select>

我的shell.js:

My shell.js:

define(['plugins/router', 'durandal/app', 'knockout'], function (router, app, ko) {
    return {
        router: router,
        selectedLanguage: ko.observable(),
        languages: [
            {code: 'cs', label: 'čeština'},
            {code: 'sk', label: 'slovenský'},
            {code: 'en', label: 'english'},
            {code: 'de', label: 'deutsch'},
            {code: 'ru', label: 'русский'}
        ],
        activate: function () {
            return router.map([
               { route: ['', 'dashboard'],      moduleId: 'core/dashboard/pages/index',     title: 'Dashboard',         icon: 'fa fa-dashboard fa-fw',      nav: true },
               { route: 'attributes*details',   moduleId: 'core/attribute/pages/index',     title: 'Attribute',     icon: 'fa fa-puzzle-piece fa-fw',   nav: true, hash: '#/attributes' }
           ]).buildNavigationModel()
             .mapUnknownRoutes('core/dashboard/pages/index', 'not-found')
             .activate();
        }
    };
});

当我加载其他视图时,该视图始终保留在标题中,并且不会更改.

When I load other views, this always stays in the header and does not change.

任务

无论用户何时更改语言,我都希望能够在任何子页面上进行注册/通知,然后做出相应的反应-就像以另一种语言显示文本一样.

Whenever the user changes the language, I want to be able to register/notice this on any sub-page and then react accordingly - like showing the text in another language.

问题

我认为这更多是一个概念性的问题.如果我更改语言,那么我可以肯定,shell.js中的可观察对象将对此有所帮助.我不明白的是如何在我的子视图中实现这一目标.我需要注册一些全球用户吗?

I suppose this is more of a conceptual question. If I change the language, then I am sure that my observable in the shell.js will pick this up. What I do not understand is how to achieve this for my sub-views. Do I need to register some global-subscriber?

仅在shell.js中做出响应显然是不够的,因为该视图模型不知道需要加载哪些内容.只有实际的子视图/模块知道这一点.但是为了做出反应,他们需要知道语言已经改变.

It is obviously not enough to just react in the shell.js because that view-model does not know which content needs to be loaded. Only the actual sub-views / modules know this. But in order to react, they need to know that the language has changed.

如果有人可以在此方面帮助我,并给我一些提示,以提示如何在durandal/knockout上下文中最有效地处理这些类型的功能,那就太好了.

It would be great if someone could help me on this and give me a hint on how to best deal with these type of features in a durandal/knockout context.

当前解决方案

我现在已经尝试了一些方法,这是我想出的:

I have tried a few things out now and this is what I have come up with:

main.js:创建一个AppViewModel并将其添加到全局myapp对象(所有页面都需要).

  define([
          'durandal/system', 
          'durandal/app', 
          'durandal/viewLocator', 
          'plugins/router', 
          'myapp/myapp',
          'myapp/appViewModel'
          ],
    function (
        system, 
        app, 
        viewLocator, 
        router, 
        myapp,
        AppViewModel
    ) {
      system.debug(true);

      // Global view-model which will be required by sub-pages.
      myapp.app = new AppViewModel({
        defaultLanguage: 'cs'
      });

      app.title = 'Control Panel';

      app.configurePlugins({
          router:true,
          dialog: true,
          widget: true
      });

      app.start().then(function() {
          viewLocator.useConvention();
          app.setRoot('shell', 'entrance');
      });
  });

AppViewModel:处理语言事件

  //------------------------------------------------------------------
  // Global AppViewModel which holds the current language, 
  // listens for changes and notifies the current sub-page 
  // if is has registered a listener-function.
  //------------------------------------------------------------------
  define([ 'jquery', 'underscore', 'knockout' ], function($, _, ko) {

    "use strict";

    function AppViewModel(options) {

        if (!(this instanceof AppViewModel)) {
            throw new TypeError("AppViewModel constructor cannot be called as a function.");
        }

        this.options = options || {};   

        // Set the initial language.
        this.selectedLanguage = ko.observable(options.defaultLanguage);

        _.bindAll(this, 'languageChanged', 'onLanguageChange');
    }

    AppViewModel.prototype = {
        constructor: AppViewModel,
        //---------------------------------------------------------------
        // Calls listener when language changes. See shell.
        //---------------------------------------------------------------
        languageChanged: function(data, event) {
            if(!_.isNull(this.onLanguageChange)
                && !_.isUndefined(this.onLanguageChange) 
                && _.isFunction(this.onLanguageChange)) {
                this.onLanguageChange(data, event);
            }
        },
        //---------------------------------------------------------------
        // Listener that can be overridden by view-models of sub-pages.
        //---------------------------------------------------------------
        onLanguageChange: function(data, event) {
            // Override by ViewModel.
        },
        //---------------------------------------------------------------
        // Clear() should be called in deactivate callback of the 
        // view-model to stop being notified when the language changes.
        //---------------------------------------------------------------
        clear: function() {
            // Reset to empty function.
            this.onLanguageChange = function(data, event) {}
        }
    }

    return AppViewModel;    
  });

shell.js:

    define(['plugins/router', 'durandal/app', 'knockout', 'underscore', 'myapp/myapp'], function (router, app, ko, _, myapp) {

      return {
          router: router,
          languages: [
              {code: 'cs', label: 'čeština'},
              {code: 'sk', label: 'slovenský'},
              {code: 'en', label: 'english'},
              {code: 'de', label: 'deutsch'},
              {code: 'ru', label: 'русский'}
          ],
          app: myapp.app, // make app available to shell.html for the event handler. 
          activate: function () {
              return router.map([
               { route: ['', 'dashboard'],      moduleId: 'core/dashboard/pages/index',     title: 'Dashboard',         icon: 'fa fa-dashboard fa-fw',      nav: true },
               { route: 'attributes*details',   moduleId: 'core/attribute/pages/index',     title: 'Attribute',     icon: 'fa fa-puzzle-piece fa-fw',   nav: true, hash: '#/attributes' }
           ]).buildNavigationModel()
             .mapUnknownRoutes('core/dashboard/pages/index', 'not-found')
             .activate();
          }
    };
  });

shell.html:现在正在全局AppViewModel上调用languageChanged()

  <select class="form-control select2"
    data-bind="event: { change: app.languageChanged }, options:languages,
    optionsText:'label',
    optionsValue:'code',
    value:app.selectedLanguage"></select>       

最后是页面视图模型之一(当shell.html中的selecbox更改时会通知该页面)

  define(['jquery', 'knockout', 'myapp/myapp'], function ($, ko, myapp) {

      return {
        activate: function(data) {

                myapp.app.onLanguageChange = function(data, event) {
                    // Handle language stuff ...
                }
        },
            deactivate : function() {
          // Stop listening for language changes
                myapp.app.clear();
            }
      }
  });

嗯...我想知道是否有人读够了这一行.如果您这样做,谢谢您的时间.

Hmm ... I wonder whether anyone reads enough to get to this line. If you do, thanks for your time.

那么,这是可行的方法吗?还是还有一种最佳实践"的durandal方法?

So, is this a feasable way of doing it or is there a more "best practise" durandal way?

带有durandal发布/订阅机制的改进解决方案:

AppViewModel:现在仅发送消息-无需侦听器逻辑.

AppViewModel.prototype = {
    constructor : AppViewModel,
    // ---------------------------------------------------------------
    // Trigger message when language has changed.
    // ---------------------------------------------------------------
    languageChanged : function(data, event) {
        app.trigger('language:change', data);
    }
}

index.js:现在仅接收消息-无需在AppViewModel上注册侦听器.

  define(['durandal/app', 'jquery', 'knockout'], function (app, $, ko) {

      return {
        _langSubscriber: {},
        activate: function(data) {
            _langSubscriber = app.on('language:change').then(function(language) {
                // Handle language stuff ...
            });
        },
        deactivate : function() {
            _langSubscriber.off();
        }
      }
  });

正如埃里克·泰勒(Eric Taylor)所指出的,再次取消订阅很重要.如果不这样做,最终将导致大量订阅,并且很可能会发生内存泄漏.

As Eric Taylor pointed out, it is important to unsubscribe again. If you don't you will end up with A LOT of subscriptions and most likely a memory leak.

最诚挚的问候, 迈克尔

Best regards, Michael

推荐答案

我们所做的是使用单例config模块.如果您不熟悉Durandal中的singleton与instance模块,那么您应该知道这一点:如果您的模块返回对象文字,它将是singleton并将在应用程序会话期间保留在内存中.

What we did was use a singleton config module. If you're not familiar with singleton versus instance modules in Durandal, then you should know this: If your module returns an object literal, it will be singleton and will remain in memory for the duration of your application's session.

如果模块返回构造函数,那么Durandal将在每次访问或组合模块时使用该构造函数实例化该模块的实例,并在不再活动时释放该模块.

If your module returns a constructor function, then Durandal will use that constructor function to instantiate an instance of the module each time it is visited or composed, and will release the module when it is no longer active.

考虑以下简单的config模块:

define('config', ['durandal/app', 'knockout'],
    function (app, ko) {

        var currentUser = ko.observable('tester'),
            languages = ko.observableArray([
                {code: 'cs', label: 'čeština'},
                {code: 'sk', label: 'slovenský'},
                {code: 'en', label: 'english'},
                {code: 'de', label: 'deutsch'},
                {code: 'ru', label: 'русский'}
            ]);
    }

    return {
        currentUser: currentUser,
        languages: languages
    };
);

您需要在模块的所有位置访问此信息,您只需要该模块.另外,请确保config模块上需要更新UI或响应UI中的 的所有属性都是可观察到的.因此,例如,我注意到您的languages属性不是observableArray. UI可能必须响应此属性中的更新.

Everywhere throughout your modules that you need access to this information, you simply require the module. Also, make sure that any properties on your config module that need to update the UI, or respond to updates in the UI, are observables. So, for example, I noticed that your languages property is not an observableArray. It probably needs to be if the UI has to respond to updates in this property.

config模块的使用

Use of the config Module

define('someViewModel', ['durandal/app', 'knockout', 'config'],
    function (app, ko, config) {

        var languages = config.languages();
    }
);

请注意,我必须使用config.languages()上的括号取消对可观察数组的引用.

Note that I had to use the parentheses on config.languages() to de-reference the observable array.

如果还需要观察您在本地使用的config.languages(),那么您将:

If your local use of config.languages() also needs to be observable, then you would have:

var languages = ko.observableArray(config.languages());

代替上面的方法,您只需将config模块直接传递回您的显示对象文字中即可:

Instead of the above, you could simply pass the config module right back out in your revealing object literal:

return {
    config: config
};

,然后直接在您的视图中使用该模块.但是我不知道我是否喜欢这种方法.如果要在视图中使用注入的模块,我的首选是对其进行本地化.

and then use that module directly in your view. But I don't know if I like this approach. My preference is to localize injected modules if I intend to use them in a view.

另一种方法

您还可以使用Durandal的pub/sub在视图模型之间发送消息以响应语言等的变化,而不是使用可观察对象的直接连接.

You could also use Durandal's pub/sub to send messages between viewmodels in response to changes in the languages, etc., rather than use the direct-connect of observables.

这两种方法均有效;这取决于您的需求.

Either approach is valid; it just depends on your needs.

资源

在Pluralsight上查看John Papa的课程带有HTML5,Web API,Knockout和jQuery的单页应用.然后,看看John课程的Durandal版本:单页应用JumpStart .您将学到很多关于自己在做什么的东西.

Take a look at John Papa's course, Single Page Apps with HTML5, Web API, Knockout and jQuery, on Pluralsight. Then, take a look at the Durandal version of John's course: Single Page Apps JumpStart. You will learn a great deal about exactly what you're doing.

这篇关于杜兰达/淘汰赛-全球可观察的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆