×

EmberJS 教程

EmberJS 概述EmberJS 环境配置EmberJS 应用EmberJS 对象模型EmberJS 模板EmberJS 路由器EmberJS 组件EmberJS 模型EmberJS 视图EmberJS 控制器EmberJS 测试EmberJS 指定查询参数

Ember 对象模型

Ember 类的定义 初始化 继承Ember 类的扩展Ember 计算属性Ember 观察者Ember 数据绑定Ember 枚举Ember 第一章对象模型小结

Ember handlebars模板

Ember handlebars基础Ember handlebars条件表达式Ember handlebars遍历标签Ember handlebars显示对象键Ember handlebars属性绑定Ember {{link-to}} 助手Ember 路由 模板执行渲染顺序Ember {{action}} 助手Ember 表单元素Ember 调试助手Ember 工具类的助手Ember 第二章模板小结

Ember 路由

Ember 路由定义Ember 指定与路由关联的模型Ember 模板渲染Ember 路由重定向Ember 路由终止挑战和激活Ember loading error子路由Ember 查询参数Ember 异步路由

Ember 组件

Ember 组件定义Ember 属性传递Ember 包裹内容自定义包裹组件的HTML标签Ember 处理事件Ember action触发变化

Ember 控制器

Ember 控制器Ember 管理控制器的依赖关系

Ember 模型

Ember model简介Ember 定义模型Ember 记录查询Ember 新建、更新、删除记录Ember 设置记录到StoreEmber model的关联关系处理Ember 元数据Ember 自定义适配器Ember 自定义序列号器

Ember 测试

Ember 测试简介Ember 验收测试Ember 单元测试

Ember model简介


Ember 官网用了大篇幅来介绍model,相比之前的controller简直就是天壤之别啊!

从本篇开始学习 Ember 的模型,这一章也是 Ember 基础部分的最后一章内容,非常的重要(不管你信不信反正我是信了)。

在开始学习model之前先做好准备工作: 重新创建一个 Ember 项目,仍旧使用的是Ember CLI命令创建。

ember new chapter6_models
cd chapter6_models
ember server

在浏览器执行项目,看到如下信息说明项目搭建成功。 Welcome to Ember

本章演示所用到的代码都可以从https://github.com/ubuntuvim/my_emberjs_code/tree/master/chapter6_models获取。

在介绍model之前先在项目中引入firebase。相关的配置教材请移步这里(如果无法加载页面请先在https://www.firebase.com/注册用户)。firebase的官网提供了专门用于Ember的版本,还提供了非常简单的例子。从安装到整合都给出了非常详细代码教程。 下面是我的整合步骤(命令都是在项目目录下执行的):

  • 安装

    ember install emberfire

    安装完成之后会自动创建adapter(app/adapters/application.js),对于这个文件不需要做任何修改,官网提供的代码也许跟你的项目的代码不同,应该是官网的版本是旧版的。

  • 配置config/environment.js 修改第八行firebase: 'https://YOUR-FIREBASE-NAME.firebaseio.com/'。这个地址是你注册用户时候得到的。你可以从这里查看你的地址。比如下图所示位置

截图

  • 再在config/enviroment.jsAPP:{}(大概第20行)后面新增如下代码
    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    },
    contentSecurityPolicy: {
      'default-src': "'none'",
      'script-src': "'self' 'unsafe-inline' 'unsafe-eval' *",
      'font-src': "'self' *",
      'connect-src': "'self' *",
      'img-src': "'self' *",
      'style-src': "'self' 'unsafe-inline' *",
      'frame-src': "*"
    }

    然后再注释掉第7行原有属性(安装firebase自动生成的,但是配置不够完整):contentSecurityPolicy

或者你可以参考我的配置文件:

/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'chapter6-models',
    environment: environment,
    // contentSecurityPolicy: { 'connect-src': "'self' https://auth.firebase.com wss://*.firebaseio.com" },
    firebase: '你的firebase连接',
    baseURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    },
    contentSecurityPolicy: {
      'default-src': "'none'",
      'script-src': "'self' 'unsafe-inline' 'unsafe-eval' *",
      'font-src': "'self' *",
      'connect-src': "'self' *",
      'img-src': "'self' *",
      'style-src': "'self' 'unsafe-inline' *",
      'frame-src': "*"
    }
  };

  // 其他代码省略……
  return ENV;
};

如果不做这个配置启动项目之后浏览器会提示一堆的错误。主要是一些访问权限问题。配置完之后需要重启项目才能生效!

1,简介

model是一个用于向用户呈现底层数据的对象。不同的应用有不同的model,这取决于解决的问题需要什么样的model就定义什么样的model

