EmberJs之使用Ember-Data

寫在前面

         最近比較忙,換了新工做還要學習不少全新的技術棧,並給本身找了不少藉口來不去堅持寫博客。經常具備諷刺意味的是,更多剩下的時間並無利用而更多的是白白浪費,也許這就是青春吧,揮霍吧,這不是我想要的,既然這樣,我還要繼續寫下去,堅持把博客作好,爭取進前100博客,在此謹記。javascript

                                                                                                         2015年5月7日深夜,於電腦旁。html

文章索引

JS前端框架之Ember.js系列前端

定義模型

Ember-data中全部的模型都繼承自DS.Model,模型內部的屬性是經過DS.attr來聲明的。html5

var attr = DS.attr;

App.Person = DS.Model.extend({

  firstName: attr(),

  lastName: attr(),

  birthday: attr()

});

此外,DS.Model中還能夠添加「計算屬性」:java

App.Person = Ember.Object.extend({
  // these will be supplied by `create`
  firstName: null,
  lastName: null,
 
  fullName: function() {
    return this.get('firstName') + ' ' + this.get('lastName');
  }.property('firstName', 'lastName')
});
 
var ironMan = App.Person.create({
  firstName: "Tony",
  lastName:  "Stark"
});
 
ironMan.get('fullName') // "Tony Stark"

特別的,還能夠顯式指定屬性返回值類型:git

App.Person = DS.Model.extend({
  birthday: DS.attr('date')
});

默認狀況下,REST 適配器支持的屬性類型有stringnumberbooleandate。 傳統的適配器會提供額外的屬性類型,並支持你註冊自定義的屬性類型。 詳情請查看documentation section on the REST Adaptergithub

請注意:date類型參考ISO8601,例如:2014-05-27T12:54:01web

選項

目前,Ember-data只支持一種默認的選項:json

export default DS.Model.extend({
      username: DS.attr('string'),
      email: DS.attr('string'),
      verified: DS.attr('boolean', {defaultValue: false}),
      createdAt: DS.attr('string', {
          defaultValue: function() { return new Date(); }
      })
  });

定義關聯模型

Ember Data 包括了幾個內置的關聯類型,以幫助你肯定你的模型如何相互關聯的。api

理解關聯模型

一對一

使用DS.belongsTo在兩個模型間聲明一對一的關係。

App.User = DS.Model.extend({
  profile: DS.belongsTo('profile')
});
 
App.Profile = DS.Model.extend({
  user: DS.belongsTo('user')
});

一對多

使用DS.belongsTo結合DS.hasMany來聲明兩個模型間的一對多關係,示例以下:

App.Post = DS.Model.extend({
  comments: DS.hasMany('comment')
});
 
App.Comment = DS.Model.extend({
  post: DS.belongsTo('post')
});

多對多

使用DS.hasMany來聲明兩個模型間的多對多關係。

App.Post = DS.Model.extend({
  tags: DS.hasMany('tag')
});
 
App.Tag = DS.Model.extend({
  posts: DS.hasMany('post')
});

顯式反轉

Ember Data會盡最大努力去自動發現關聯關係的映射關係。在上例的一對多的狀況下,修改了comments會自動更新post,應爲這是惟一的一個關聯模型。

可是,有時候對同一個類型有多個belongsTo/hasMany關聯關係。這時能夠經過指定在反向端使用DS.hasManyinverse選項來指定其關聯的模型:

var belongsTo = DS.belongsTo,
    hasMany = DS.hasMany;
 
App.Comment = DS.Model.extend({
  onePost: belongsTo("post"),
  twoPost: belongsTo("post"),
  redPost: belongsTo("post"),
  bluePost: belongsTo("post")
});
 
App.Post = DS.Model.extend({
  comments: hasMany('comment', {
    inverse: 'redPost'
  })
});

固然也能夠在belongsTo一側指定,它將按照預期那樣工做。

建立和刪除記錄

建立

經過調用倉庫的createRecord方法,能夠建立記錄:

store.createRecord('post', {
  title: 'Rails is Omakase',
  body: 'Lorem ipsum'
});

儘管createRecord的使用已經很是直接,可是還須要注意一點,就是目前還不支持將一個承諾賦值給一個關聯。

