Backbone.js 将模型添加到集合问题

Posted

技术标签:

【中文标题】Backbone.js 将模型添加到集合问题【英文标题】:Backbone.js Adding Model to Collection Issue 【发布时间】:2012-01-19 20:01:09 【问题描述】:

我正在 Backbone.js 中构建一个测试应用程序(我的第一个使用 Backbone 的应用程序)。该应用程序是这样的:

    从服务器“计划”加载数据 建立计划列表并显示在屏幕上 有一个添加新计划的按钮 添加新计划后,添加到集合中(暂时不要保存到服务器) 重定向到索引页面并显示新集合(包括您刚刚添加的计划)

我的问题是第 5 项。当我保存计划时,我将模型添加到集合中,然后重定向到初始视图。此时,我从服务器获取数据。当我从服务器获取数据时,这会覆盖我的集合并且我添加的模型消失了。

如何防止这种情况发生?我找到了一种方法来做到这一点,但这绝对不是正确的方法。您将在下面找到我的代码示例。谢谢您的帮助。

PlansListView 视图:

var PlansListView = Backbone.View.extend(

    tagName : 'ul',

    initialize : function()
    
        _.bindAll( this, 'render', 'close' );
        //reset the view if the collection is reset 
        this.collection.bind( 'reset', this.render , this );

    ,
    render : function()
    
        _.each( this.collection.models, function( plan )

            $( this.el ).append( new PlansListItemView( model: plan ).render().el );

        , this );

        return this;
    ,
    close : function()
    
        $( this.el ).unbind();
        $( this.el ).remove();  

       
);//end

NewPlanView保存方法

var NewPlanView = Backbone.View.extend(

    tagName : 'section',
    template : _.template( $( '#plan-form-template' ).html() ),
    events : 
        'click button.save' : 'savePlan',
        'click button.cancel' : 'cancel'

    ,
    intialize: function()
    
        _.bindAll( this, 'render', 'save', 'cancel' );  

    ,
    render : function()
    
        $( '#container' ).append( $( this.el ).html(this.template( this.model.toJSON() )) );                                    
        return this;
    ,
    savePlan : function( event )
    

        this.model.set(

            name : 'bad plan',
            date : 'friday',
            desc : 'blah',
            id : Math.floor(Math.random()*11),
            total_stops : '2'           
        );

        this.collection.add( this.model );

        app.navigate('', true );
        event.preventDefault();

    ,
    cancel : function()
);

路由器(默认方式):

    index : function()
    
        this.container.empty();
        var self = this;

        //This is a hack to get this to work
        //on default page load fetch all plans from the server
        //if the page has loaded ( this.plans is defined) set the updated plans collection to the view
        //There has to be a better way!!
        if( ! this.plans )
        
            this.plans = new Plans();


            this.plans.fetch(

                success: function()
                
                    self.plansListView = new PlansListView( collection : self.plans );
                    $( '#container' ).append( self.plansListView.render().el );
                    if( self.requestedID ) self.planDetails( self.requestedID );
                   
            );
        
        else
        
            this.plansListView = new PlansListView( collection : this.plans );
            $( '#container' ).append( self.plansListView.render().el );
            if( this.requestedID ) self.planDetails( this.requestedID );

        
    ,

新计划路线:

    newPlan : function()
       var plan = new Plan(name: 'Cool Plan', date: 'Monday', desc: 'This is a great app');
        this.newPlan = new NewPlanView( model : plan, collection: this.plans );
        this.newPlan.render();
       

