3

Durandal を使用して SPA に取り組んでおり、特定のページ コンポーネントを表示するためのウィジェットを作成しました。Durandal Documentationに従って、ウィジェットは にあり、とapp/widgets/my-widgetで構成されます。viewmodel.jsview.html

ここで、同じウィジェットに別のビューを追加したいと考えています。実際には、「基本」ビューと「詳細」ビューがあり、使用される ViewModel にフラグがあります。

ViewModel はまったく同じで、不要なコードの重複を避けたいので、2 つの異なるウィジェットを作成したくありません。また、ビューの両方のバージョンを入れてview.html、ViewModel のフラグに基づいてどちらか一方だけを表示したくはありません。後でウィジェットに機能が追加されると、維持するのがすぐに悪夢になるからです。

1 つの答えは、ある種のView Compositionを実行することだと思います。この場合、構成するビューの名前は、フラグに基づいて ViewModel から返されますが、その方法がよくわかりません。

誰にもこれを行う方法がありますか?この問題に取り組むべき別の方法はありますか?


更新これは私がやりたいことの例です。workitem「作業項目」を表示するためのウィジェットがあります。ViewModel では、ワーク アイテムには、、、またはstatusの値を取ることができるプロパティがあります。作業項目のステータスに応じて、それに関して表示する必要がある情報 (したがってそのビュー) は完全に異なります。私がやりたいことは、次のようなものを設定することです:readyin-progresscompleteinvalid

-widgets
|
|-workitem
|  |-viewmodel.js
|  |-view-ready.html
|  |-view-in-progress.html
|  |-view-complete.html
|  |-view-invalid.html

...そして、ViewModel のプロパティに基づいてこれらのビューの 1 つを自動的に選択します。

4

3 に答える 3

3

マルチビュー ウィジェットに対するアプローチは、単純にifまたはvisibleバインディングを使用し、それをビュー タイプ (「コンパクト」、「展開」、「グループ化」など) を保持するビューモデルのオブザーバブルにバインドすることです。

datepicker ウィジェットの場合、ハイレベル ビューは次のとおりです (2 つのビューのみを表示)。

</div>
    <div data-bind="visible: viewMode() === 'months'">
        <div data-bind="compose: {model: 'widgets/datepicker/datepickerMonths', view: 'widgets/datepicker/datepickerMonths', activate: true, activationData: messageChannel }">    </div>
    </div>
    <div data-bind="visible: viewMode() === 'years'">
        <div data-bind="compose: {model: 'widgets/datepicker/datepickerYears', view: 'widgets/datepicker/datepickerYears', activate: true, activationData: messageChannel }">    </div>
    </div>
</div>

オブザーバブルを使用すると、viewModeビューを切り替えることができます。

この場合、バインディングを使用するvisible理由は 2 つあります。

  1. パフォーマンス: レイアウトは DOM にロードされたままになり、非表示になります。
  2. jQuery clickoutside プラグインはifバインディングによって混乱します (ifバインディングは、いわばユーザーのマウス ポインターの下から DOM を追い出し、ユーザーが外部をクリックしたと clickoutside プラグインに信じ込ませます)。

上記のアプローチでは、ウィジェットの単一の view.html ファイルで、動的に構成可能な複数のビューを参照できます。

デュランダルの構成とウィジェット システムを活用するために完全に作成された、日付ピッカーのパブリック SkyDrive (現在の OneDrive) フォルダーへのリンクを提供しまし。ビューの切り替えは、上で概説した手法を使用して行われます。

デートピッカーの特徴:

  • datepicker はマルチビュー ウィジェットです
  • 入力フィールドとポップオーバー ウィジェット (つまり、ウィジェット内のウィジェット) を構成します。
  • ハイレベル ビューは 3 つの異なるビューを動的に構成し、ユーザーの操作に応じてそれらを切り替えます
  • ビュー間の選択のルーティング、親ビューとの調整、および入力コントロールの更新のためのクライアント側メッセージ バスとして postal.js に依存しています (必要に応じて、Durandal の組み込みの pub/sub を使用できます)。
  • キーボード管理に jwerty.js を使用します
