简体   繁体   English

Backbone - 如何重构此代码以防止出现鬼视图?

[英]Backbone - how to refactor this code to prevent ghost views?

I have a collection of flash cards that are tied to a Backbone collection.我有一系列与 Backbone 集合相关的闪存卡。 Once I get the collection, I create an instance of a player model.一旦我获得了集合,我就创建了一个玩家模型的实例。

Then the user can navigate through the rest of the flash cards using the "next" and "previous" buttons.然后用户可以使用“下一个”和“上一个”按钮浏览其余的闪存卡。 My first stab in doing this which I thought was simple was to pass the flashCards to a player like this.我第一次尝试这样做,我认为很简单,就是将抽认卡传递给这样的玩家。

Unfortunately, this design is causing the next and previous button events to be bound every time they are clicked.不幸的是,这种设计导致下一个和上一个按钮事件在每次点击时都被绑定。 So, after the first time clicking on the next button for example, the event starts firing more than once.因此,例如,在第一次单击下一个按钮后,事件开始多次触发。 I read somewhere about ghost views, but could not exactly figure out how I can break the code below into a chunk that will help me prevent the ghost view issue.我在某处阅读了有关鬼视图的内容,但无法完全弄清楚如何将下面的代码分解成一个块,以帮助我防止出现鬼视图问题。

var flashCards = new Quiz.Collections.FlashCards({
        id: this.model.get('card_set_id')
});

Quiz.player = new Quiz.Models.FlashCardPlayer({
        collection: flashCards
})

Quiz.Models.FlashCardPlayer = Backbone.Model.extend({
defaults: {
    'currentCardIndex': 0
},

initialize: function(){
    this.collection = this.get('collection');
    this.showCard();

},

showCard: function(){
    var flashCard = this.collection.at(this.get('currentCardIndex'));
    var cardView = new Quiz.Views.FlashCardPlayer({
        model: flashCard
    });
},

currentFlashCard: function(){
    return this.get('currentCardIndex');
},

previousFlashCard: function(){
    var currentFlashCardIndex = parseInt(this.get('currentCardIndex'), 10);
    if(currentFlashCardIndex <= 0){
        console.log("no less");
    }
    this.set({
        'currentCardIndex': currentFlashCardIndex--
    });
    this.showCard();
},

nextFlashCard: function(){
    var currentFlashCardIndex = parseInt(this.get('currentCardIndex'), 10);
    if(currentFlashCardIndex >= this.collection.length){
        console.log("no more");
    }
    currentFlashCardIndex = currentFlashCardIndex + 1;
    this.set({
        'currentCardIndex': currentFlashCardIndex
    });
    console.log(this.get('currentCardIndex'));
    this.showCard();
 }
}); 


Quiz.Views.FlashCardPlayer = Backbone.View.extend({
   el: $('#cardSet'),
   tagName: 'div',
   _template: _.template($('#playerTemplate').html()),

initialize: function(){
    console.log("in view flashcardplayer", this);
    this.render();
},

events: {
    'click #previous': 'getPreviousCard',
    'click #next': 'getNextCard'
},

render: function(){
    $(this.el).html(this._template(this.model.toJSON()));
    return this;
},

getPreviousCard: function(){
    this.close();
    Quiz.player.previousFlashCard();
},

getNextCard: function(){
    this.close();
    Quiz.player.nextFlashCard();
}
});


script#playerTemplate(type="text/template")
<div id="state"></div>
<div id="previous">Previous</div>
<div id="card">
   <h2><%= question %></h2>
    <h3><%= answer %></h3> 
</div>
<div id="next">Next</div>

You're creating a new instance of Quiz.Views.FlashCardPlayer each time you show a new card.每次显示新卡片时,您都在创建Quiz.Views.FlashCardPlayer的新实例。 Each of these instances does its own event handling, so each instance is binding to the same #next and #previous elements.这些实例中的每一个都执行自己的事件处理,因此每个实例都绑定到相同的#next#previous元素。

I think there are a couple of conceptual issues here:我认为这里有几个概念性问题:

  • You only need one FlashCardPlayer view, which should bind events on the next/previous elements.你只需要一个FlashCardPlayer视图,它应该绑定下一个/上一个元素上的事件。 You probably ought to have a separate FlashCard view, which displays a single card, and the player can swap those views in and out as the next/previous buttons are pressed.您可能应该有一个单独的FlashCard视图,它显示一张卡片,玩家可以在按下下一个/上一个按钮时交换这些视图。 As a general rule, if you have an element with an id , you should only be rendering and binding to it once, with a single view instance, otherwise you end up with the same issue you have now.作为一般规则,如果你有一个带有id的元素,你应该只渲染和绑定一次,使用单个视图实例,否则你最终会遇到与现在相同的问题。

  • You're trying to stuff way too much into the FlashCardPlayer model.您试图在FlashCardPlayer模型中塞入太多内容。 As a rule, models should only know about their data, not about the views used to display them (in part because one model might need to be displayed in a variety of views).通常,模型应该只知道它们的数据,而不是用于显示它们的视图(部分原因是一个模型可能需要在各种视图中显示)。 I don't mind having the nextFlashCard() and previousFlashCard() methods on the model, as this is still in the realm of storing data about the collection, but the showCard() method is really moving squarely into view territory, as it deals with presentation logic.我不介意在模型上使用nextFlashCard()previousFlashCard()方法,因为这仍然在存储有关集合的数据的领域,但showCard()方法确实正在直接进入视图领域,因为它处理与呈现逻辑。 A much better idea would be to have your view bind to the change:currentCardIndex event on the model and handle the display of the new card, using this.model.get('currentCardIndex')) (or a new getCurrentCard() method) to get it.一个更好的主意是让您的视图绑定到模型上的change:currentCardIndex事件并处理新卡的显示,使用this.model.get('currentCardIndex')) (或新的getCurrentCard()方法)为拿到它,为实现它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM