2

私は以前バックボーンを扱っていましたが、道場でこの種のパターンを実現する同様の方法があるかどうか疑問に思っていました。ルーターがあり、ビューを 1 つずつ (レイヤーのように) 個別に渡し、インターン機能を別の場所 (ビュー内など) に追加できるため、コードは非常にモジュール化されており、新しいものを非常に簡単に変更/追加できます。このコードは実際には jquery にあり (以前のプロジェクトから来ています)、 jquery/backbone.js の下で単一のアプリケーション ページを開発するための「一般的な」基本パターンです。

main.js

var AppRouter = Backbone.Router.extend({

routes: {
        "home"                  : "home"},
home: function(){

        if (!this.homeView) {
            this.homeView= new HomeView();
        }
        $('#content').html(this.homeView.el);

        this.homeView.selectMenuItem('home-link');
    }};

utils.loadTemplate(['HomeView'], function() {
    app = new AppRouter();
    Backbone.history.start();
});

ユーティリティ.js

loadTemplate: function(views, callback) {

        var deferreds = [];

        $.each(views, function(index, view) {
            if (window[view]) {
                deferreds.push($.get('tpl/' + view + '.html', function(data) {
                    window[view].prototype.template = _.template(data);
                }));
            } else {
                alert(view + " not found");
            }
        });

        $.when.apply(null, deferreds).done(callback);
    }};

HomeView.js

window.HomeView = Backbone.View.extend({

    initialize:function () {
        this.render();
    },

    render:function () {
        $(this.el).html(this.template());
        return this;
    }

});

基本的に、html テンプレートを渡すだけです。このパターンは、次のリンクを使用してどこでも呼び出すことができます。

<li class="active"><a href="#home"><i class="icon-home"></i> Dashboard</a></li>

または、Dojo ボイラープレートを使用してこれを実装する最良の方法は何ですか。

4

1 に答える 1

1

このテーマの「ボイラープレート」は dojox.mvc アプリです。参考はこちら。

別の側面から、私がしばらく前に行ったことを見てください。「コントローラー」の抽象を設定し、その実装でビューを構築します。

概要

次に、menu.onClickで次のことを行うアプリケーションコントローラーがあります

  1. 読み込みアイコンを起動し、
  2. 現在のペインをアンロードします (フォームが汚れていない場合)
  3. 必要なモジュールをロードします (メインメニューストアで定義された「ルート」)
  4. 新しく要求されたものでビュー ペインを設定する

各ビューは、単純な server-html ページであるか、宣言された 'oocms' コントローラー モジュールで構築されています。抽象実装の最も単純な例はこちらです。それぞれがアンロード機能と、ティアダウンでストアまたはイベントフックを逆参照するスタートアップ機能を実装します。次に、セットアップでストアがロードされるなどをアサートします。

テンプレートを使用する場合は、ビューを dijit._TemplatedMixin に基づいて作成します。

編集

これは私の oocms セットアップの簡単な説明です。ここでは、BorderLayout に基づくのではなく、ContentPanes にします。

上記で宣言されたビューを表す 1 つの項目を含む、メニューの JSON の例

 {
    identifier: 'view',
    label: 'name',
    items: [
      { name: 'myForm', view: 'App.view.MyForm', extraParams: { foo: 'bar' } }
    ]
 }

ファイル 'AppPackagePath/Application.js' のベース アプリケーション コントローラー