model通常是持久化的。这也就意味着用户关闭了浏览器窗口model数据不应该丢失。为了确保model数据不丢失,你需要存储model数据到你所指定的服务器或者是本地数据文件中。
一种非常常见的情况是,model数据会以JSON的格式通过HTTP发送到服务器并保存在服务中。Ember还未开发者提供了一种更加简便的方式:使用IndexedDB(使用在浏览器中的数据库)。这种方式是把model数据保存到本地。或者使用Ember Data,又或者使用firebase,把数据直接保存到远程服务器上,后续的文章我将引入firebase,把数据保存到远程服务器上。

Ember使用适配器模式连接数据库,可以适配不同类型的后端数据库而不需要修改任何的网络代码。你可以从emberobserver上看到几乎所有Ember支持的数据库。

如果你想把你的Ember应用与你的远程服务器整合,几遍远程服务器API返回的数据不是规范的JSON数据也不要紧,Ember Data可以配置任何服务器返回的数据。

Ember Data还支持流媒体服务器,比如WebSocket。你可以打开一个socket连接远程服务器,获取最新的数据或者把变化的数据推送到远程服务器保存。

Ember Data为你提供了更加简便的方式操作数据,统一管理数据的加载,降低程序复杂度。
对于model与Ember Data的介绍就到此为止吧,官网用了大量篇幅介绍Model,在此我就不一一写出来了!太长了,写出来也没人看的!!!如果有兴趣自己看吧!点击查看详细信息
下面先看一个简单的例子,由这个例子延伸出有关于model的核心概念。这些代码是旧版写法,仅仅是为了说明问题,本文也不会真正执行。

//  app/components/list-of-drafts.js
export default Ember.Component.extend({
    willRender() {
        // ECMAScript 6语法
        $.getJSON('/drafts').then(data => {
            this.set('drafts', data);
        });
    }
});

定义了一个组件类。并在组件类中获取json格式数据。 下面是组件对应的模板文件。