例如,若是但願給文章設置author屬性,若是指定ID的user並無加載到倉庫中的話,下面的代碼將不會正常工做。

var store = this.store;
 
store.createRecord('post', {
  title: 'Rails is Omakase',
  body: 'Lorem ipsum',
  author: store.find('user', 1)
});

不過在承諾履行時能夠很是方便的進行關聯關係的設置:

var store = this.store;
 
var post = store.createRecord('post', {
  title: 'Rails is Omakase',
  body: 'Lorem ipsum'
});
 
store.find('user', 1).then(function(user) {
  post.set('author', user);
});

刪除

刪除記錄與建立記錄同樣簡單。只須要調用DS.Model實例的deleteRecord()方法便可。這將會吧記錄標記爲isDeleted,而且不在storeall()查詢中返回。刪除操做以後會經過使用save()來進行持久化。此外,也可使用destroyRecord來將刪除和持久化一次完成。

var post = store.find('post', 1);
post.deleteRecord();
post.get('isDeleted'); // => true
post.save(); // => DELETE to /posts/1
 
// OR
var post = store.find('post', 2);
post.destroyRecord(); // => DELETE to /posts/2

將記錄推送進倉庫

上節講到倉庫的概念,倉庫相似於數據的緩衝池,當應用程序向倉庫中查找不存在數據時,倉庫則向數據源發起數據請求,若是存在的話,直接返回倉庫中的數據。

推送記錄

爲了將記錄推入倉庫,須要調用倉庫的push()方法。

var attr = DS.attr;
 
App.Album = DS.Model.extend({
  title: attr(),
  artist: attr(),
  songCount: attr()
});
 
App.ApplicationRoute = Ember.Route.extend({
  model: function() {
    this.store.push('album', {
      id: 1,
      title: "Fewer Moving Parts",
      artist: "David Bazan",
      songCount: 10
    });
 
    this.store.push('album', {
      id: 2,
      title: "Calgary b/w I Can't Make You Love Me/Nick Of Time",
      artist: "Bon Iver",
      songCount: 2
    });
  }
});

持久化記錄

Ember Data中的記錄都基於實例來進行持久化。調用DS.Model實例的save()會觸發一個網絡請求,來進行記錄的持久化。

var post = store.createRecord('post', {
  title: 'Rails is Omakase',
  body: 'Lorem ipsum'
});
post.save(); // => POST to '/posts'

承諾

save()會返回一個承諾,這使得能夠很是容易的來處理保存成功和失敗的場景。下面是一個通用的模式:

var post = store.createRecord('post', {
  title: 'Rails is Omakase',
  body: 'Lorem ipsum'
});
 
var self = this;
 
function transitionToPost(post) {
  self.transitionToRoute('posts.show', post);
}
 
function failure(reason) {
  // handle the error
}
 
post.save().then(transitionToPost).catch(failure);
 
// => POST to '/posts'
// => transitioning to posts.show route

對於失敗的網絡請求,承諾也能夠方便的來處理:

var post = store.createRecord('post', {
  title: 'Rails is Omakase',
  body: 'Lorem ipsum'
});
 
var onSuccess = function(post) {
  this.transitionToRoute('posts.show', post);
};
 
var onFail = function(post) {
  // deal with the failure here
};
 
post.save().then(onSuccess, onFail);
 
// => POST to '/posts'
// => transitioning to posts.show route

更多關於承諾的內容請參看這裏,下面是一個示例展現瞭如何在重試失敗的持久化操做:

function retry(callback, nTimes) {
  // if the promise fails
  return callback().fail(function(reason) {
    // if we haven't hit the retry limit
    if (nTimes-- > 0) {
      // retry again with the result of calling the retry callback
      // and the new retry limit
      return retry(callback, nTimes);
    }
 
    // otherwise, if we hit the retry limit, rethrow the error
    throw reason;
  });
}
 
// try to save the post up to 5 times
retry(function() {
  return post.save();
}, 5);

查詢記錄

