Emberjs中JSONAPISerializer的經常使用API

Ember Serializer

Emberjs 默認使用 JSONAPISerializer ,在咱們的項目中也是使用JSONAPISerializer.因此只是涉及關於 JSONAPISerializer 的相關內容。
@[TOC]javascript

先後端通訊協議

在目前的版本中,後端咱們使用 大駝峯法命名,可是前端基本使用都是 駝峯法,致使有所差別,在數據獲取展現過程當中,須要統一 key 的名稱。這時就可使用 keyForAttribute 這個方法來實現咱們的需求,好比:前端

//    後端數據
{
    'data': [{
    'type':  'people',
    'id':  '123',
    'attributes': {
        'FirstName':  'Jeff',
        'LastName':  'Atwood',
        'Birthday':  new  Date().getTime()
        }
    }, {
    'type':  'people',
    'id':  '124',
    'attributes': {
        'FirstName':  'Yehuda',
        'LastName':  'Katz',
        'Birthday':  new  Date('2011-11-11 11:11:11').getTime()
        }
    }]
};

而在前端 咱們定義的屬性名爲:java

//    person/model.js
import DS from  'ember-data';

export  default  DS.Model.extend({
    firstName:  DS.attr('string'),
    lastName:  DS.attr('string'),
    birthday:  DS.attr('date')
});

前端使用的是 經常使用的駝峯命名法,在這樣的情形下(先後端屬性名字不一致),咱們就須要修改向後端申請的屬性名稱來將後端數據合理的傳遞到咱們前端定義的屬性名上來:git

//    person/serializer.js
import DS from  'ember-data';
import { camelize, capitalize } from  '@ember/string';
  
export  default  DS.JSONAPISerializer.extend({
    keyForAttribute(key) {
        let newKey = camelize(key);
        return capitalize(newKey);
    }
});

keyForAttribute 這個方法接受 前端 定義的屬性名稱爲參數,如當下的firstName等,通過轉換爲後端相同的屬性名,如FirstName 。這樣來達到先後端通訊的要求。github

前端 modelName 的變化

在上面咱們能夠看到,後端返回數據中的 typepeople,可是前端尋找的type 卻爲person,這是由於 Ember Data 中的約定的關係,可是若是咱們就想讓它尋找的typepeople呢?那咱們就須要用到 modelNameFromPayloadKey 這個方法:後端

//    people/serilizer.js
import DS from  'ember-data';
import { camelize, capitalize } from  '@ember/string';
  
export  default  DS.JSONAPISerializer.extend({
    modelNameFromPayloadKey(key) {
        return key;
    },
    keyForAttribute(key) {
        let newKey =  camelize(key);
        return  capitalize(newKey);
    }
});

經過複寫這個方法就能夠禁止 modelName 的單複數轉換了。
目前咱們是在單獨的 model 下執行的這個 serializer 的幾個方法,若是須要在全局則須要將 serializer.js文件寫在 application1
相應的,還會有payloadKeyFromModelNameapi

## IDs
在 JSONAPI 中 每一個條目都應該使用 id 中的值來做爲爲一標識,先後端都應如此,若是後端沒有將 id 做爲惟一標識,那麼就可使用 primaryKey 這個屬性來定義一個惟一標識:
這是前端定義的:瀏覽器

//    people/serilizer.js
import DS from  'ember-data';
import { camelize, capitalize } from  '@ember/string';
  
export  default  DS.JSONAPISerializer.extend({
    primaryKey: 'pKey',
    modelNameFromPayloadKey(key) {
        return key;
    },
    keyForAttribute(key) {
        let newKey = camelize(key);
        return capitalize(newKey);
    }
});

然後端傳來的數據便可變動爲:app

//    後端數據
{
    'data': [{
    'type':  'people',
    'pKey':  '123',
    'attributes': {
        'FirstName':  'Jeff',
        'LastName':  'Atwood',
        'Birthday':  new  Date().getTime()
        }
    }, {
    'type':  'people',
    'pKey':  '124',
    'attributes': {
        'FirstName':  'Yehuda',
        'LastName':  'Katz',
        'Birthday':  new  Date('2011-11-11 11:11:11').getTime()
        }
    }]
};

縮短屬性名

在平常工做中的難題之一就是命名,而每每後端的命名和前端的不太一致,若是咱們作了大量的展現工做,再更換名稱那就有點比較麻煩,這時候咱們可使用 attrs屬性來達到咱們的目的:
後端傳輸的數據中有一:this

//    ...
    ThePersonSelfHeight:176
    // ...

前端認爲這個名字過於複雜因此想alias:

//    people/serilizer.js
import DS from  'ember-data';
 
export  default  DS.JSONAPISerializer.extend({
    primaryKey: 'pKey',
    attrs: {
        height: 'ThePersonSelfHeight'
    },
    // ...
});

