【问题标题】:Backbone Marionette Layout Region not closing when returning to module返回模块时骨干木偶布局区域未关闭
【发布时间】:2013-12-03 15:38:17
【问题描述】:

我的 Backbone Marionette 应用程序的布局区域无法正常工作。我的应用程序是使用 Require 模块构建的,当模块第二次返回时,其中一些模块的区域无法自行关闭。第一次通过区域按预期关闭,但在返回时布局对象不再包含它在第一次访问期间所做的区域对象:我正在使用浏览器调试器来确定这种差异。

这是我的模块代码:-

define(["marionette", "shell/shellapp", "interaction/videoreveal/model", "interaction/videoreveal/controller", "utils/loadingview", "utils/imagepreloader"], function(Marionette, shellApp, Model, Controller, LoadingView, imagePreloader){
    var currentModuleModel = shellApp.model.get("currentInteractionModel");                      // get module name from menu model
    var Module = shellApp.module(currentModuleModel.get("moduleName"));           // set application module name from menu model

    Module.init = function() {                                                    // init function called by shell
        //trace("Module.init()");
        Module.model = new Model(shellApp.interactionModuleData);             // pass in loaded data
        Module.controller = new Controller({model: Module.model, mainRegion:shellApp.mainRegion});             // pass in loaded data and region for layout
        Module.initMain();
    };

    Module.initMain = function() {
        trace("Module.initMain()");
        shellApp.mainRegion.show(new LoadingView());

        // do some preloading
        var imageURLs = this.model.get('imagesToLoad');
        imagePreloader.preload(imageURLs, this.show, this);


    };

    Module.show = function() {
        Module.controller.show();
    };

    Module.addInitializer(function(){
        //trace("Module.addInitializer()");

    });
    Module.addFinalizer(function(){
        //trace("Module.addFinalizer()");
    });

    return Module;
});

这是处理布局和视图的控制器类:-

define(["marionette", "shell/vent", "shell/shellapp", "interaction/videoreveal/layout", "interaction/videoreveal/views/mainview", "ui/feedbackview", "ui/videoview"], function(Marionette, vent, shellApp, Layout, MainView, FeedbackView, VideoView){

    return Marionette.Controller.extend({
        initialize: function(options){
            trace("controller.initialize()");
            // store a region that will be used to show the stuff rendered by this component
            this.mainRegion = options.mainRegion;
            this.model = options.model;
            this.model.on("model:updated", this.onModelUpdate, this);
            this.layout = new Layout();
            this.layout.render();
            this.mainView = new MainView({model:this.model, controller:this});
            this.feedbackView = new FeedbackView({feedbackBoxID:"vrFeedbackBox"});
            this.videoView = new VideoView({videoContainerID:"vrVideoPlayer"});
            vent.on("feedbackview:buttonclicked", this.onFeedbackClick, this);
            vent.on("videoview:buttonclicked", this.onVideoClick, this);
        },
        // call the "show" method to get this thing on screen
        show: function(){
            // get the layout and show it
            this.mainRegion.show(this.layout);
            this.model.initInteraction();
        },
        initFeedback: function (index) {
            this.model.set("currentItem", this.model.itemCollection.models[index]);
            this.model.set("itemIndex", index);
            this.model.initFeedback();
        },
        initVideo: function (index) {
            this.model.set("currentItem", this.model.itemCollection.models[index]);
            this.model.set("itemIndex", index);
            this.model.initVideo();
        },
        finalizer: function() {
            this.layout.close();
        },
        // events

        onFeedbackClick: function(e) {
            this.layout.overlayRegion.close();
        },
        onVideoClick: function(e) {
            this.layout.overlayRegion.close();
        },
        onFinishClick: function() {
            this.model.endInteraction();
        },
        onFeedbackClosed: function() {
            this.layout.overlayRegion.off("close", this.onFeedbackClosed, this);
            if (this.model.get("currentItem").get("correct") === true) {
                this.model.initThumb();
            }
        },
        onModelUpdate: function() {
            trace("controller onModelUpdate()");
            switch (this.model.get("mode")) {
                case "initInteraction":
                    this.layout.mainRegion.show(this.mainView);
                    break;
                case "initFeedback":
                    this.layout.overlayRegion.on("close", this.onFeedbackClosed, this);
                    this.feedbackView = new FeedbackView({feedbackBoxID:"vrFeedbackBox"})
                    this.feedbackView.setContent(this.model.get("currentItem").get("feedback"));
                    this.layout.overlayRegion.show(this.feedbackView    );
                    break;
                case "initVideo":

                    this.layout.overlayRegion.show(new VideoView({videoContainerID:"vrVideoPlayer"}));
                    break;


                case "interactionComplete":
                    vent.trigger('interactionmodule:completed', this);
                    vent.trigger('interactionmodule:ended', this);
                    break;
            }
        }
    });
});

这里是 FeedbackView 类:-