Ember Data倉庫提供了一個很是簡單的查詢一類記錄的接口,該接口就是store對象的find方法。在內部,store根據傳入的參數使用findfindAllfindQuery完成查詢。store.find()的第一個參數是記錄的類型,第二個可選參數肯定查詢是獲取全部記錄,仍是一條記錄,仍是特定的記錄。

查詢一個類型的全部記錄

var posts = this.store.find('post');

若是但願獲取已經加載到倉庫中的記錄的列表,而不但願經過一個網絡請求去獲取,可使用all方法:

var posts = this.store.all('post'); // => no network request

find會返回一個將使用DS.RecordArray來履行的DS.PromiseArray,而all直接返回DS.RecordArray

須要重點注意的一點是DS.RecordArray不是一個Javascript數組。它是一個實現了Ember.Enumerable的對象。這一點很是重要,由於例如但願經過索引獲取記錄,那麼[]將沒法工做,須要使用objectAt(index)來獲取。

 

查詢一個記錄

若是調用store.find()方法時,第二個參數是一個數字或者字符串,Ember Data將嘗試獲取對應ID的記錄。find()方法將返回一個用請求的記錄來履行的承諾。

var aSinglePost = this.store.find('post', 1); // => GET /posts/1

查詢記錄

若是傳遞給find方法的第二個參數是一個對象,Ember Data會發送一個使用該對象來序列化出來的查詢參數的GET請求。這是方法返回與不加第二個參數時候同樣的DS.PromiseArray

例如,能夠查詢名爲Peterperson模型的全部記錄:

var peters = this.store.find('person', { name: "Peter" }); // => GET to /persons?name='Peter'

與路由的模型鉤子集成

如同在指定路由的模型一節中討論的同樣,路由是負責告訴模板將渲染哪一個模型。

Ember.Routemodel鉤子支持當即可用的異步值。若是model鉤子返回一個承諾,路由將等待承諾履行條件知足時才渲染模板。

這使得使用Ember Data的異步數據來編寫應用變得容易。只須要經過model鉤子返回請求的記錄,交個Ember來處理是否須要一個網絡請求。

App.Router.map(function() {
  this.resource('posts');
  this.resource('post', { path: ':post_id' });
});
 
App.PostsRoute = Ember.Route.extend({
  model: function() {
    return this.store.find('post');
  }
});
 
App.PostRoute = Ember.Route.extend({
  model: function(params) {
    return this.store.find('post', params.post_id);
  }
});

使用記錄

修改屬性

一旦一條記錄已經加載進來,你就能夠開始修改它的屬性(attributes)了。屬性(attributes)和Ember.js中對象的普通屬性(properties)差很少。

var tyrion = this.store.find('person', 1);
// ...after the record has loaded
tyrion.set('firstName', "Yollo");

你能夠經過isDirty屬性來判斷一條記錄是否被更改,且還沒有保存。此外使用changedAttributes函數還能夠查看記錄哪些部分被修改了,以及這些部分被修改前的值是什麼。changedAttributes返回一個對象,其鍵值是被改變的屬性,而值是一個數組[oldValue, newValue]

person.get('isAdmin');      //=> false
person.get('isDirty');      //=> false
person.set('isAdmin', true);
person.get('isDirty');      //=> true
person.changedAttributes(); //=> { isAdmin: [false, true] }

此時,能夠經過save()將改變持久化,也能夠回滾作的改變。調用rollback()會將全部changedAttributes設置回其原來的值。

person.get('isDirty');      //=> true
person.changedAttributes(); //=> { isAdmin: [false, true] }
person.rollback();
person.get('isDirty');      //=> false
person.get('isAdmin');      //=> false
person.changedAttributes(); //=> {}

使用Fixture

當開發客戶端應用時,服務端可能尚未完成對應API的開發。使用FixtureAdapter能夠先進行Ember應用開發,而後切換到其餘的適配器來使用API,而且這個切換並不須要改變應用的代碼。

使用夾具適配器須要完成三步簡單的配置:

  1. 建立一個新的、使用夾具適配器的store並將其關聯到應用。
  2. 使用DS.Model.extend來定義模型。
  3. 將夾具(樣本數據)關聯到對應的模型類。

建立夾具適配器

只需將其定義爲Ember.Application應用的ApplicationAdapter屬性便可。

var App = Ember.Application.create();