完整代码 (函数($)

var Plan = Backbone.Model.extend(

    defaults: 
        name : '',
        date : '',
        desc : ''   
       
);

var Plans = Backbone.Collection.extend(

    model : Plan,
    url : '/data/'  

);     






$( document ).ready(function( e )

    var PlansListView = Backbone.View.extend(

        tagName : 'ul',

        initialize : function()
        
            _.bindAll( this, 'render', 'close' );
            //reset the view if the collection is reset 
            this.collection.bind( 'reset', this.render , this );

        ,
        render : function()
        
            _.each( this.collection.models, function( plan )

                $( this.el ).append( new PlansListItemView( model: plan ).render().el );

            , this );

            return this;
        ,
        close : function()
        
            $( this.el ).unbind();
            $( this.el ).remove();  

           
    );//end

    var PlansListItemView = Backbone.View.extend(

        tagName : 'li',
        template : _.template( $( '#list-item-template' ).html() ),
        events :

            'click a' : 'listInfo'  
        ,
        render : function()
        
            $( this.el ).html( this.template( this.model.toJSON() ) );
            return this;            
        ,
        listInfo : function( event )
        


        

    );//end


    var PlanView = Backbone.View.extend(

        tagName : 'section',
        events : 
            'click button.add-plan' : 'newPlan'
        ,
        template: _.template( $( '#plan-template' ).html() ),
        initialize: function()
        
            _.bindAll( this, 'render', 'close', 'newPlan' );            
        ,
        render : function()
        
            $( '#container' ).append( $( this.el ).html( this.template( this.model.toJSON() ) ) );
            return this;
        ,
        newPlan : function( event )
        
            app.navigate( 'newplan', true );
        ,
        close : function()
        
            $( this.el ).unbind();
            $( this.el ).remove();  
           

    );//end


    var NewPlanView = Backbone.View.extend(

        tagName : 'section',
        template : _.template( $( '#plan-form-template' ).html() ),
        events : 
            'click button.save' : 'savePlan',
            'click button.cancel' : 'cancel'

        ,
        intialize: function()
        
            _.bindAll( this, 'render', 'save', 'cancel' );  

        ,
        render : function()
        
            $( '#container' ).append( $( this.el ).html(this.template( this.model.toJSON() )) );                                    
            return this;
        ,
        savePlan : function( event )
        

            this.model.set(

                name : 'bad plan',
                date : 'friday',
                desc : 'blah',
                id : Math.floor(Math.random()*11),
                total_stops : '2'           
            );

            this.collection.add( this.model );

            app.navigate('', true );
            event.preventDefault();

        ,
        cancel : function()
    );



    var AppRouter = Backbone.Router.extend(

        container : $( '#container' ),

        routes : 

            ''              : 'index',
            'viewplan/:id'  : 'planDetails',
            'newplan'           : 'newPlan'
        ,
        initialize: function()

        ,
        index : function()
        
            this.container.empty();
            var self = this;

            //This is a hack to get this to work
            //on default page load fetch all plans from the server
            //if the page has loaded ( this.plans is defined) set the updated plans collection to the view
            //There has to be a better way!!
            if( ! this.plans )
            
                this.plans = new Plans();


                this.plans.fetch(

                    success: function()
                    
                        self.plansListView = new PlansListView( collection : self.plans );
                        $( '#container' ).append( self.plansListView.render().el );
                        if( self.requestedID ) self.planDetails( self.requestedID );
                       
                );
            
            else
            
                this.plansListView = new PlansListView( collection : this.plans );
                $( '#container' ).append( self.plansListView.render().el );
                if( this.requestedID ) self.planDetails( this.requestedID );

            
        ,

        planDetails : function( id )
        

            if( this.plans )
            
                this.plansListView.close();
                this.plan = this.plans.get( id );

                if( this.planView ) this.planView.close();
                    this.planView = new PlanView( model : this.plan );
                this.planView.render();
            
            else

                this.requestedID = id;
                this.index();
            

            if( ! this.plans ) this.index();
        ,

        newPlan : function()
           var plan = new Plan(name: 'Cool Plan', date: 'Monday', desc: 'This is a great app');
            this.newPlan = new NewPlanView( model : plan, collection: this.plans );
            this.newPlan.render();
           
    );



    var app = new AppRouter();
    Backbone.history.start();

); 








)( jQuery );

【问题讨论】:

【参考方案1】:

我认为您正在寻找“remove”参数,它将防止模型在提取期间从集合中删除。

this.plans.fetch(remove: false)

有关 fetch 方法的更多详细信息,请参阅backbone documentation。

【讨论】:

【参考方案2】:

您是否每次点击索引页面时都获取数据,因为您希望列表与数据库同步(即其他人添加了一个项目并且您希望它反映)?

如果不是不是,我觉得你正在做的事情很好;仅当数据不存在时才获取。

但是,如果您确实希望列表保持同步;但是您仍然希望在其中呈现新添加的项目;您可以从获取的列表中合并或使用另一个列表来保存新添加的数据。

如果你使用另一个列表,你只需要渲染两个列表;未保存的列表可能应该存在于持久列表的顶部或底部,并组合在一起。

【讨论】:

不,我不需要列表与每个索引视图上的服务器同步。但是,您建议如何进行您所说的合并? 您可以通过模型的 id .get(id) 获取模型,并使用新获取的列表对其进行更新;或者使用集合的 .parse() 方法将所有未保存的模型添加到新列表中。 也许这可能会有所帮助:(来自 Collection 的 fetch 方法的主干文档)“如果您想将传入的模型添加到当前集合中,而不是替换集合的内容,请通过 add : true 作为获取的选项。”

以上是关于Backbone.js 将模型添加到集合问题的主要内容,如果未能解决你的问题,请参考以下文章

将集合绑定到 Backbone.js 中的视图

Backbone.js 模型与集合

Backbone.js:在现实世界的应用程序中呈现集合

模型和集合到 Backbone 中的一个视图

Backbone.js:组合多个模型的复杂视图

Backbone.js 模型和集合的最佳实践