define(['marionette', 'tweenmax', 'text!templates/ui/feedbackWithScrim.html', 'shell/vent'], function (Marionette, TweenMax, text, vent) {
    return Marionette.ItemView.extend({
        template: text,
        initialize: function (options) {
            this.model = options.model;
            this.content = options.content;                 // content to add to box
            this.feedbackBoxID = options.feedbackBoxID;     // id to add to feedback box
            this.hideScrim = options.hideScrim;             // option to fully hide scrim

        },
        ui: {
            feedbackBox: '.feedbackBox',
            scrimBackground: '.scrimBackground'
        },
        events : {
            'click button': 'onButtonClick'                 // any button events within scope will be caught and then relayed out using the vent

        },
        setContent: function(content) {
            this.content = content;

        },

        // events
        onRender: function () {
            this.ui.feedbackBox.attr("id", this.feedbackBoxID);
            this.ui.feedbackBox.html(this.content);
            if (this.hideScrim) this.ui.scrimBackground.css("display", "none");
            this.$el.css('visibility', 'hidden');
            var tween;
            tween = new TweenMax.to(this.$el,0.5,{autoAlpha:1});


        },

        onButtonClick: function(e) {
            trace("onButtonClick(): "+ e.target.id);
            vent.trigger("feedbackview:buttonclicked", e.target.id)         // listen to this to catch any button events you want
        },


        onShow : function(evt) {
            this.delegateEvents();      // when rerendering an existing view the events get lost in this instance. This fixes it.
        }


    });
});

知道为什么在重新启动模块时该区域没有保留在布局中,或者我可以做些什么来纠正这个问题?

非常感谢,

山姆

【问题讨论】:

  • 由于在模块停止并重新启动时会出现问题,因此查看模块的初始化程序和终止程序代码会很有用。如果我不得不冒险猜测,我敢打赌布局在终结器中关闭,并且在重新启动时永远不会重新创建。
  • 嗨,克里斯,感谢您回复我。我已将模块代码添加到上面的帖子中。这是否进一步说明了可能发生的情况?
  • 我想看看布局在上下文中的用法。您创建和关闭视图的方式没有任何问题(尽管@ekeren 提出了关于存储反馈视图的引用的有效观点),因此在更广泛的背景下查看它们将有助于了解正在发生的事情。跨度>
  • 谢谢 Chris,我已经在上面完整添加了 Controller 类......
  • 我没有在模块或控制器中看到任何进行任何类型清理的代码。我所看到的是模块/控制器的布局通过控制器的show 调用中的应用程序区域显示。所以发生的事情是,当显示您的其他模块之一的layout 时,该模块的layoutclosed,使其无法使用,如下@ekeren 所述。我会继续添加带有具体建议的答案。

标签: javascript backbone.js requirejs marionette


【解决方案1】:

好的....经过多次调试,我最终到达了那里。如果不是这个线程上其他人的慷慨帮助,我根本不会到达那里,所以谢谢!

Chris Camaratta 的解决方案无疑将我推向了正确的方向。我在 Controller 类中得到了一个僵尸布局视图。我决定将我的很多 on listener 切换为 listenTo listeners,以使他们的解耦和解除绑定更简单,希望更有效。关键的变化是触发 Controller 类的 close 方法。我应该一直发生这种情况,但老实说,这是我第一次陷入这种混乱,它以前一直有效,而无需这样做。无论如何,希望吸取教训。 Marionette 在为您关闭、解除绑定和处理所有这些东西方面做得很好,但它并不能做所有事情,剩下的就是您的责任。这是 Module 类的关键修改:-

    Module.addFinalizer(function(){
        trace("Module.addFinalizer()");
        Module.controller.close();
    });

这是我更新的控制器类:-

    define(["marionette", "shell/vent", "shell/shellapp", "interaction/videoreveal/layout", "interaction/videoreveal/views/mainview", "ui/feedbackview", "ui/videoview"], function(Marionette, vent, shellApp, Layout, MainView, FeedbackView, VideoView){

    return Marionette.Controller.extend({
        initialize: function(options){
            trace("controller.initialize()");
            // store a region that will be used to show the stuff rendered by this component
            this.mainRegion = options.mainRegion;
            this.model = options.model;
            this.listenTo(this.model, "model:updated", this.onModelUpdate);
            this.listenTo(vent, "feedbackview:buttonclicked", this.onFeedbackClick);
            this.listenTo(vent, "videoview:buttonclicked", this.onVideoClick);
        },
        // call the "show" method to get this thing on screen
        show: function(){
            // get the layout and show it
            // defensive measure - ensure we have a layout before axing it
            if (this.layout) {
                this.layout.close();
            }
            this.layout = new Layout();
            this.mainRegion.show(this.layout);
            this.model.initInteraction();
        },
        initFeedback: function (index) {
            this.model.set("currentItem", this.model.itemCollection.models[index]);
            this.model.set("itemIndex", index);
            this.model.initFeedback();
        },
        initVideo: function (index) {
            this.model.set("currentItem", this.model.itemCollection.models[index]);
            this.model.set("itemIndex", index);
            this.model.initVideo();
        },
        onClose: function() {
            trace("controller onClose()");
            if (this.layout) {
                this.layout.close();
            }
        },
        // events
        onFeedbackClick: function(e) {
            this.layout.overlayRegion.close();
        },
        onVideoClick: function(e) {
            this.layout.overlayRegion.close();
        },
        onFinishClick: function() {
            this.model.endInteraction();
        },
        onFeedbackClosed: function() {
            if (this.model.get("currentItem").get("correct") === true) {
                this.model.initThumb();
            }
        },
        onModelUpdate: function() {
            trace("controller onModelUpdate()");
            switch (this.model.get("mode")) {
                case "initInteraction":
                    this.layout.mainRegion.show(new MainView({model:this.model, controller:this}));
                    break;
                case "initFeedback":
                    var feedbackView = new FeedbackView({feedbackBoxID:"vrFeedbackBox", controller:this});
                    feedbackView.setContent(this.model.get("currentItem").get("feedback"));
                    this.layout.overlayRegion.show(feedbackView);
                    this.listenTo(this.layout.overlayRegion, "close", this.onFeedbackClosed);
                    break;
                case "initVideo":
                    this.layout.overlayRegion.show(new VideoView({videoContainerID:"vrVideoPlayer"}));
                    break;
                case "interactionComplete":
                    vent.trigger('interactionmodule:completed', this);
                    vent.trigger('interactionmodule:ended', this);
                    break;
            }
        }
    });
});

