将数据从一个Backbone视图传递到另一个视图

3
假设我有以下的Backbone视图,其中包含加载两个链接,一个链接显示“test1”,另一个链接显示“test2”。
我绑定了点击事件,并且获取被点击的链接的HTML内容并将其存储在clickedHtml变量中。
现在,这个视图是由Backbone路由器加载的。
当用户点击这两个链接(test1或test2)中的任意一个时,路由器将加载另一个名为“main”的视图。
那么,我应该如何将“clickedHtml”变量传递给那个视图?
我应该使用LocalStorage吗?
我应该像全局变量window.clickedHtml一样声明它吗?
还有更好的方法吗?
谢谢!
// file: views/test.js
            define([
                'jquery', 
                'underscore', 
                'backbone'
            ], function($, _, Backbone) {

                var Test = Backbone.View.extend({

                    el : '.test',

                    initialize : function () {

                        var that = this;

                        that.$el.html('<a href="#/main">test1</a><br /><a href="#/main">test2</a>');


                    },

                    events : {

                        'click .test a' : 'click'

                    },

                    click : function (e) {

                        var clickedHtml = $(e.target).html();

                    }

                return Test;

            });

这是我的路由器:

// file: router.js

    define([
        'jquery', 
        'underscore', 
        'backbone',
        'views/test',
        'views/main'
    ], function ($, _, Backbone, Test, Main) {

        var Router = Backbone.Router.extend({

            routes: {
                '' : 'home',
                'test' : 'test'
            }
        });

        var initialize = function () {

            var router = new Router();

            router.on('route:home', function () {

                var main = new Main();

            });

            router.on('route:test', function () {

                var test = new Test();
            });

            Backbone.history.start();
        }

        return { 

            initialize : initialize 
        }
    });

你可以在一个视图中包含两个视图,这样你就可以在主视图中访问这两个子视图的所有变量。 - crenckly
5个回答

5
基本上,您应该使用Backbone.Event:(或Marionette中的等效项)
//Declaration
var notificationService = {};
_.extend(notificationService, Backbone.Events);

//Used by listener
notificationService.on("alert", function(o) {
   alert(o);
});

//Used by publisher 
notificationService.trigger("alert", {foo:"bar"});

真正的问题是它是如何从一个视图传递到另一个视图的?我认为你有两个选项:
1.在初始化期间从一个视图向另一个视图冒泡notificationService。
2.使用 requirejs 模型包装 notificationService(创建“几乎全局”的 notificationService 可以通过 requirejs 传递)。
虽然我不喜欢单例模式,但是在这种情况下,可以轻松地通过 requirejs 在每个模型中注入一个 singleton notificationService 对象,这会很方便。
编辑:另一种选择是“快速且简单”的方法,只需使用 jQuery 在 DOM 上触发事件(特别是 body 元素),并在另一个视图中监听 body。
 //on Listening view, after DOM is ready
$( "body" ).on( "alert", function( event, param1, param2 ) {
  alert( param1 + "\n" + param2 );
});

 //on Triggering view, after DOM is ready
$( "body").trigger( "alert", [ "Custom", "Event" ] );

注意:

请注意,一旦监听视图被关闭,它必须从事件监听器中解除绑定/关闭(unbind/off),以避免内存泄漏。


这是我一直在使用的方法。请参见http://stackoverflow.com/questions/20181679/call-a-function-in-another-marionette-itemview/20182584#20182584 但是,由于您正在使用requirejs,因此您将不得不对其进行包装,并使其至少在所有视图中可用。 - Yurui Zhang
如果你将notificationService设为全局变量,你就可以在视图中使用.listenTo来订阅该对象的事件(类似于我下面发布的答案)。 - 1nfiniti

4

从架构角度来看,你的目标应该是保持代码的通用性和可重用性。

在这种情况下,你不想做的主要事情之一就是直接从一个对象传递引用到另一个对象 - 如果你最终改变了其中一个对象的设置,或者你还需要从另一个对象传递数据,这可能会非常混乱。

在这种情况下广泛使用的一种设计模式是中介者。也被称为“发布/订阅”,你可以有一个集中的独立对象来调解对象之间的信息。某些对象将发布信息,其他对象可以订阅它们。中介者充当中间人,使对象永远不必直接相互通信。这使得解决方案更加通用、可重用和可维护。

更多信息请参见:

http://addyosmani.com/largescalejavascript/#mediatorpattern,

Javascript模式

在Backbone方面...如果你使用过Marionette,你可能会遇到一个补充的迷你库(也是由Derick Bailey实现)叫做wreqr。你可以在Backbone应用程序中使用它来创建一个简单的中介器,低开销。

https://github.com/marionettejs/backbone.wreqr

它基本上允许您在对象之间使用backbone风格的事件。以下是示例:
首先,您需要创建一个全局可访问的中介者对象,或将其添加到应用程序命名空间或使用require.js:
var mediator = new Wreqr.EventAggregator();

内部视图#1

events : {
    'click .test a' : 'click'
},

click : function (e) {
     var clickedHtml = $(e.target).html();

     // trigger an 'element:click' event, which can be listened to from other
     // places in your application. pass var clickedHtml with the event
     // (passed to the arguments in your eventhandler function).
     mediator.trigger('element:click', clickedHtml); 
}

内部视图 #2

initialize: function(){
    //...
    this.listenTo(mediator, 'element:click', this.myEventHandler, this);
}

myEventHandler: function(elem){
    // elem = clickedHtml, passed through the event aggregator
    // do something with elem...
}

非常有帮助!谢谢。我只需要改成这个 new Backbone.Wreqr.EventAggregator() 就可以让它工作了。 - moreirapontocom

1
Backbone事件是解决这个问题的途径。 当您在视图中捕获事件时,我建议您使用以下方法进行冒泡处理:
click : function (e) {
    var clickedHtml = $(e.target).html();
    Backbone.Events.trigger("eventname",clickedHtml); 
}

然后,在您的路由器初始化函数中,您应该能够使用以下代码捕获它:
Backbone.Events.on("eventname", responseFunction); // listen out for this event 

然后在路由器中声明一个单独的函数:
responseFunction : function(clickedHtml) 
   {
       //Do whatever you want here
   }

我正在凭记忆编写此内容,希望能讲得清楚。我也没有测试过在路由器中捕获这样的事件,但是应该可以工作。
希望对你有所帮助。

0
在你所描述的情况下,我会在全局命名空间中创建一个临时存储对象,并使用它在视图之间传递数据。虽然这有点“hacky”,但比直接使用本地存储或窗口对象要好,至少通过在自己的全局命名空间中使用临时对象,可以知道对象的使用意图。
我发现使用http://backbonejs.org/#Events来实现类似于在两个视图之间传递数据的目的更好,但这取决于你如何构建页面,如果你在页面上有两个表示“控件”或“组件”的视图,这种方法非常有效。
如果你发布你的网站链接或其他东西,我可以看一看并给你更多帮助。
Russ

-1

你可以将它作为视图的属性存储:

click : function (e) {
     this.clickedHtml = $(e.target).html();
}

如果您的路由器可以访问两个视图,那么它可以将firstView.clickedHtml属性简单地传递给secondView中的函数(或者传递给初始化程序)。

谢谢,我已经添加了我的路由器,不确定如何从路由器文件中访问该属性。 - webmasters
您可以通过视图访问属性。为了做到这一点,您需要将视图公开可见。 - damienc88

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接