操作后对列表视图进行主干排序和更新

Backbone Sorting and Updating a listview after an action

本文关键字:排序 更新 列表 视图 操作      更新时间:2023-09-26

我正在尝试制作我的第一个主干应用程序,但遇到了一个我无法解决的问题。。

我有一个链接列表,每个链接旁边都有一个计数器,当我点击一个链接时,我希望计数器增加1。(我已经做了这个,它正在工作)

接下来,如果计数器值高于上面的链接,我希望我单击的链接在列表中向上移动。

像这样。

  • 第一个链接(4)
  • 第二个链接(3)
  • 第三链路(3)<-如果我点击这个链接,我希望它向上移动到第二个链接之上

我试过使用comparator和sortBy,但每次我尝试一些东西时,我似乎都无法重新渲染视图,也无法使链接向上移动一个位置。

当主视图初始化时,我最初确实设法对列表进行了排序。但是在我点击其中一个链接后更新视图和列表位置,我不知道如何完成。

我的代码:

(function() {
window.App = {
    Models: {},
    Collections: {},
    Views: {}
};
window.template = function(id) {
    return _.template( $('#' + id).html() );
};

//Modellen
App.Models.Task = Backbone.Model.extend({
    defaults: {
        name: 'Foo Bar Baz',
        uri: 'http://www.google.com',
        counter: 0
    },
    validate: function(attr) {
            if ( ! $.trim(attr.name) ) {
                    return 'En opgave kræver en title.';
            };
    }
});
//Collection
App.Collections.Tasks = Backbone.Collection.extend({ 
    model: App.Models.Task,
    comparator: function(task) {
    return task.get('counter');
    },
});

//Singel view
App.Views.TaskView = Backbone.View.extend({
    tagName: 'li',
    template: template('Tasks'),
    initialize: function() {
        this.model.on('change', this.render, this);
        this.model.on('destroy', this.remove, this);
    },
    events: {
    'click .edit' : 'retTask',
    'click .delete' : 'destroy',
    'click .uriLink' : 'addCounter'
    },
    retTask: function() {
        var newTaskNavn = prompt('Hvad skal det nye navn være', this.model.get('name'));
        if ( !newTaskNavn ) return;
        this.model.set('name', newTaskNavn);
    },      
    destroy: function() {
        this.model.destroy();
    },
    addCounter: function(e) {
        e.preventDefault();
        var newCounter = this.model.get('counter');
        this.model.set('counter', newCounter + 1);
    },
    remove: function() {
        this.$el.remove();
    },
    render: function()  {
        this.$el.html(this.template(this.model.toJSON()) );
        return this;
    }
});
//Collection View
App.Views.TasksView = Backbone.View.extend({
    tagName: 'ul',
    initialize: function() {
        this.collection.on('add', this.addOne, this);
        this.render();
    },
    render: function() {
        this.collection.each(this.addOne, this);
        return this;
    },
    addOne: function(task) {
        var taskView = new App.Views.TaskView({ model: task });
        this.$el.append(taskView.render().el);
    }
});
App.Views.AddTask = Backbone.View.extend({
    el: '#addTask',
    initialize: function() {
    },
    events: {
        'submit' : 'submit'
    },
    submit: function(e) {
        e.preventDefault();
        var taskNavn = $(e.currentTarget).find('.navnClass').val(),
             uriNum =  $(e.currentTarget).find('.uriClass').val();
        if ( ! $.trim(taskNavn)) {
            var test =  prompt('opgaven skal have et navn', '');
            if ( ! $.trim(test)) return false;
                taskNavn = test;
        }
        if( uriNum.indexOf( "http://" ) == -1 ) {
                     addedValue = 'http://',
                     uriNum = addedValue + uriNum;
        }
        $(e.currentTarget).find('input[type=text]').val('').focus();
        //var task = new App.Models.Task({ name: taskNavn, uri: uriNum });
        this.collection.add({ name: taskNavn, uri: uriNum });
    }
});

// new tasks collection
var tasks = new App.Collections.Tasks([
{
    name: 'Foo',
    uri: 'www.google.com',
    counter: 3
},
{   
    name: 'Bar',
    uri: 'http://google.com',
    counter: 2
},
{
    name: 'Baz',
    uri: 'http://www.google.com',
    counter: 1
}
]);
 // tasks.comparator = function(task) {
 // return task.get("counter");
 // };  
 tasks.sort();
// new collection view (add)
var addTaskView = new App.Views.AddTask({ collection: tasks});
// new collection view
var tasksView = new App.Views.TasksView({ collection: tasks });
$('.tasks').html(tasksView.el);
})();

我的HTML:(如果有人想复制场景:)

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>LinkList</title>
</head>
<body>
<h1>Mine opgaver</h1>
<form action="" id="addTask">
    <input class="navnClass" type="text" placeholder="Link name"><input clas s="uriClass" type="text" placeholder="www.url-here.com">
    <button class="nyOpgave">Ny opgave</button><br />