【讨论】:

  • 感谢您揭露您的问题并展示您如何解决此问题。我在这里遇到了同样的问题,我按照你的回答来解决它。谢谢。
【解决方案2】:

如果我正确理解您的问题,您的观点在关闭并重新打开后效果不佳。

看起来您正在使用关闭后的布局/视图,并保留它们以备将来与这些参考一起使用:

this.feedbackView = new FeedbackView();

Marionette 不喜欢这样,一旦你关闭了一个视图,就不要再使用它了。查看这些问题:

我建议您不要存储这些视图,而是在显示它们时重新创建它们

layout.overlayRegion.show(new FeedbackView());

【讨论】:

  • 如果我正确阅读了 OPs 问题,则布局的区域不可用。我认为布局本身正在发生这样的事情,并且将此修复应用于布局应该可以解决他的问题。
  • 感谢 ekeren 和 Chris。看来你已经找到问题了。当重新应用到 overlayRegion 时,FeedbackView 确实存在并显示在 DOM 中,但它丢失了它的事件并在视图本身中重新授权它们,或者布局 onShow 似乎无法修复它。当我尝试关闭 onFeedbackClick 函数中的 overlayRegion 时,会发生错误。当我查看调试器时(在第二次查看中断时),布局的 regionManager 区域对象确实存在,但它们不包含我第一次看到的 el 或通常事件。有任何想法吗?再次感谢:-)
【解决方案3】:

@ekeren 的回答基本正确;我只是在扩展它。以下是一些我认为可以解决您的问题的具体建议。

由于您使用的是区域,因此您可能不需要提前创建视图:

initialize: function(options) {
    this.mainRegion = options.mainRegion;
    this.model = options.model;
    this.model.on("model:updated", this.onModelUpdate, this);
    vent.on("feedbackview:buttonclicked", this.onFeedbackClick, this);
    vent.on("videoview:buttonclicked", this.onVideoClick, this);
},

相反,只需根据需要动态创建它们:

onModelUpdate: function() {
    switch (this.model.get("mode")) {
        case "initInteraction":
            this.layout.mainRegion.show(new MainView({model:this.model, controller:this}));
            break;

        case "initFeedback":
            var feedbackView = new FeedbackView({feedbackBoxID:"vrFeedbackBox"})
            feedbackView.setContent(this.model.get("currentItem").get("feedback"));
            this.layout.overlayRegion.show(feedbackView);
            this.layout.overlayRegion.on("close", this.onFeedbackClosed, this);
            break;

        case "initVideo":
            this.layout.overlayRegion.show(new VideoView({videoContainerID:"vrVideoPlayer"}));
            break;

        case "interactionComplete":
            vent.trigger('interactionmodule:completed', this);
            vent.trigger('interactionmodule:ended', this);
            break;
    }
}

布局有点特殊,因为它可以在多个地方关闭,但原则适用:

show: function(){
    // defensive measure - ensure we have a layout before axing it
    if (this.layout) {
        this.layout.close();
    }
    this.layout = new Layout();
    this.mainRegion.show(this.layout);
    this.model.initInteraction();
},

有条件地清理布局:

finalizer: function() {
    if (this.layout) {
        this.layout.close();
    }
},

【讨论】:

  • 嗨,克里斯,非常感谢您的帮助。我在上面添加了您的调整,但遗憾的是我仍然收到相同的错误 - TypeError: 'undefined' is not an object (evalating 'this.layout.overlayRegion.close') - 这是在控制器的 onFeedbackClick 方法中抛出的。布局的 overlayRegion 以某种方式丢失了区域对象的某些部分,例如其关闭、重置等功能。奇怪的是,当模块重新启动时,所有这些东西都在那里,但是一旦显示视图,该区域就会突然失去这些东西。我在第一篇文章中添加了查看代码以防万一。再次感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多