App.ApplicationAdapter = DS.FixtureAdapter;

定義模型

這裏定義一個編寫Ember文檔的人員的模型:

App.Documenter = DS.Model.extend({

  firstName: DS.attr( 'string' ),

  lastName: DS.attr( 'string' )

});

關聯夾具與模型類

關聯夾具很是之簡單。只須要將一個Javascript對象集合賦值給模型的FIXTURES屬性便可:

App.Documenter.FIXTURES = [

  { id: 1, firstName: 'Trek', lastName: 'Glowacki' },

  { id: 2, firstName: 'Tom' , lastName: 'Dale'     }

]

在Route中使用:

App.DocumenterRoute = Ember.Route.extend({

  model: function() {

    return this.store.find('documenter', 1); // returns a promise that will resolve

                                             // with the record representing Trek Glowacki

  }

});

命名慣例

REST Adapter不一樣,夾具適配器並不作任何命名慣例的猜想。如同上例中所示,若是在DS.Model.extend中定義了一個firstName屬性,那麼須要在夾具樣本數據中使用firstName來表示該屬性。

更重要地是,須要確保夾具樣本數據中得每一條記錄都有一個惟一的標識。缺省狀況下,Ember Data假定該標識爲id。若是沒有重定義主鍵標識名,又未在記錄中提供一個id,那麼夾具適配器會拋出一個錯誤。

 

鏈接Http服務器

若是Ember應用須要從HTTP服務器加載JSON數據,本指南將介紹如何配置Ember Data來從服務器端加載記錄,不論服務器返回的數據格式是什麼樣子。

倉庫使用了一個稱爲適配器,知道如何經過網絡進行通訊的對象。默認狀況下,倉庫會使用DS.RESTAdapter,一個能夠與HTTP服務器經過XHR進行JSON數據交互的適配器。

本指南分爲兩部分。第一部分介紹適配器的默認行爲,包括將從哪些URL獲取記錄,和指望的JSON格式。

第二部分介紹如何從新定義這些默認行爲,如請求數據的URL和JSON的結構。

URL約定

REST適配器使用模型的名稱來斷定將要發送JSON的URL。

例如,用ID來請求一個App.Photo的記錄:

App.PhotoRoute = Ember.Route.extend({

  model: function(params) {

    return this.store.find('photo', params.photo_id);

  }

});

REST適配器將發送一個GET請求到/photos/1。

能夠在記錄上執行的操做,在REST適配器中被映射到以下的URL上:

操做

HTTP Verb

URL

查詢

GET

/people/123

查詢全部

GET

/people

更新

PUT

/people/123

建立

POST

/people

刪除

DELETE

/people/123

Json約定

給定模型以下:

var attr = DS.attr,

    hasMany = DS.hasMany,

    belongsTo = DS.belongsTo;

App.Post = DS.Model.extend({

  title: attr(),

  comments: hasMany('comment'),

  user: belongsTo('user')

});


App.Comment = DS.Model.extend({

  body: attr()

});

Ember Data指望一個發送到/posts/1請求將返回以下的JSON格式:

{

  "post": {

    "id": 1,

    "title": "Rails is omakase",

    "comments": ["1", "2"],

    "user" : "dhh"

  },


  "comments": [{

    "id": "1",

    "body": "Rails is unagi"

  }, {

    "id": "2",

    "body": "Omakase O_o"

  }]

}

自定義適配器

自定義REST適配器,須要定義一個DS.RESTAdapter的子類,並將其命名爲App.ApplicationAdapter。接下來只須要從新定義它的屬性和方法,就能夠自定義記錄如何加載和保存了。

自定義URL

URL前綴

若是JSON API並不在主機的根目錄,而是在一個其餘的路徑下,那麼須要爲請求設置一個URL前綴。

例如,在使用了版本化的JSON API時,請求一個特定的person可能須要發送請求到/api/v1/people/1。

這種狀況下,須要設置namespace屬性爲api/v1。

App.ApplicationAdapter = DS.RESTAdapter.extend({

  namespace: 'api/v1'

});

如今請求一個ID爲1person就會發送請求到/api/v1/people/1

URL主機

