1

baseViewModel.jsDurandalJS アプリで、他のページからの継承を処理するためのより良い方法を探しています。これが私の現在のアプリ構造です:

  • baseViewModel.js
  • メインシェル
    • グループ 1 シェル
      • ページ1
      • ページ2
    • グループ 2 シェル
      • ページ1
      • ページ2

これらのページはすべて、次のようないくつかの共通機能 (baseViewModel.js) を共有しています。

  • isLoading: ページに何かがロードされているかどうかを確認するオブザーバブル
  • isValid: フォームが有効かどうかを確認します
  • 追加および削除されたアイテムの afterAdd および beforeRemove 効果
  • フォームのキーボード ショートカット
  • テンプレートスイッチャー機能

現時点での仕組み

  1. 内部のすべては、self が Window オブジェクトへの参照であるようにbaseViewModel.js宣言されています。次に例を示します。self.*

    self.showElement = function(elem) {
      if (elem.nodeType === 1) $(elem).hide().fadeIn();
    }
    
    self.fadeRemove = function(elem) {
      if (elem.nodeType === 1) $(elem).fadeOut(500, function() { $(elem).remove(); });
    }
    
  2. メイン シェル内で1 回baseViewModel.jsだけ定義し、次のようにアプリ全体でアクセスします。

    <tbody data-bind="foreach: { data: dataArr, afterAdd: showElement, beforeRemove: fadeRemove }">
    

    またはビューモデル内:

    Page1.load = function() {
      self.isLoading(true);
      // get data
    };
    

私が試したこと

プロトタイプの継承を調べて、なんとか機能させることができましたが、実装方法についてはわかりません。Base内部に関数を作成baseViewModel.jsし、シングルトンとして返しました。次に、ページの1つで、次のことを行いました。

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

  var Page1 = function() {};

  Page1.prototype = base;

  return Page1;

});

Base で宣言された関数は、Page1 のビューとビュー モデルで正常に動作しますが、問題は、Page1 と共に読み込まれたすべてのモジュールで動作する必要があることです。これも:

  • グループ 1 シェル ビュー (ベース内の機能をアクティブにするボタンがあります)
  • isLoadingメイン シェル ビュー (ローディング バーはBase 内の Observableの値に基づいて表示されます)

これらを機能させるには、ファイルを定義し*.prototype = base;、各ビュー モデルに同じものを適用する必要がありました。

では、継承を処理するためのより良い方法はありますか? そうでない場合、ベースを一度だけ宣言し、それをすべてのサブページに適用する方法はありますか?

ありがとう!

4

2 に答える 2

3

ファクトリ アプローチと組み合わせてプロトタイピング継承を使用しましたが、これまでのところかなりうまく機能しているようです。私は単純な継承のみを望んでいたので、これは一部の継承ライブラリのような完全な機能を備えた継承アプローチではありません。

PW が示唆したように、基本ビュー モデルは requireJs モジュールにラップされています。

これは単純化されたコード例ですが、機能するはずです。意味がわからない場合はお知らせください。詳しく説明できます。

ベース ビュー モデル

define(function (require) {
    "use strict";

    function ViewModelBase() {

        //this is the activate function that Durandal will call
        function activate() {

            //call activate on derived vm
            if (this.onActivate) {
                return this.onActivate.apply(this, arguments);
            } else {
                return true;
            }
        }

        //validate view model and display all remaining errors
        function validate() {
            // passing 'this' so that the viewmodel instance is evaluated at the time it's needed 
            return validation.validateViewModel(this);   //this is knockout validation
        }

        //exports
        this.activate = activate;
        this.validate = validate;
    };

    //return the constructor (non-singleton)
    return ViewModelBase;
});

工場

define(function (require) {
    "use strict";

    return {
        createViewModel: function (ctor) {
            ctor.prototype = new (require("view-model-base"))();
            ctor.prototype.constructor = ctor;
            return ctor;
        }
    };

});

派生ビュー モデル

define(function (require) {
    "use strict";

    var factory = require("fusion/factory");
    var HomeViewModel = factory.createViewModel(function () {
        var __viewModel = this; //use __viewModel variable to access this view model instance.

        function onActivate() {
            //add application startup logic here

            //access public functions on the base view model like this:
            __viewModel.validate();

            return true;
        }
        this.onActivate = onActivate;
    });

    //returns the constructor (non-singleton)
    return HomeViewModel;

    //to return a singleton instead, do this
    return new HomeViewModel();


}); // END :: define statement
于 2013-10-11T14:05:05.310 に答える
0

ミックスインは、モジュール間で機能を共有するために考慮される可能性のある別のパターンです。 @Joseph Gabrielの回答は間違いなく正しいことに注意してください。ミックスインは、一部のユース ケースでは軽量な代替手段にすぎませんが、いかなる種類の継承も提供しません。

編集Page例とライブ バージョンが更新され、a) 共通、b) 共有、c) すべてのインスタンス間で一意であるために配置する必要がある場所をより適切に示しています。

common.js

/*globals define */
define(['jquery', 'knockout'], function($, ko) {
    "use strict";

    // common must return a singleton
    return {
        commonObservable: ko.observable('commonObservable: Defined in common.js.  Click me...'),
        showElement: function( model, elem ) {
            var target = elem.currentTarget;
            if ( target.nodeType === 1 ) {
                $(target).hide().fadeIn();
            }
        },
        fadeRemove: function( model, elem ) {
            var target = elem.currentTarget;
            if ( target.nodeType === 1 ) {
                $(target).fadeOut(500, function() {
                    $(target).remove();
                });
            }
        }
    };
});

ページ ビュー モデル

/*globals define */
define(['durandal/app', 'jquery', 'knockout', './common'], function( app, $, ko, common ) {
    "use strict";
    var Page = function() {
        this.uniqueObservable = ko.observable('uniqueObservable: Page property. Click me... ');
    };

    // Extend the prototype with the common properties and
    // `extend` it further with shared properties
    $.extend(true, Page.prototype, common, {
         sharedObservable : ko.observable('sharedObservable: mixed in after common. Click me...')
    });


    return Page;

});

ページビュー

<div>
    <h1>mixin sample</h1>

    <ul class="unstyled ">
        <li data-bind="text: commonObservable, click: fadeRemove"></li>
        <li data-bind="text: sharedObservable, click: fadeRemove"></li>
        <li data-bind="text: uniqueObservable, click: fadeRemove"></li>
    </ul>    
</div>

ライブ バージョンはhttp://dfiddle.github.io/dFiddle-2.0/#extras/mixinで入手できます。

于 2013-10-14T17:05:10.563 に答える