Saving jQuery UI Sortable's order to Backbone.js Collection

前端 未结 3 1611
醉梦人生
醉梦人生 2020-12-07 07:46

I have a Backbone.js collection that I would like to be able to sort using jQuery UI\'s Sortable. Nothing fancy, I just have a list that I would like to be able to sort.

相关标签:
3条回答
  • 2020-12-07 08:24

    Just use Backbone.CollectionView!

    var collectionView = new Backbone.CollectionView( {
      sortable : true,
      collection : new Backbone.Collection
    } );
    

    Voila!

    0 讨论(0)
  • 2020-12-07 08:26

    I've done this by using jQuery UI Sortable to trigger an event on the item view when an item is dropped. I can then trigger another event on the item view that includes the model as data which the collection view is bound to. The collection view can then be responsible for updating the sort order.

    Working example

    http://jsfiddle.net/7X4PX/260/

    jQuery UI Sortable

    $(document).ready(function() {
        $('#collection-view').sortable({
            // consider using update instead of stop
            stop: function(event, ui) {
                ui.item.trigger('drop', ui.item.index());
            }
        });
    });
    

    The stop event is bound to a function that triggers drop on the DOM node for the item with the item's index (provided by jQuery UI) as data.

    Item view

    Application.View.Item = Backbone.View.extend({
        tagName: 'li',
        className: 'item-view',
        events: {
            'drop' : 'drop'
        },
        drop: function(event, index) {
            this.$el.trigger('update-sort', [this.model, index]);
        },        
        render: function() {
            $(this.el).html(this.model.get('name') + ' (' + this.model.get('id') + ')');
            return this;
        }
    });
    

    The drop event is bound to the drop function which triggers an update-sort event on the item view's DOM node with the data [this.model, index]. That means we are passing the current model and it's index (from jQuery UI sortable) to whomever is bound to the update-sort event.

    Items (collection) view

    Application.View.Items = Backbone.View.extend({
        events: {
            'update-sort': 'updateSort'
        },
        render: function() {
            this.$el.children().remove();
            this.collection.each(this.appendModelView, this);
            return this;
        },    
        appendModelView: function(model) {
            var el = new Application.View.Item({model: model}).render().el;
            this.$el.append(el);
        },
        updateSort: function(event, model, position) {            
            this.collection.remove(model);
    
            this.collection.each(function (model, index) {
                var ordinal = index;
                if (index >= position) {
                    ordinal += 1;
                }
                model.set('ordinal', ordinal);
            });            
    
            model.set('ordinal', position);
            this.collection.add(model, {at: position});
    
            // to update ordinals on server:
            var ids = this.collection.pluck('id');
            $('#post-data').html('post ids to server: ' + ids.join(', '));
    
            this.render();
        }
    }); 
    

    The Items view is bound to the update-sort event and the function uses the data passed by the event (model and index). The model is removed from the collection, the ordinal attribute is updated on each remaining item and the order of items by id is sent to the server to store state.

    Collection

    Application.Collection.Items = Backbone.Collection.extend({
        model: Application.Model.Item,
        comparator: function(model) {
            return model.get('ordinal');
        },
    });
    

    The collection has a comparator function defined which orders the collection by ordinal. This keeps the rendered order of items in sync as the "default order" of the collection is now by the value of the ordinal attribute.

    Note there is some duplication of effort: the model doesn't need to be removed and added back to the collection if a collection has a comparator function as the jsfiddle does. Also the view may not need to re-render itself.

    Note: compared to the other answer, my feeling was that it was more correct to notify the model instance of the item that it needed to be updated instead of the collection directly. Both approaches are valid. The other answer here goes directly to the collection instead of taking the model-first approach. Pick whichever makes more sense to you.

    0 讨论(0)
  • 2020-12-07 08:42

    http://jsfiddle.net/aJjW6/2/

    HTML:

    `<div class="test-class">
         <h1>Backbone and jQuery sortable - test</h1>
         <div id="items-collection-warper"></div>
    </div>`
    

    JavaScript:

    $(document).ready(function(){
    
    var collection = [
        {name: "Item ", order: 0},
        {name: "Item 1", order: 1},
        {name: "Item 2", order: 2},
        {name: "Item 3", order: 3},
        {name: "Item 4", order: 4}
    ];
    var app = {};
    
    app.Item = Backbone.Model.extend({});
    app.Items = Backbone.Collection.extend({
        model: app.Item,
        comparator: 'order',
    
    });
    
    app.ItemView = Backbone.View.extend({
        tagName: 'li',
        template: _.template('<span><%= name %> - <b><%= order %></b></span>'),
        initialize: function(){
    
        },
        render: function(){
            var oneItem = this.$el.html(this.template(this.model.attributes));
            return this;
        }
    });
    
    app.AppView = Backbone.View.extend({
        el: "#items-collection-warper",
        tagName: 'ul',
        viewItems: [],
        events:{
            'listupdate': 'listUpdate'
        },
        initialize: function(){
            var that = this;
    
            this.$el.sortable({
                 placeholder: "sortable-placeholder",
                update: function(ev, ui){
                   that.listUpdate();
                }
            });            
        },
        render: function(){
            var that= this;
            this.collection.each(function(item){
                that.viewItems.push(that.addOneItem(item));
                return this;
            });
    
        },
        addOneItem: function(item){
            var itemView = new app.ItemView({model: item});
            this.$el.append(itemView.render().el);
    
            return itemView;
        },
    
        listUpdate: function(){
    
    
            _.each(this.viewItems, function(item){
                item.model.set('order', item.$el.index());
            });
            this.collection.sort({silent: true})
             _.invoke(this.viewItems, 'remove');
            this.render();
        }
    });
    
    var Items = new app.Items(collection)
    var appView = new app.AppView({collection: Items});
    appView.render();
    });
    

    CSS:

    .test-class{
        font-family: Arial;
    }
    .test-class li{
        list-style:none;
        height:20px;
    
    }
    .test-class h1{
        font-size: 12px;
    }
    .ui-sortable-helper{
        opacity:0.4;
    }
    .sortable-placeholder{
        background: #ffffd;
        border:1px dotted #ccc;
    }
    
    0 讨论(0)
提交回复
热议问题