若是JSON API的主機與提供Ember應用服務的主機不一樣,那麼須要修改發送HTTP請求的主機。

注意爲了使其正常工做,須要使用支持CORS的瀏覽器,而且服務器須要配置支持發送正確的CORS頭。

修改請求對應的目標主機,須要設置host屬性:

App.ApplicationAdapter = DS.RESTAdapter.extend({

  host: 'https://api.example.com'

});

如今請求一個ID爲1person就會發送請求到https://api.example.com/people/1

自定義HTTP頭

一些API須要指定HTTP頭,例如提供一個API密鑰。任意鍵值對HTTP頭能夠經過RESTAdapter的headers屬性來進行設置,Ember Data會將這些頭設置到到每一個Ajax請求中去。

例如:

App.ApplicationAdapter = DS.RESTAdapter.extend({

  headers: {

    "API_KEY": "secret key",

    "ANOTHER_HEADER": "Some header value"

  }

});

請求任意資源都會包含以下的HTTP頭。

ANOTHER_HEADER: Some header value

API_KEY: secret key

處理元數據

與從倉庫中返回的記錄的同時,有時候須要處理一些元數據。元數據是除記錄外,與特定模型類型一同的數據。

分頁是常見的一種元數據。例如一個博客擁有一次沒法顯示完的文章,那麼就須要使用以下的查詢:

this.store.findQuery("post", {

  limit: 10,

  offset: 0

});

爲了獲取不一樣頁面的數據,只須要以10爲增量來修改offset。到目前爲止,一切都正常。如今有一個問題,就是如何知道擁有多少頁數據呢?服務器須要以元數據的形式返回全部的記錄數。

默認狀況下,Ember Data的JSON反序列化會查找一個名爲meta的鍵:

{

  "post": {

    "id": 1,

    "title": "Progressive Enhancement is Dead",

    "comments": ["1", "2"],

    "links": {

      "user": "/people/tomdale"

    },

    // ...

  },


  "meta": {

    "total": 100

  }

}

特定類型的元數據將被設置到meta中。可使用store.metadataFor來獲取。

var meta = this.store.metadataFor("post");

如今meta.total能夠用來計算擁有多少頁文章了。

此外,經過重寫extractMeta方法,能夠自定義元數據抽取的方法。例如,服務器沒有使用meta對象來返回元數據,而是返回了下面的格式:

{

  "post": [

    // ...

  ],

  "total": 100

}

那麼能夠這樣來抽取元數據:

App.ApplicationSerializer = DS.RESTSerializer.extend({

  extractMeta: function(store, type, payload) {

    if (payload && payload.total) {

      store.metaForType(type, { total: payload.total });  // sets the metadata for "post"

      delete payload.total;  // keeps ember data from trying to parse "total" as a record

    }

  }

});

自定義適配器

在Ember Data中,處理與後臺數據倉庫通訊的邏輯是經過Adapter來完成的。Ember Data適配器內置了一些關於REST API的假定。若是後臺的實現與Ember Data假定的慣例不一樣,那麼經過擴展缺省的適配器可能很容易的實現。

有時由於一些緣由須要自定義適配器,例如須要使用下劃線風格的URL,使用REST外的其餘媒介來與後臺通訊,或者是使用本地後臺.

擴展適配器在Ember Data中是一個常見的過程。Ember的立場是應該經過擴展適配器來添加不一樣的功能,而非添加標識。這樣可使得代碼更加容易測試,更加易於理解,同時也下降了可能須要擴展適配器的用戶的代碼。

若是後臺具備必定的一致性規則,那麼能夠定義一個ApplicationAdapterApplicationAdapter的優先級比缺省的適配器高,可是又會被模型特定的適配器取代。

App.ApplicationAdapter = DS.RESTAdapter.extend({

    // Application specific overrides go here

});

若是一個模型的後臺有一些特殊的規則,那麼能夠定義一個模型特定的適配器,並將適配器命名爲:"ModelName" + "Adapter"。

App.PostAdapter = DS.RESTAdapter.extend({

  namespace: 'api/v1'

});

缺省狀況下,Ember Data內置了一些很是有用的適配器。能夠根據本身的實際狀況,選擇其中之一做爲起點來自定義適配器。

