zoukankan      html  css  js  c++  java
  • Backbone Collection——数据模型集合

    如果将一个Model对象比喻成数据库中的一条记录,那么Collection就是一张数据表。它表示为一个模型集合类,用于存储和管理一系列相同类型的模型对象。

    1、创建集合
    集合用于组织和管理多个模型,但它并不是必须的,如果你的某个模型对象是唯一的(单例),那么你没必要将它放到集合中。

    我们来看一个创建集合的例子:

    // 定义模型类
    var Book = Backbone.Model.extend({
            defaults: {
                    name: ''
            }
    });
    
    // 定义集合类
    var BookList = Backbone.Collection.extend({
            model: Book
    });
    
    // 创建一系列模型对象
    var book1 = new Book({
            name: 'Effective Java中文版(第2版)'
    });
    var book2 = new Book({
            name: 'JAVA核心技术卷II:高级特性(原书第8版)'
    });
    var book3 = new Book({
            name: '精通Hibernate:Java对象持久化技术详解(第2版)'
    });

    在这个例子中,我们定义了模型类Book和集合类BookList,然后创建了3个模型对象,并将它们放到一个集合对象中。(你可以在控制台输出books.models属性,用来查看集合中包含的模型对象列表)

    我们为了创建3个Book模型对象,对Book类显式实例化了3次,其实Model本身已经提供了更简单的方法来复制一个模型,例如:

    var book1 = new Book({
            name: 'Effective Java中文版(第2版)'
    });
    
    var book2 = book1.clone();
    book2.set('name', 'JAVA核心技术卷II:高级特性(原书第8版)');
    
    var book3 = book1.clone();
    book3.set('name', '精通Hibernate:Java对象持久化技术详解(第2版)');

    在这段代码中,我们使用模型的clone()方法来复制一个和当前对象相同(包括数据)的新对象,这可以简化我们创建模型的流程。

    在实例化集合对象时,除了可以向构造函数中添加已经创建好的模型列表(就像上面的例子那样),我们还可以直接传递模型数据,集合会自动将这些数据转换为模型对象,例如:

    // 定义模型类
    var Book = Backbone.Model.extend({
            defaults: {
                    name: ''
            }
    });
    
    // 定义集合类
    var BookList = Backbone.Collection.extend({
            model: Book
    });
    
    var models = [{
            name: 'Effective Java中文版(第2版)'
    },
    {
            name: 'JAVA核心技术卷II:高级特性(原书第8版)'
    },
    {
            name: '精通Hibernate:Java对象持久化技术详解(第2版)'
    }];
    
    // 创建集合对象
    var books = new BookList(models);

    运行这个例子,并在控制台输出books.models属性,你可以看到集合中存储的是Book模型类的实例,而并非我们在models数组中声明的原始数据。

    这是因为我们在声明BookList集合类的时候,就已经设置了model属性,该属性指向集合中存储的模型对象的构造函数,当我们传递原始数据时,集合会自动创建model中定义的模型类,并将原始数据传递给它。
    (如果你在定义集合类的时候没有设置model,那么集合会默认将原始数据转换为Backbone.Model类的实例)

    我们之所以要使用extend来继承Backbone.Collection类,是因为我们希望定义一个自己的集合类,并向其中扩展更多的自定义方法。如果你的集合类仅仅是用于简单地存储和管理模型对象,且Backbone.Collection类所提供的方法已经可以满足你的要求,那么你可以直接实例化一个Backbone.Collection,同时也可以像上面一样实现原始数据和模型对象的自动转换,例如:

    // 定义模型类
    var Book = Backbone.Model.extend({
            defaults: {
                    name: ''
            }
    });
    
    var models = [{
            name: 'Effective Java中文版(第2版)'
    },
    {
            name: 'JAVA核心技术卷II:高级特性(原书第8版)'
    },
    {
            name: '精通Hibernate:Java对象持久化技术详解(第2版)'
    }];
    
    // 创建集合对象
    var books = new Backbone.Collection(models, {
            model: Book
    });

    在本例中,我们没有通过extend定义自己的集合类,而是直接实例化Collection类。我们依然传入了原始数据,但同时我们在构造函数的第2个参数(配置对象)中设置了model属性,Collection通过它就能知道要将原始数据转换为哪个模型类的实例。

    2、向集合中添加模型
    集合提供了3个方法允许我们动态地向集合中动态插入模型:

    add():向集合中的指定位置插入模型,如果没有指定位置,默认追加到集合尾部
    push():将模型追加到集合尾部(与add方法的实现相同)
    unshift():将模型插入到集合头部

    这些方法很容易理解,但我们还是通过一个例子来说明:

    // 定义模型类
    var Book = Backbone.Model.extend({
            defaults: {
                    name: '',
                    price: 0
            }
    });
    
    // 创建集合对象
    var books = new Backbone.Collection(null, {
            model: Book
    });
    
    books.add({
            name: '构建高性能Web站点',
            price: 56.30
    });
    
    books.push({
            name: '深入分析Java Web技术内幕',
            price: 51.80
    });
    
    books.unshift({
            name: '编写高质量代码:Web前端开发修炼之道',
            price: 36.80
    });
    
    books.push({
            name: '基于MVC的JavaScript Web富应用开发',
            price: 42.50
    },
    {
            at: 1
    });
    
    books.unshift({
            name: 'RESTful Web Services Cookbook中文版',
            price: 44.30
    
    },
    {
            at: 2
    });
    
    // 在控制台输出集合中的模型列表
    console.dir(books.models);

    在例子中,我们通过3个方法向集合中添加了多个模型,最后,我们在控制台输出了集合中的模型列表。请仔细观察和分析我们调用的方法,以及最终列表中模型的排列顺序:
    a、这些方法的作用和上面介绍的一样,用于将模型添加到集合中不同的位置,但当我们设置了at配置之后,它们就变得完全一样了,因为它们会忽略自身的规则,将模型插入到at所指向的位置。
    b、当数据被成功添加到集合中时,集合会触发add事件,执行所有监听add事件的方法。除非我们在调用add()方法时设置了silent配置项,则会忽略事件的触发。

    3、操作集合中的模型
    在Underscore中,提供了许多对对象和数组集合进行操作的方法,这些方法已经被Backbone添加到Collection类的原型中。这意味着你可以使用Underscore中的集合方法来操作Collection集合中的数据,例如each()、map()、find()等方法。

    但有一些方法我们还是要单独介绍它们,因为Collection对这些方法进行了重写,它们和Underscore中的同名方法不完全相同。(这也是为什么我要在上面单独介绍模型的添加方法)

    删除模型:
    集合类提供了3个方法用于从集合中移除模型对象,分别是:
    remove():从集合中移除一个或多个指定的模型对象
    pop():移除集合尾部的一个模型对象
    shift():移除集合头部的一个模型对象

    这些方法与添加的方法是对应的,而且当模型被移除成功后,会触发集合对象的remove事件,除非你在移除时使用了silent配置。
    这些方法很容易理解,但还是让我们通过一个简单的例子来说明:

    // 定义模型类
    var Book = Backbone.Model.extend({
            defaults: {
                    name: '',
                    price: 0
            }
    });
    
    // 定义初始化数据
    var data = [{
            name: '构建高性能Web站点',
            price: 56.30
    },
    {
            name: '深入分析Java Web技术内幕',
            price: 51.80
    },
    {
            name: '编写高质量代码:Web前端开发修炼之道',
            price: 36.80
    },
    {
            name: '基于MVC的JavaScript Web富应用开发',
            price: 42.50
    },
    {
            name: 'RESTful Web Services Cookbook中文版',
            price: 44.30
    
    }]
    
    // 创建集合对象
    var books = new Backbone.Collection(data, {
            model: Book
    });
    
    books.remove(books.models[2]);
    books.pop();
    books.shift();
    
    // 在控制台输出集合中的模型列表
    console.dir(books.models);

    在本例中,我们分别调用了remove()方法移除了集合中第2个模型,调用pop()方法移除了最后一个模型,调用shift()方法移除了第一个模型。最后我们在控制台输出集合中剩下的模型列表,请查看控制台输出结果,它和你想象的结果是一致的。

    在集合中查找模型:
    Collection定义了一系列用于快速从集合中查找我们想要的模型的方法,包括:

    get():根据模型的唯一标识(id)查找模型对象
    getByCid():根据模型的cid查找模型对象
    at():查找集合中指定位置的模型对象
    where():根据数据对集合的模型进行筛选

    前面介绍数据模型时我们提到,每个模型对象都有一个唯一标识(id),它与数据库中记录的id保持同步。实际上,每个模型对象内部还会自动创建一个cid,它用来标识每一个模型(请注意将id和cid区分开,它们没有任何关系)。

    集合对象提供了两个方法用于根据id和cid来查找模型对象,分别是get()方法和getByCid()方法,例如:

    // 定义模型类
    var Book = Backbone.Model.extend({
            defaults: {
                    name: '',
                    price: 0
            }
    });
    
    // 定义初始化数据
    var data = [{
            id: 1001,
            name: '构建高性能Web站点',
            price: 56.30
    },
    {
            id: 1002,
            name: '深入分析Java Web技术内幕',
            price: 51.80
    },
    {
            id: 1003,
            name: '编写高质量代码:Web前端开发修炼之道',
            price: 36.80
    },
    {
            id: 1004,
            name: '基于MVC的JavaScript Web富应用开发',
            price: 42.50
    },
    {
            id: 1005,
            name: 'RESTful Web Services Cookbook中文版',
            price: 44.30
    }]
    
    // 创建集合对象
    var books = new Backbone.Collection(data, {
            model: Book
    });
    
    // 根据id和cid查找模型对象
    var book1 = books.get(1001);
    var book2 = books.getByCid('c2');
    
    // 在控制台输出模型
    console.dir(book1);
    console.dir(book2);

    本例中,我们从集合中根据模型的id和cid查找出2个模型对象,但实际开发中我们不会直接在代码中写出模型的id和cid。

    id应该是从服务器接口进行同步获取到的。
    cid应该是在之前已经记录下某个模型的cid,再根据它从集合中查找的。

    at()方法根据我们给定的索引,从集合中查找对应位置的模型,我们在上面的例子中追加以下代码:
    // 根据索引查找模型对象
    var book3 = books.at(1);

    // 在控制台输出模型
    console.dir(book3);

    最后,我们还可以通过where()方法,实现相对复杂的查找规则,例如:
    // 根据price从集合中查找模型
    var book4 = books.where({
    price: 51.80
    });

    // 在控制台输出模型
    console.dir(book4);
    请查看控制台输出的结果:where()方法用于给定一个或多个数据,查找并返回集合中匹配数据的模型。该方法返回一个数组,因此能够包含一个或多个结果。

    当我们调用get()、getByCid()和at()方法没有找到到匹配对象时,会返回undefined,而where()方法在没有找到匹配对象时会返回一个空数组。你可以使用Underscore中的isEmpty()方法检查返回值是否为空,因为它能检查到空数组和空对象。

    4、自动排序
    我们常常使用数组的sort()方法对元素进行排序,Underscore也提供了sortBy()方法实现更为复杂的集合排序。但在Backbone的集合对象中,为我们提供了集合元素的实时排序,当任何模型对象被插入到集合中时,都会按照预定的排序规则放到对应的位置。

    // 定义模型类
    var Book = Backbone.Model.extend({
            defaults: {
                    name: '',
                    price: 0
            }
    });
    
    // 创建集合对象
    var books = new Backbone.Collection(null, {
            model: Book,
            comparator: function(m1, m2) {
                    var price1 = m1.get('price');
                    var price2 = m2.get('price');
    
                    if (price1 > price2) {
                            return 1;
                    } else {
                            return 0;
                    }
            }
    });
    
    books.add({
            name: '构建高性能Web站点',
            price: 56.30
    });
    
    books.push({
            name: '深入分析Java Web技术内幕',
            price: 51.80
    });
    
    books.unshift({
            name: '编写高质量代码:Web前端开发修炼之道',
            price: 36.80
    });
    
    books.push({
            name: '基于MVC的JavaScript Web富应用开发',
            price: 42.50
    },
    {
            at: 1
    });
    
    books.unshift({
            name: 'RESTful Web Services Cookbook中文版',
            price: 44.30
    
    },
    {
            at: 2
    });
    
    // 在控制台输出集合中的模型列表
    console.dir(books.models);

    这个例子和我们前面介绍添加方法时的例子相同,但集合中存储的模型顺序却不一样,因为我们在创建集合对象时设置了comparator方法。我们不需要手动调用该方法,因为它会在新模型被添加到集合中时自动被调用,并按照方法中定义的规则对集合中的数据进行重新排序。

    comparator方法接收两个参数,表示临近的两个模型对象,你需要通过返回值表示它们的排序规则,这和JavaScript中原生的sort()方法是一样的。

    当我们设置了comparator方法后,所有关于元素位置的方法和参数都会失效,例如push()、unshift()方法和at参数等。

    需要注意的是:comparator方法在很多时候都是非常有用的(例如显示动态数据列表时),因为它能保证我们获取到的数据始终都是按规则排列的,但在集合中的数据量太多时,它可能会耗费很多的资源和事件来实时确保数据的排序规则。这时,你可以手动调用集合对象的sort()方法在需要的进行手动排序。

    8.5、从服务器获取集合数据

    Collection也提供了两个与服务器进行交互的方法:
    fetch():用于从服务器接口获取集合的初始化数据,覆盖或追加到集合列表中
    create():在集合中创建一个新的模型,并将其同步到服务器

    我们先来看一个fetch()方法例子:

    // 定义模型类
    var Book = Backbone.Model.extend({
            defaults: {
                    name: '',
                    price: 0
            }
    });
    
    // 定义集合类
    var BookList = Backbone.Collection.extend({
            model: Book,
            url: '/service'
    });
    
    // 创建集合对象, 并从服务器同步初始化数据
    var books = new BookList();
    books.fetch({
            success: function(collection, resp) {
                    // 同步成功后在控制台输出集合中的模型列表
                    console.dir(collection.models);
            }
    });

    我们前面讨论过模型的数据同步,需要与服务器同步数据,需要设置一个url或urlPath指定服务器接口地址。同步集合数据时也不例外,本例中我们设置服务器接口地址为/service,接口返回数据为:

    [{
            "id": "1001",
            "name": "构建高性能Web站点",
            "price": "56.30"
    },
    {
            "id": "1002",
            "name": "深入分析Java Web技术内幕",
            "price": "51.80"
    },
    {
            "id": "1003",
            "name": "编写高质量代码:Web前端开发修炼之道",
            "price": "36.80"
    },
    {
            "id": "1004",
            "name": "基于MVC的JavaScript Web富应用开发",
            "price": "42.50"
    },
    {
            "id": "1005",
            "name": "RESTful Web Services Cookbook中文版",
            "price": "44.30"
    }]

    我们在实例化集合对象之后,调用fetch()与服务器接口进行数据同步,并在同步成功后将集合中的数据列表输出在控制台。请运行例子中的代码,你能看到控制台中输出的结果,它包含5个模型对象,正是我们服务器接口返回的这些数据。

    在调用fetch()方法同步集合数据时,默认会以覆盖的方式进行,这意味着集合在同步之前的数据将丢失。我们可以在调用fetch()方法时传递add参数来通知集合进行添加,而不是覆盖,例如:

    var books = new BookList();
    books.add({
            id: 1000,
            name: 'Thinking in Java',
            price: 395.70
    });
    
    books.fetch({
            add: true,
            success: function(collection, resp) {
                    // 同步成功后在控制台输出集合中的模型列表
                    console.dir(collection.models);
            }
    });

    我们修改了例子中的代码,在实例化集合对象后,我们向集合中添加了一条数据,然后从服务器同步了5条数据(请注意在调用fetch()方法时我们设置了add参数为true)。在控制台输出的结果中,之前的数据并没有被覆盖掉。

    数据在成功同步到集合中之后,会触发reset事件,我们可以通过监听该事件从而进行下一步操作(比如将集合中的数据显示到页面中)。

    集合的数据同步与模型的数据同步有许多相似之处(例如你可以重载parse()方法来对服务器返回的数据进行解析,使其能顺利被添加到集合中),这里就不再重复讨论。

    集合提供的另一个create()方法,是根据集合的model所指向的模型类,创建一个模型对象,并把该对象添加到集合中,最后将数据同步到服务器接口。

    我们通过例子来说明create()方法的使用:

    var books = new BookList();
    // 创建一个模型
    books.create({
            name: 'Thinking in Java',
            price: 395.70
    },
    {
            success: function(model, resp) {
                    // 添加成功后, 在控制台输出集合中的模型列表
                    console.dir(books.models);
            }
    });

    请将这段代码替换到前面的例子中,并查看运行效果。

    (通过抓包我们能看到Request Method为POST,如果创建的模型中包含id,则Request   Method为PUT,这与我们之前讲模型的save()方法是相同的。)

    集合对象默认会先将模型添加到集合中,再提交到服务器接口,无论接口返回是否成功,新建的模型对象都会被添加到集合中。我们可以通过传递wait配置,来控制只有在服务器返回成功之后(响应状态码为200),才将模型对象添加到集合中。

    在Backbone内部,create()方法是通过add()方法将新创建的模型添加到集合中的,因此我们一般通过监听add事件,来对新模型进行下一步操作。

    6、将数据批量同步到服务器
    上一节我们讨论过,Backbone中集合提供了数据同步和创建的方法与服务器进行交互,但实际上这可能并不能满足我们的需求。例如:当我们需要对数据进行批量地添加、修改和删除操作时,就需要在Collection的基础上扩展自己的方法。

    在下面的例子中,我扩展了对集合中的模型数据批量同步的方法:

    // 定义模型类
    var Book = Backbone.Model.extend({
            defaults: {
                    name: '',
                    price: 0
            }
    });
    
    // 定义BookList类
    var BookList = Backbone.Collection.extend({
            model: Book,
            url: '/service',
            // 将集合中所有的模型id连接为一个字符串并返回
            getIds: function() {
                    return _(this.models).map(function(model) {
                            return model.id;
                    }).join(',');
            },
            // 将集合中所有模型提交到服务器接口
            createAll: function(options) {
                    return Backbone.sync.call(this, 'create', this, options);
            },
            // 修改集合中的所有模型数据
            updateAll: function(options) {
                    return Backbone.sync.call(this, 'update', this, options);
            },
            // 删除集合中所有的模型
            deleteAll: function(options) {
                    var result = Backbone.sync.call(this, 'delete', this, _.extend({
                            url: this.url + '/' + this.getIds()
                    },
                    options));
                    this.remove(this.models);
                    return result;
            }
    });
    
    // 创建集合对象
    var books = new BookList();
    
    // 当集合触发reset事件时, 对数据进行批量同步
    books.on('reset',
    function() {
            books.createAll();
            books.updateAll();
            books.deleteAll();
    });
    
    // 从服务器接口同步默认数据
    books.fetch();

    来分析这个例子:
    我们定义了BookList集合类,并扩展了createAll()、updateAll()和deleteAll()方法(稍后我们再讨论这3些方法的作用)。
    然后我们实例化了一个BookList对象books,并监听了reset事件,reset事件会在从服务器同步数据成功之后被触发。
    接着我们调用fetch()方法从服务器接口获取默认数据(默认数据跟前面例子中返回的数据一致),获取成功后,reset事件的监听函数将被执行。
    我们可以在reset事件的监听函数中做许多的事情(例如写入业务逻辑),但这里为了更直观地演示,我直接调用books的自定义方法进行批量同步。

    首先调用的是createAll()方法,它将把当前集合中的所有数据同步到服务器接口,以Request   Method为POST方式告诉服务器接口创建并保存这些数据。

    在createAll()方法中,我们调用Backbone.sync()方法发送异步请求,请注意sync()方法的第2个参数,它是一个模型或集合对象,当操作为create或update时,在sync()方法内部会调用该对象的toJSON()方法,并将toJSON()方法的返回值作为Request   Payload请求数据发送到服务器接口。

    你可以通过抓包并分析请求信息,来更好地理解它。
    (toJSON()方法默认会返回模型或集合的数据对象,你可以通过重载该方法来自定义需要发送的请求数据)

    我们假设集合中的数据已经被用户批量修改过,所以我们通过updateAll()方法将最新的数据提交到服务器接口。

    updateAll()方法与createAll()方法几乎相同,它们的区别在于updateAll()方法在修改数据时传递给sync()方法的操作名为update而不是create,而发送给服务器的Request Method为PUT而不是POST。

    最后我们通过deleteAll()方法,通知服务器删除集合中的模型,并从本地集合对象中删除这些数据。

    deleteAll()方法与createAll()和updateAll()方法有些不同,因为deleteAll()方法发送给服务器的Request   Method为DELETE方式,这种方式下不能直接调用toJSON()方法将数据发送给接口,因此我们需要手动组装和发送数据。

    在本例中,我们定义了getIds()方法用于将集合中的所有模型的id连接起来,在deleteAll()方法中,我们调用sync()方法发送了Request Method为DELETE的请求,并在URL中将集合中的所有模型id传递给接口进行删除。

  • 相关阅读:
    Linux 学习 step by step (1)
    ubuntu server nginx 安装与配置
    ubuntu server samba服务器配置
    iOS app集成支付宝支付流程及后台php订单签名处理
    mac 连接windows 共享内容
    linux 文件查找,which,whereis,locate,find
    ubuntu server vsftpd 虚拟用户及目录
    ubuntu server 安装 mantis bug tracker 中文配置
    ubuntu server vsftpd 匿名用户上传下载及目录设置
    linux 用户管理,用户权限管理,用户组管理
  • 原文地址:https://www.cnblogs.com/linjiqin/p/3723936.html
Copyright © 2011-2022 走看看