<ul>
    {{#each drafts key="id" as |draft|}}
    <li>{{draft.title}}</li>
    {{/each}}
</ul>

再定义另外一个组件类和模板

//  app/components/list-button.js
export default Ember.Component.extend({
    willRender() {
        // ECMAScript 6语法
        $.getJSON('/drafts').then(data => {
            this.set('drafts', data);
        });
    }
});

{{#link-to ‘drafts’ tagName=’button’}}
Drafts ({{drafts.length}})
{{/link-to}}

组件list-of-drafts类和组件list-button类是一样的,但是他们的对应的模板却不一样。但是都是从远程服务器获取同样的数据。如果没有Storemodel核心内容之一)那么每次这两个模板渲染都会是组件类调用一次远程数据。并且返回的数据是一样的。这无形中增加了不必要的请求,暂用了不必要的宽带,用户体验也不好。但是有了Store就不一样了,你可以把Store理解为仓库,每次执行组件类时先到Store中获取数据,如果没有再去远程获取。当在其中一个组件中改变某些数据,数据的更改也能理解反应到另一个获取此数据的组件上(与计算属性自动更新一样),而这个组件不需要再去服务请求才能获取最新更改过的数据。

下面的内容将为你一一介绍Ember Data最核心的几个东西:modelsrecordsadaptersstore

2,核心概念

声明:下面简介内摘抄至http://www.emberjs.cn/guides/models/#toc_

1,store

store是应用存放记录的中心仓库。你可以认为store是应用的所有数据的缓存。应用的控制器和路由都可以访问这个共享的store;当它们需要显示或者修改一个记录时,首先就需要访问store

DS.Store的实例会被自动创建,并且该实例被应用中所有的对象所共享。

store可以看做是一个缓存。在下面的cache会结合store介绍。

下面的例子结合firebase演示: 创建路由和model

ember g route store-example
ember g model article
//   app/models/article.js

import DS from 'ember-data';

export default DS.Model.extend({
    title: DS.attr('string'),
    body: DS.attr('string'),
    timestamp: DS.attr('number'),
    category: DS.attr('string')
});

这个就是model,本章要讲的内容就是它!为何没有定义id属性呢?Ember会默认生成id属性。

我们在路由的model回调中获取远程的数据,并显示在模板上。

//  app/routes/store-example.js

import Ember from 'ember';

export default Ember.Route.extend({
    model: function() {
        // 从store中获取id为JzySrmbivaSSFG6WwOk的数据,这个数据是我在我的firebase中初始化好的
        return this.store.find('article', '-JzySrmbivaSSFG6WwOk');
    }
});

find方法的第一个参数是model类名,第二个参数对象的id属性值。记得id属性不需要在model类中手动定义,Ember会自动为你定义。

<h1>{{model.title}}</h1>

<div class="body">
{{model.body}}
</div>

页面加载之后可以看到获取到的数据。

加载得到的数据

下面是我的firebase上的部分数据截图。

firebase数据

可以看到成功获取到id-JzySrmbivaSSFG6WwOk的数据。更多关于数据的操作在后面会详细介绍。

2,model

有关model的概念前面的简介已经介绍了,这里不再赘述。 model定义:

model是由若干个属性构成的。attr方法的参数指定属性的类型。

export default DS.Model.extend({
    title: DS.attr('string'),  //  字符串类型
    flag: DS.attr('boolean'), //  布尔类型
    timestamp: DS.attr('number'),  //  数字类型
    birth: DS.attr(‘date’)  //日期类型
});

模型也声明了它与其他对象的关系。例如,一个Order可以有许多LineItems,一个LineItem可以属于一个特定的Order

App.Order = DS.Model.extend({
  lineItems: DS.hasMany('lineItem')
});

App.LineItem = DS.Model.extend({
  order: DS.belongsTo('order')
});

这个与数据的表之间的关系是一样的。

3,record

recordmodel的实例,包含了从服务器端加载而来的数据。应用本身也可以创建新的记录,以及将新记录保存到服务器端。

记录由以下两个属性来唯一标识:

  1. 模型类型
  2. 一个全局唯一的ID

比如前面的实例article就是通过find方获取。获取到的结果就是一个record

4,adapter

适配器是一个了解特定的服务器后端的对象,主要负责将对记录(record)的请求和变更转换为正确的向服务器端的请求调用。

例如,如果应用需要一个ID1person记录,那么Ember Data是如何加载这个对象的呢?是通过HTTP,还是Websocket?如果是通过HTTP,那么URL会是/person/1,还是/resources/people/1呢?

适配器负责处理所有类似的问题。无论何时,当应用需要从store中获取一个没有被缓存的记录时,应用就会访问适配器来获取这个记录。如果改变了一个记录并准备保存改变时,store会将记录传递给适配器,然后由适配器负责将数据发送给服务器端,并确认保存是否成功。

5,cache

store会自动缓存记录。如果一个记录已经被加载了,那么再次访问它的时候,会返回同一个对象实例。这样大大减少了与服务器端的往返通信,使得应用可以更快的为用户渲染所需的UI。

例如,应用第一次从store中获取一个ID1person记录时,将会从服务器端获取对象的数据。

但是,当应用再次需要ID1person记录时,store会发现这个记录已经获取到了,并且缓存了该记录。那么store就不会再向服务器端发送请求去获取记录的数据,而是直接返回第一次时候获取到并构造出来的记录。这个特性使得不论请求这个记录多少次,都会返回同一个记录对象,这也被称为Identity Map(标识符映射)。

使用标识符映射非常重要,因为这样确保了在一个UI上对一个记录的修改会自动传播到UI其他使用到该记录的UI。同时这意味着你无须手动去保持对象的同步,只需要使用ID来获取应用已经获取到的记录就可以了。

3,架构简介

应用第一次从store获取一个记录时,store会发现本地缓存并不存在一份被请求的记录的副本,这时会向适配器发请求。适配器将从持久层去获取记录;通常情况下,持久层都是一个HTTP服务,通过该服务可以获取到记录的一个JSON表示。

架构图1

如上图所示,适配器有时不能立即返回请求的记录。这时适配器必须向服务器发起一个异步的请求,当请求完成加载后,才能通过返回的数据创建的记录。

由于存在这样的异步性,store会从find()方法立即返回一个承诺(promise)。另外,所有请求需要store与适配器发生交互的话,都会返回承诺。 一旦发给服务器端的请求返回被请求记录的JSON数据时,适配器会履行承诺,并将JSON传递给storestore这时就获取到了JSON,并使用JSON数据完成记录的初始化,并使用新加载的记录来履行已经返回到应用的承诺。

架构图2

下面将介绍一下当store已经缓存了请求的记录时会发生什么。

架构图3

在这种情形下,store已经缓存了请求的记录,不过它也将返回一个承诺,不同的是,这个承诺将会立即使用缓存的记录来履行。此时,由于store已经有了一份拷贝,所以不需要向适配器去请求(没有与服务器发生交互)。

modelsrecordsadaptersstore是你必须要理解的概念。这是Ember Data最核心的东西。


分类导航

关注微信下载离线手册

bootwiki移动版 bootwiki
(群号:472910771)