同時也須要在model.js 文件進行相應的修改:

//    people/model.js
// ...
    height:  DS.attr('number')
// ...

便可在頁面中使用.height展現ThePersonSelfHeight 屬性的值。

Format Response

在工做中,後端傳回來的數據每每會有些許嵌套或者是後端人員有本身的思考方式致使數據的結構和咱們設想的有些許不一樣。這個時候咱們能夠經過 serialize() 以及 normalizeResponse() 這兩個方法來實現咱們的需求。
好比後端傳給咱們的數據有:

data: {
    type:  'phone',
    id:  1,
    attributes: {
        brand:  'Nokia',
        capacity:  64,
        size: {
            width:  70.9,
            height:  143.6,
            depth:  7.7,
            weight:  177
        },
        display:  5.8
    }
}

而咱們在數據顯示中但願能直接展現的depth,而不想從size中取:

//    phone/model.js
import DS from  'ember-data';

export  default  DS.Model.extend({
    brand:  DS.attr('string'),
    capacity:  DS.attr('number'),
    depth:  DS.attr('number'),
    display:  DS.attr('number')
});

normalizeResponse()

咱們能夠經過 serializer.js 中的 normalizeResponse()方法來實現咱們的需求:

// phone/serializer.js
// ...
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
    payload.data.attributes.depth = payload.data.attributes.size.depth;
    delete payload.data.attributes.size;
    return  this._super(...arguments);
}

以後在 route.js 中咱們請求數據:

//    route.js
// ...
model() {
    return this.get('store').queryRecord('phone',{})
}

這樣頁面中咱們就能夠直接獲取 depth的值了:

<tr>
    <td>{{model.brand}}</td>
    <td>{{model.capacity}}</td>
    <td>{{model.depth}}</td>
    <td>{{model.display}}</td>
</tr>

除了 normalizeResponse()方法,還有其餘的幾個方法,如:

normalize()

//    phone/serializer.js
// ...
normalize(typeClass, hash) {
    hash.attributes.depth = hash.attributes.size.depth;
    delete hash.attributes.size;
    return  this._super.apply(this, arguments);
}

也能夠起到相同的做用。這個方法能夠在application中定義特定的 typeClass來針對調用。

normalizeQueryRecordResponse()

在本文中咱們獲取數據使用了 queryRecord(),因此咱們還可使用:

//    phone/serializer.js
// ...
normalizeQueryRecordResponse(store, primaryModelClass, payload, id, requestType) {
    payload.data.attributes.depth = payload.data.attributes.size.depth;
    delete payload.data.attributes.size;
    return  this._super(...arguments);
},

normalizeFindAllResponse()

相同的也能夠聯想,當咱們獲取數據爲:

// route.js
    mode() {
        return this.get('store').findAll('phone');
    }

這種狀況的時候,可使用:

//    phone/serializer.js
normalizeFindAllResponse(store, primaryModelClass, payload) {
    let data = payload.data.attributes;
 
    payload.data.attributes.depth = data.size ? data.size.depth : data.depth;
    delete payload.data.attributes.size;
    return  this._super(...arguments);
}

依然能夠達到咱們的目的。Emberjs 還提供其餘的 normalize hook:

  • normalizeFindBelongsToResponse()
  • normalizeFindHasManyResponse()
  • normalizeFindManyResponse()
  • normalizeFindRecordResponse()
  • normalizeQueryResponse()

等其餘修改建立的請求。

normalizeCreateRecordResponse()

這個 hook 的使用須要從 建立一個 record 提及:

//    arcicle/route.js or controller.js
let articles =  this.get('store').createRecord('article', {
    id:  new  Date().getTime(),
    title:  'how to use serialize',
    body:  'let try'
});

在建立了 record 以後,能夠在 EmberInspector 中的 Data 中查看到相應的 data。同時在 serializer.js中添加 normalizeCreateRecordResponse():

//    article/serializer.js
import DS from  'ember-data';

export  default  DS.JSONAPISerializer.extend({
    normalizeCreateRecordResponse(store, primaryModelClass, payload, id, requestType) {
        console.log(payload);
        return  this._super(...arguments);
    }
});

這時在瀏覽器看到這個 hook 並無執行。這個hook 執行的時機時在 保存 record 的時候:

//    article/route.js or controller.js
// ...
articles.save()

刷新後便可看到 normalizeCreateRecordResponse() 此鉤子已經被執行。
同理,其餘的更新/刪除類似:

至此 DS.JSONAPISerialize 的相關屬性與method 已解釋完成。

Written By FrankWang.

  1. serializer.js文件除了最頂層的是在 application文件夾下,其他是跟 model 名稱走的,不是跟路由名稱。
相關文章
相關標籤/搜索