</form>
<div class="tasks">
    <script type="text/template" id="Tasks">
        <span class="linkNavn"><%= name %></span> - <a href="<%= uri %>" class="uriLink" target="_blank"><%= uri %></a> : [<span class="counterClass"><%= counter %></span>] <button class="edit">Edit</button> <button class="delete">Delete</button>
    </script>
</div>
<script src="js/underscore.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script src="js/jquery.js"></script>
<script src="js/backbone.js"></script>
<script src="main.js"></script>
</body>
</html>

有人能帮我算出这个吗?

/干杯Marcel

好的,我已经为您创建了应用程序,正如您希望它运行的那样。我将尝试向您解释整个代码,我写了什么以及为什么要写。

首先,看看JSfiddle:这里是

接下来,让我解释一下:

1.这是我的模型,它存储链接的名称、href、id(在我的示例中没有使用,但为每个模型分配一个唯一的id是一种很好的做法),最后存储链接(模型)的点击次数。

var myModel = Backbone.Model.extend({
    defaults:{
        'id' : 0,
        'name' : null,
        'link' : '#',
        'clicks' : 0
    }
});

2.这个集合存储了我所有的模型,我添加了一个比较器函数,这样当你向集合添加模型时,它就会对集合进行排序。

注意:我添加了一个-符号,以按点击量降序对集合进行排序(点击量最大的链接将首先出现)

var myCollection = Backbone.Collection.extend({
        model: myModel,
        comparator: function(item) {
            return -item.get('clicks');
        }
    });

3.现在这是我的主要观点,什么是主要观点?此视图对要显示的列表进行主要渲染。这里的代码非常不言自明。有一件事,this.coll.bind('add change',this.render,this),我添加了一个"更改",因为每当这个集合中的任何模型发生更改时,我们都想重新渲染整个列表,当我更改任何链接的计数时,单击它,我就想重新渲染完整列表。

var myView = Backbone.View.extend({
    el: $("#someElement"),
    tagName : 'ul',
    initialize: function() {
        this.coll = new myCollection();
        this.coll.bind('add change',this.render,this);
    },
    events: {
        "click #add": "add"
    },
    add: function(e){
        e.preventDefault();
        var mod = new myModel();
        var name = $('#name').val();
        var link = $('#link').val();
        mod.set({'id':mod.cid, 'name':name,'link':link});                    
        this.coll.add(mod);
    },
    render : function(){
        $('#list').empty();
        this.coll.sort();
        this.coll.forEach(function(model){
            var listItem = new printView({ model: model});
            $('#list').append(listItem.render().el);
        });
    }
});

4.这是我的子观点,为什么我会提出第二个观点,为什么一个观点不够?好吧,考虑一个场景,每个链接都有一个删除按钮(例如),当我点击删除按钮时(我只有一个视图),我如何识别要销毁的模型(从集合中删除?),一种可能的方法是将cid与每个模型相关联,然后点击我可以执行this.coll.getByCid(),但这不是一个很好的方法,IMHO,所以我为每个模型创建了一个单独的视图。此视图渲染每个模型,不再返回任何内容。

var printView = Backbone.View.extend({
    tagName: 'li',
    initialize : function(options) {
        _.bindAll(this, "render");                   
    },
    events:{
        "click a": "count"
    },
    render:function(){
        var linkName = this.model.get("name");
        var link= this.model.get("link");
        var clicks = this.model.get("clicks");
        this.$el.append("<a class='link' href='"+link+"'>"+linkName+"</a> ("+clicks+")");
        return this;
    },
    count:function(e){
        e.preventDefault();
        e.stopPropagation();
        var clicks = this.model.get("clicks");
        clicks++;
        this.model.set({'clicks':clicks});
    }
});

5.初始化我的(主)myView

new myView();

注意:我相信这个应用程序/代码可以用更好的方式编写,有一些改进,但以我的能力和它的工作原理(:p),我认为它可以帮助你。

只有在向集合中添加新模型时才会执行集合比较器:当属性发生变化时,它不会更新集合顺序。为了实现这一点,您需要调用collection.sort():

App.Collections.Tasks = Backbone.Collection.extend({ 
    model: App.Models.Task,
    initialize: function() {
        this.on('change:counter', this.sort);
    },
    comparator: function(task) {
        return task.get('counter');
    }
});

在列表视图中,您可以监听集合的sort事件,并重新呈现视图:

App.Views.TasksView = Backbone.View.extend({
    tagName: 'ul',
    initialize: function() {
        this.collection.on('add', this.addOne, this);
        this.collection.on('sort', this.render, this);
        this.render();
    },
    render: function() {
        //if there are existing child views, remove them
        if(this.taskViews) {
            _.each(this.taskViews, function(view) {
                view.remove();
            });
        }
        this.taskViews = []; 
        this.collection.each(this.addOne, this);
        return this;
    },
    addOne: function(task) {
        var taskView = new App.Views.TaskView({ model: task });
        this.$el.append(taskView.render().el);
        //keep track of child views
        this.taskViews.push(taskView);
    }
});