DS.Adapter是最基礎的適配器,其自身不包含任何功能。若是須要建立一個與Ember適配器有根本性區別的適配器,那麼能夠這裏入手。

DS.FixtureAdapter是一個用來從內存中加載記錄的適配器,經常使用於開發和測試階段。

DS.RESTAdapter是最通用的擴展適配器。RESTAdapter能夠實現store與HTTP服務器之間經過XHR交互JSON數據。大部分使用JSON API的Ember應用都應該使用RESTAdapter

DS.ActiveModelAdapter是一個RESTAdapter的特列,用於與Rails風格的REST API協同工做。

自定義RESTAdapter

DS.RESTAdapter是Ember Data提供的一個最通用的擴展適配器。它提供了一些很是有用的,能夠擴展來與非標準化的後臺接口通訊的鉤子。

自定義端點路徑

namespace屬性用來指定一個特定的url前綴。

App.ApplicationAdapter = DS.RESTAdapter.extend({

  namespace: 'api/1'

});

App.Person的請求將會發至/api/1/people/1

自定義主機路徑

缺省狀況下,適配器認爲主機是當前域。若是但願指定一個新的域,那麼能夠經過設置適配器的host屬性來指定。

App.ApplicationAdapter = DS.RESTAdapter.extend({

  host: 'https://api.example.com'

});

App.Person的請求將會發至https://api.example.com/people/1

自定義路徑

缺省狀況下,RESTAdapter嘗試將模型名進行駝峯化和複數化來做爲路徑名。若是這種慣例並不符合使用的後臺接口,能夠經過重載pathForType方法來實現。

例如,並不須要將模型名稱複數化,須要採用下劃線分割的模式替代駝峯命名,那麼能夠這樣來重載pathForType方法:

App.ApplicationAdapter = DS.RESTAdapter.extend({

  pathForType: function(type) {

    return Ember.String.underscore(type);

  }

});

App.Person的請求將會發至/person/1。 App.UserProfile的請求將會發至/user_profile/1

製做適配器

defaultSerializer屬性能夠用來指定適配器使用的序列化對象。這隻在沒有模型特定的序列化對象,也沒有ApplicationSerializer的狀況下。

在一個應用中,指定一個ApplicationSerializer比較容易。可是若是自定了一個通訊的適配器,而且沒有指定一個ApplicationSerializer,那麼設定defaultSerializer屬性,來確保Ember的行爲正確性比較重要。

MyCustomAdapterAdapter = DS.RESTAdapter.extend({

  defaultSerializer: '-default'

});

社區適配器

若是Ember Data內置的適配器並不能很好的與使用的後臺工做,能夠查看社區維護的Ember Data適配器,看有不有合適的選擇。能夠去一下地方去查找:

 

常見問題

應該使用查詢仍是過濾條件來查詢記錄

這取決於想要查詢多少記錄和記錄是否已經加載到倉庫。

查詢比較適合於搜索成百上千甚至百萬級的記錄。須要作的只是將搜索條件發送給服務器端,而後搜索匹配的記錄的責任就交由服務器來處理。由於服務器返回的數據包含匹配的記錄的ID,因此倉庫即便以前沒有加載這些記錄也不會有影響;倉庫發現這些記錄並無被緩存會經過ID來發送請求獲取記錄。

使用查詢的缺點是它們沒法獲得自動更新,效率較低,而且要求服務器端支持但願執行的查詢類型。

由於服務器端決定與查詢匹配的記錄,而不是倉庫,查詢沒法自動更新。若是須要更新查詢結果,須要手動的調用reload(),並等待服務器端返回。若是在客戶端建立了一個新的記錄,該記錄不會自動在結果中顯示,除非服務端已經包含了該記錄,而且從新加載了查詢結果。

由於倉庫必須讓服務器端來決定查詢的結果,所以須要一個網絡請求。這會讓用戶以爲緩慢,特別是在用於與服務器間的鏈接速度較慢的狀況下。當須要訪問服務器的話,Javascript Web應用會明顯的感受到慢。