于 2014-03-06T16:46:54.933 に答える
2

あなたが提案したように、コンポジションを使用して適切なビューを返すこともできます。次のようなものかもしれません (ビューの場所戦略を使用):

localViewStrategy - これにより良い名前を思い付くことができます:)

define(['durandal/system', 'durandal/viewLocator'], function (system, viewLocator) {
    return function(settings) {
            var moduleId = system.getModuleId(settings.model);
            var viewName = settings.viewName;
            var path = moduleId.substring(0, moduleId.lastIndexOf('/') + 1) + viewName + '.html';

        return viewLocator.locateView(path, settings.area , settings.parent);
    };
});

ウィジェット ビュー モデル

define(function(){
    var ctor = function() {
        //assuming this is your default view?
        this.view = ko.observable('view-ready');
    };

    ctor.prototype.activate = function (settings) {
        var self = this;

        if(settings.view !== null){
            this.view(settings.view);
        }
    }
    return ctor;
});

view.html

<span data-bind="compose: { model: $data, strategy: 'path/to/strategy/localViewStrategy', 'viewName' : view, activate: false, preserveContext: true, cacheViews : false }">

</span>

ただし、これが最善の方法であるかどうかはわかりません。

于 2013-09-17T03:21:17.787 に答える
2

areaウィジェット ビューは、常に type であるため、通常のビューのように簡単に切り替えることはできませんpartial

それらを変更するには、おそらく上書きする必要がwidget.convertKindToModulePathありますwidget.convertKindToViewPath

https://github.com/BlueSpire/Durandal/issues/217の例を次に示します。

var oldConvert = widget.convertKindToModulePath;
widget.convertKindToModulePath: function(kind) {
    if (typeof(kind) == 'function') {
        return widget.mapKindToModuleId(kind());
    }
    return oldConvert(kind);
}

Update Widget は、ビュー内でオブザーバブルや計算などを使用して構築できます。

<!-- ko widget: getWidgetSettings() -->
<!-- /ko -->

getWidgetSettingsのようなものを返すko.computed代わりに (ステータスに応じて) になる可能性があります。{kind: 'workitem'}{kind: { id : 'workitem', status: 'statusId'}}

OOTB Durandal はkindwidget.convertKindToModulePathが.widget.convertKindToViewPathstringobject

以下に沿って何かを始める必要があります。

var oldconvertKindToModulePath = widget.convertKindToModulePath;
widget.convertKindToModulePath = function( kind ) {
    if ( typeof(kind) == 'object' ) {
        return  'widgets/' + kind.id + '/viewmodel';
    }
    return oldconvertKindToModulePath(kind);
};

var oldconvertKindToViewPath = widget.convertKindToViewPath;
widget.convertKindToViewPath = function( kind ) {
    if ( typeof(kind) == 'object' ) {
        return 'widgets/' + kind.id + '/' + statusViewMap[statusId] ;
    }
    return oldconvertKindToViewPath(kind);
};

これをウィジェットとして実装する代わりに、これを標準モジュールとして実装し、getViewメソッドを活用することを検討してください。これが例です

http://dfiddle.github.io/dFiddle-2.0/#view-composition/getView

define(['knockout'], function(ko) {

  var roles = ['default', 'role1', 'role2'];
  var role = ko.observable('default');

  var getView = ko.computed(function(){
        var roleViewMap = {
          'default': 'viewComposition/getView/index.html',
          role1: 'viewComposition/getView/role1.html',
          role2: 'viewComposition/getView/role2.html'
        };

        this.role = (role() || 'default');

        return roleViewMap[this.role];
  });


  return {
    showCodeUrl: true,
    roles: roles,
    role: role,
    getView: getView,
    propertyOne: 'This is a databound property from the root context.',
    propertyTwo: 'This property demonstrates that binding contexts flow through composed views.'
  };


});

この例ではシングルトンを使用していますが、これは複数の独立したワークアイテムを持つ要件を満たさないため、コンストラクター パターンを使用して書き直す必要があります。

お気軽にフォークしてください。プルリクエストを受け付けています:)。

于 2013-09-16T21:27:25.083 に答える