コードはテストされていませんが、そのようなセットアップがどのように実装されるかについて良い印象を与えるはずです。

 define(['dojo/_base/declare', 
"dojo/_base/lang",
"dijit/registry",
"OoCmS/messagebus", // dependency mixin which will monitor 'notify/progress' topics'
"dojo/topic",
"dojo/data/ItemFileReadStore",
"dijit/tree/ForestStoreModel",
"dijit/Tree"

], function(declare, lang, registry, msgbus, dtopic, itemfilereadstore, djforestmodel, djtree) {
    return declare("App.Application", [msgbus], {

        paneContainer: NULL,
        treeContainer: NULL,
        menuStoreUrl: '/path/to/url-list',
        _widgetInUse: undefined,
        defaultPaneProps: {},
        loading: false, // ismple mutex
        constructor: function(args) {
            lang.mixin(this, args);
            if(!this.treeContainer || !this.paneContainer) {
                console.error("Dont know where to place components")
            }
            this.defaultPaneProps = {
                id: 'mainContentPane'
            }
            this.buildRendering();
        },
        buildRendering: function() {
            this.menustore = new itemfilereadstore({
                id: 'appMenuStore',
                url:this.menuStoreUrl
            });
            this.menumodel = new djforestmodel({
                id: 'appMenuModel',
                store: this.menustore
            });
            this.menu = new djtree( {
                model: this.menumodel,
                showRoot: false,
                autoExpand: true,
                onClick: lang.hitch(this, this.paneRequested) // passes the item
            })
                            // NEEDS a construct ID HERE
            this.menu.placeAt(this.treeContainer)
        },
        paneRequested: function(item) {
            if(this.loading || !item) {
                console.warn("No pane to load, give me a menustore item");
                return false;
            }
            if(!this._widgetInUse || !this._widgetInUse.isDirty()) {
                dtopic.publish("notify/progress/loading");
                this.loading = true;
            }
            if(typeof this._widgetInUse != "undefined") {
                if(!this._widgetInUse.unload()) {
                    // bail out if widget says 'no' (isDirty)
                    return false;
                }
                this._widgetInUse.destroyRecursive();
                delete this._widgetInUse;
            }

            var self = this,
                modules = [this.menustore.getValue(item, 'view')];
            require(modules, function(viewPane) {
                self._widgetInUse = new viewPane(self.defaultProps);

                            // NEEDS a construct ID HERE

                self._widgetInUse.placeAt(this.paneContainer)
                self._widgetInUse.ready.then(function() {
                    self.paneLoaded();
                })
            });
            return true;
        },
        paneLoaded: function() {
            // hide ajax icons
            dtopic.publish("notify/progress/done");
            // assert widget has started
            this._widgetInUse.startup();
            this.loading = false;
        }
    })
})

ファイル 'AppPackagePath/view/AbstractView.js' の AbstractView:

define(["dojo/_base/declare",
"dojo/_base/Deferred",
"dojo/_base/lang",
"dijit/registry",
"dijit/layout/ContentPane"], function(declare, deferred, lang, registry, contentpane) {

    return declare("App.view.AbstractView", [contentpane], {
        observers: [],      // all programmatic events handles should be stored for d/c on unload
        parseOnLoad: false,
        constructor: function(args) {
            lang.mixin(this, args)
            // setup ready.then resolve
            this.ready = new deferred();
            // once ready, create
            this.ready.then(lang.hitch(this, this.postCreate));
            // the above is actually not nescessary, since we could simply use onLoad in contentpane
            if(typeof this.content != "undefined") {
                this.set("content", this.content);
                this.onLoad();
            } else if(typeof 'href' == "undefined") {
                console.warn("No contents nor href set in construct");
            }
        },
        startup : function startup() {
            this.inherited(arguments);
        },
        // if you override this, make sure to this.inherited(arguments);
        onLoad: function() {
            dojo.parser.parse(this.contentNode);
            // alert the application, that loading is done
            this.ready.resolve(null);
            // and call render
            this.render();
        },
        render: function() {
            console.info('no custom rendering performed in ' + this.declaredClass)
        },
        isDirty: function() { return false; },
        unload: function() {
            dojo.forEach(this.observers, dojo.disconnect);
            return true;
        },
        addObserver: function() {
            // simple passthrough, adding the connect to handles
            var handle = dojo.connect.call(dojo.window.get(dojo.doc), 
                arguments[0], arguments[1], arguments[2]);
            this.observers.push(handle);
        }
    });

});

ファイル 'AppPackagePath/view/MyForm.js' の実装サンプルを表示:

define(["dojo/_base/declare",
"dojo/_base/lang",
"App/view/AbstractView",
// the contentpane href will pull in some html
// in the html can be markup, which will be renderered when ready
// pull in requirements here
"dijit/form/Form",  // markup require
"dijit/form/Button" // markup require
], function(declare, lang, baseinterface) {
    return declare("App.view.MyForm", [baseinterface], {
        // using an external HTML file
        href: 'dojoform.html',
        _isDirty : false,
        isDirty: function() {
            return this._isDirty;
        },
        render: function() {
            var self = this;
            this.formWidget = dijit.byId('embeddedForm') // hook up with loaded markup
            // observer for children
            dojo.forEach(this.formWidget._getDescendantFormWidgets(), function(widget){
                if(! lang.isFunction(widget.onChange) )
                    console.log('unable to observe ' + widget.id);
                self.addObserver(widget, 'onChange', function() {
                    self._isDirty = true;
                });
            });
        // 
        },

        // @override
        unload: function() {
            if(this.isDirty()) {
                var go = confirm("Sure you wish to leave page before save?")
                if(!go) return false;
            }
            return this.inherited(arguments);

        }
    })
});
于 2012-07-28T18:13:40.773 に答える