最後,執行查詢須要倉庫和服務器端協同工做。在默認狀況下,Ember Data將搜索條件做爲HTTP請求的body發送到服務器端。若是服務器端並不支持查詢的格式,那麼須要修改服務器以便支持查詢,或者經過自定義一個適配器來定義須要發送的查詢。

過濾器是對倉庫中緩存的對象執行實時的查詢。一旦新的記錄被加載到倉庫,過濾器會自動檢查記錄是否匹配條件,若是匹配,會將其加入到搜索結果的數組中。若是這個數組顯示在模板中,模板也會自動更新。

過濾器還會顧及新建立的還未保存的記錄,以及被修改未保存的記錄。若是但願記錄在客戶端一建立或者修改就在搜索結果中顯示,那麼就應該是用過濾器。

請記住,若是倉庫不知道記錄的存在,記錄不會包含在過濾器中。經過倉庫的push()方法能夠確保倉庫知道記錄的存在。

此外也有一個制約條件,就是有多少記錄能夠保存在內存中進行過濾,直到出現性能問題。

最後,應該聯合使用查詢和過濾器來揚長避短。記住經過查詢服務器返回的記錄都是緩存在倉庫中的。能夠利用這一點來作過濾,經過查詢倉庫來將記錄加載到倉庫,而後使用一個過濾函數來匹配相同的記錄。

這能夠避免全部的記錄都經過服務器來查詢,同時也建立了一個能包含客戶端建立和修改的記錄的動態更新列表。

App.PostsFavoritedRoute = Ember.Route.extend({

  model: function() {

    var store = this.store;

 

    // Create a filter for all favorited posts that will be displayed in

    // the template. Any favorited posts that are already in the store

    // will be displayed immediately;

    // Kick off a query to the server for all posts that

    // the user has favorited. As results from the query are

    // returned from the server, they will also begin to appear.

    return store.filter('post', { favorited: true }, function(post) {

      return post.get('isFavorited');

    });

  }

});

如何將後臺建立的記錄通知Ember Data?

當經過Ember Data的store.find方法來請求一條記錄時,Ember會自動將數據加載到store中。這樣Ember就避免了在以後在發起一個請求來獲取已經獲取到的記錄。此外,加載一條記錄到store時,全部的包含該條記錄的RecordArray都會被更新(例如store.filter或者store.all構造的)。這就意味着全部依賴與RecordArray的數據綁定或者計算屬性都會在添加新的或者更新記錄值的時候自動進行同步。

而一些應用可能但願能不經過store.find請求記錄來添加或者更新store中得記錄。爲了實現這種需求,能夠經過使用DS.StorepushpushPayload,或者update方法。這對於那些有一個通道(例如SSE或者Web Socket)通知應用後臺有新記錄建立或者更新很是有用。

push是加載記錄到Ember Data的store的最簡單方法。當使用push時,必定要記住將JSON對象推入store以前將其反序列化。push一次只接受一條記錄。若是但願一次加載一組記錄到store那麼能夠調用pushMany.

socket.on('message', function (message) {

  var type = store.modelFor(message.model);

  var serializer = store.serializerFor(type.typeKey);

  var record = serializer.extractSingle(store, type, message.data);

  store.push(message.model, record);

});

pushPayload是一個store#push方法的便利封裝,它將使用模型實現了pushPayload方法的序列化對象來反序列化有效載荷。須要注意的是這個方法並不能與JSONSerializer一同使用,由於其並無實現pushPayload方法。

socket.on('message', function (message) {

  store.pushPayload(message.model, message.data);

});

updatepush方法相似,不一樣的是其能夠處理部分屬性,而不須要覆蓋整個記錄的屬性。這個方法對於只接收到記錄改變的屬性的通知的應用尤其有用。與push方法同樣,update須要在調用以前將JSON對象反序列化。

socket.on('message', function (message) {

  var hash = message.data;

  var type = store.modelFor(message.model);

  var fields = Ember.get(type, 'fields');

  fields.forEach(function(field) {

    var payloadField = Ember.String.underscore(field);

    if (field === payloadField) { return; }

      hash[field] = hash[payloadField];

      delete hash[payloadField];

  });

  store.push(message.model, hash);

});

 

資源引用

 

https://github.com/emberjs/data

相關文章
相關標籤/搜索