angular-restmod, Rails 对 Angular的REST API inspired

分享于 

31分钟阅读

GitHub

  繁體 雙語
Rails inspired REST-API ORM for Angular
  • 源代码名称:angular-restmod
  • 源代码网址:http://www.github.com/platanus/angular-restmod
  • angular-restmod源代码文档
  • angular-restmod源代码下载
  • Git URL:
    git://www.github.com/platanus/angular-restmod.git
    Git Clone代码到本地:
    git clone http://www.github.com/platanus/angular-restmod
    Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/platanus/angular-restmod
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
    
    Angular Restmod

    Angular Restmod是开源软件,由以下内容赞助:

    Supporter

    ( 我们需要这个库的赞助商,如果你想要帮助,请写到 contact@platan.us

    [Gitter](https://badges.gitter.im/Join chat。svg )!

    Build StatusCode ClimateStories in ReadyBower version

    Restmod创建可以从 Angular 中使用的对象来与你的RESTful API交互。

    在服务器端数据库上保存自行车将非常简单:

    var newBike =Bike.$build({ brand:'Trek' });newBike.model='5200';newBike.$save(); // bike is persisted sending a POST to/bikes

    它还支持集合。关系。生命周期钩子。属性重命名。侧数据加载和更多。 要了解更多信息,请继续阅读,查看这里演示文稿,了解有关更多信息,请查看API参考:http://platanus.github.io/angular-restmod

    如果你使用 Ruby on Rails,我们建议使用 active_model_serializers 来实现无缝集成。

    为什么 Restmod?

    Restmod使 Rails 易于在 Angular 框架中使用。 of succesfuly结合了 Angular 设计与记录风格积极自动化的结合。 还有其他可用的替代方法:

    • 对于小型项目( 包括 Angular 选项),$resource: 可能足够了。 它只提供了基本的模型类型层,具有有限的特性。
    • Restangular: 非常完整,但是不建议模型层,也不支持 jsonapi.org. 中所见的链接资源响应。
    • 不错的替代方案在它的功能方面仍然非常有限。
    • ModelCore: 激发了角度 activerecord,提供了更完整的特性集,但缺乏测试。

    用 SauceLabs awesome OpenSauce服务对与AngularJS相同的平台进行全面测试,以达到 !

    启动

    1获取代码

    你可以直接从仓库得到它

    
    git clone git@github.com:platanus/angular-restmod.git
    
    
    
    

    但是我们建议你使用 Bower 来检索Restmod包

    
    bower install angular-restmod --save
    
    
    
    

    如果你愿意,也可以使用npm包

    
    npm install angular-restmod -d
    
    
    
    
    2在你的项目中包含它

    确保代码中需要restmod源。

    <scripttype="text/javascript"src="js/angular-restmod-bundle.min.js"></script>

    接下来,将 Angular MODULE 作为你的应用程序依赖项

    module=angular.module('MyApp', ['restmod'])
    REST API 集成

    Restmod附带了各种( 现在只需要一个) 预定义 API-style-mixins,根据你的后端配置来选择。

    查看样式列表了解更多信息。 我们要在这上面寻找贡献者 !

    如果你不设置基本样式 'No API style base was included' 将生成警告,有关详细信息,请参阅上面的链接。

    如果仍然需要更改某些行为,或者希望创建自己的api样式,则可以使用以下配置:

    • 通用url前缀配置
    • 主密钥 NAME 配置
    • Json root 属性配置
    • Json元数据提取
    • jsonapi.org 样式api的Json端数据解析( 用于使用'链接'
    • 请求自定义
    • Url格式设置选项

    确保在启动API集成之前阅读了 API集成常见问题解答

    基本用法

    首先,使用 restmod.model 方法创建一个新模型。 我们建议你将每个模型放在单独的工厂中。 model的第一个参数是资源 URL。

    module.factory('Bike', function(restmod) {
     returnrestmod.model('/bikes');
    });

    生成的模型类型提供了基本的CRUD操作以与API交互:

    使用 $find 检索对象使用,返回的对象将在接收到服务器响应时填充响应数据。

    假设你有一个 REST API,它对/bikes/1 上的get请求响应 JSON

    {
     "id": 1,
     "brand": "Trek",
     "created_at": "2014-05-23"}

    然后在你的代码里

    bike =Bike.$find(1);

    在执行这一行之后,自行车对象是一个空对象。 这个自行车对象将在API返回一些数据后立即被填充。 这在 Angular 方面很有用。 不过,当数据可用时,你可以使用 $then 来做一些事情。

    bike.$then(function() {
     expect(bike.brand).toBeDefined();
    });

    如果需要向 $find 传递额外的参数,可以使用第二个函数参数。

    bike =Bike.$find(1, { includeParts:true });

    重新加载对象使用 $fetch警告: 这将覆盖修改后的属性。

    bike.$fetch();

    如果只希望检索还没有检索对象数据的对象,请使用 $resolve 而不是 $fetch:

    bike.$resolve();

    将对象标记为未解析的调用 $reset。 可以挂钩到 before-resolve 事件,为已经解析对象添加一些过期逻辑,只需在钩子中调用 $reset 即可以检索。

    如果在解析函数中返回,请记住使用 $resolve().$asPromise()

    若要检索对象集合 $collection 或者 $search,可以使用。

    bikes =Bike.$search({ category:'enduro' });// same asbikes =Bike.$collection({ category:'enduro' }); // server request not yet sentbikes.$refresh();

    若要重新加载集合,请使用 $refresh。 若要追加更多结果,请使用 $fetch

    bikes =Bike.$collection({ category:'enduro' });bikes.$refresh({ page:1 }); // clear collection and load page 1bikes.$fetch({ page:2 }); // page 2 is appended to page 1, usefull for infinite scrolls...bikes.$refresh({ page:3 }); // collection is reset, page 3 is loaded on response

    若要更新对象,只需修改属性并调用 $save

    bike =Bike.$find(1);bike.brand='Trek';bike.$save();

    要创建一个新对象,使用 $build 然后调用 $save。 这将向服务器发送一个POST请求。

    var newBike =Bike.$build({ brand:'Comencal' });newBike.model='Meta';newBike.$save(); // bike is persisted

    要修补一个对象,只需修改属性并调用 $save 传递要作为第一个参数修补的属性的array。

    bike =Bike.$find(1);bike.brand='Trek';bike.model='Slash';bike.dim= { width:10.0, height:10.0 };bike.$save(['brand', 'dim']); // will only send brand and dim (every sub property)

    要指定在面片中发送的单个子属性,请使用点表示法:

    bike =Bike.$find(1);bike.brand='Trek';bike.model='Slash';bike.dim= { width:10.0, height:10.0 };bike.$save(['dim.height']); // will only send dim.height

    或者使用 $create

    var newBike =Bike.$create({ brand:'Comencal', model:'Meta' });

    如果调用集合,$build$create 将返回一个绑定对象,该对象将在成功保存时添加。

    newBike =bikes.$create({ brand:'Comencal', model:'Meta' });// after server returns, the 'bikes' collection will contain 'newBike'.

    若要在绑定集合上显示非保存的对象,请使用 $reveal

    var newBike =bikes.$create({ brand:'Comencal', model:'Meta' }).$reveal();// 'newBike' is inmediatelly available at 'bikes'

    最后,要销毁一个对象,只需调用 $destroy

    bike.$destroy();

    在绑定到集合的记录上调用 $destroy的$create, 一样,它也将从服务器响应的集合中移除它。

    上面描述的所有REST操作都使用 $q 承诺,当操作成功或者失败时。 请看一下的承诺指南,了解更多关于这个问题的细节。

    定制模型行为

    定义模型时,你可以通过定义对象

    Bike =restmod.model('api/bikes').mix(// This is the definition object:{
     createdAt: { encode:'date' },
     owner: { belongsTo:'User' }
    }
    );

    定义对象允许你:

    • 定义模型之间的关系
    • 自定义属性序列化默认值为
    • 设置模型配置变量。
    • 添加收费的自定义方法
    • 添加生命周期钩子

    关系

    关系定义如下:

    Bike =restmod.model('/bikes').mix({
     parts: { hasMany:'Part' },
     owner: { belongsTo:'User' }
    });

    有四种类型的关系:

    HasMany

    假设你有以下'部分'模型:

    module.factory('Part', function() {
     returnrestmod.model('/parts');
    });

    HasMany关系允许你直接从自行车对象访问特定自行车的零件。 换句话说,HasMany是模型实例( 自行车) 与模型集合( 部件) 之间的层次关系。

    Bike =restmod.model('/bikes').mix({
     parts: { hasMany:'Part' }
    });
    bike =Bike.$new(1); // no request are made to the server yet.parts =bike.parts.$fetch(); // sends a GET to/bikes/1/parts

    注:这不一定是很好的建模。 比如,如果破坏自行车部分,如果不是你想要的资源,你将在/parts/:id. 中发送一个删除信息,如果这不是你想要的资源,那么你应该考虑在自行车和一个零件之间使用这个资源。

    之后,在'部件'已经被解析之后,

    parts[0].$fetch(); // updates the part at index 0. This will do a GET/parts/:id

    在集合上调用 $create 将投递到集合嵌套的url。

    var part =bike.parts.$create({ serialNo:'XX123', category:'wheels' }); // sends POST/bikes/1/parts

    子集合模型是嵌套的,则集合项的所有CRUD路径都绑定到父集合。

    如果'部分'被定义为:

    restmod.model();

    上面的示例将执行如下操作:

    console.log(bike.parts[0].$url())bike.parts[0].$fetch();

    将发送到/bikes/1/parts/:id 而不是/parts/:id

    HasMany选项

    有许多关系为你提供了以下选项来自定义它的行为:

    • path: 将更改用于获取/创建记录的相对路径。 比如: { hasMany: 'Part', path: 'pieces' }
    • inverseOf: 添加子对象上指向父对象的属性。 比如: { hasMany: 'Part', inverseOf: 'bike' }
    • params: 获取集合时要使用的可选查询字符串参数。 比如: { hasMany: 'Part', params: { foo: 'bar' } }
    • hooks: 你还可以定义 hasMany 关系钩子。 检查的高级文档了解更多信息。
    HasOne

    这是模型实例与模型另一实例之间的层次关系。 子实例url绑定到父 url。 子实例在与父实例相同的时间创建,因此即使父实例未解析,也可用。

    假设你有以下'用户'模型:

    module.factory('User', function() {
     returnrestmod.model('/users');
    });

    这与通过 hasOne 关系的'自行车'相关:

    Bike =restmod.model('/bikes').mix({
     owner: { hasOne:'User' }
    });

    然后,可以通过了解自行车主密钥( id ) 来获取自行车数据的拥有者:

    owner =Bike.$new(1).owner.$fetch();

    将发送 get/bikes/1/owner"

    因为用户资源定义了自己的资源 url:

    owner.name='User';owner.$save();

    将发送/user/X.

    如果要将'用户'定义为嵌套资源:

    module.factory('User', function() {
     returnrestmod.model();
    });

    然后调用:

    owner.name='User';owner.$save();

    将把a 发送到/bikes/1/owner

    HasOne选项

    有许多关系为你提供了以下选项来自定义它的行为:

    • path: 将更改用于获取/创建记录的相对路径。 比如: { hasOne: 'Part', path: 'pieces' }
    • inverseOf: 添加子对象上指向父对象的属性。 比如: { hasOne: 'Part', inverseOf: 'bike' }
    • hooks: 你还可以定义 hasOne 关系钩子。 检查的高级文档了解更多信息。
    BelongsTo

    这里关系应在以下情况中使用:

    • api资源通过id引用另一个资源:
    {
     name:'...',
     brand:'...',
     owner_id:20}
    • api资源contanis另一个资源作为内联属性,并且不提供与嵌套url相同的对象:
    {
     name:'...',
     brand:'...',
     owner: {
     id:20,
     user:'extreme_rider_99' }
    }

    应用时,引用实例不绑定到主机范围,并且在服务器响应父节点的一个 $fetch 时,将产生。

    假设你与以前拥有相同的'用户'模型:

    module.factory('User', function() {
     returnrestmod.model('/users');
    });

    这与通过 belongsTo belongsTo belongsTo关系相关的'自行车'相关:

    Bike =restmod.model('/bikes').mix({
     owner: { belongsTo:'User', key:'last_owner_id' } // default key would be 'owner_id'});

    此外,你还拥有以下自行车资源:

    
    GET/bikes/1
    
    
    
    {
    
    
     id: 1,
    
    
     brand: 'Transition',
    
    
     last_owner_id: 2
    
    
    }
    
    
    
    

    然后检索资源:

    bike =Bike.$find(1);

    如果在服务器响应到达后,它的所有者属性初始化为具有id=2的用户,则所有者属性将仅在服务器响应后可用。

    然后打电话

    bike.owner.$fetch();

    将发送到/users/2,并使用用户数据填充所有者属性。

    这里关系还支持子对象数据在父对象数据中内联。 可以使用 map 属性选择内联属性 NAME。

    让我们重新定义 bike 模型:

    var Bike =restmod.model('/bikes').mix({
     owner: { belongsTo:'User', map:'last_owner' } // map would default to *owner*});

    假设最后的自行车资源看起来像:

    
    GET/bikes/1
    
    
    
    {
    
    
     id: 1,
    
    
     brand: 'Transition',
    
    
     last_owner: {
    
    
     id: 2
    
    
     name: 'Juanito'
    
    
     }
    
    
    }
    
    
    
    

    然后检索自行车资源:

    var bike =Bike.$find(1);

    将生成 bike 对象,它的所有者属性初始化为具有id=2和name=Juanito的用户。 以前一样,owner服务器响应到达后,所有者属性将只有

    保存宿主对象时,将使用所选外键在请求中发送引用主键。

    根据前面的模型定义,执行以下操作:

    var bike =Bike.$create({ last_owner:User.$find(20) });

    将生成以下请求:

    
    POST/bikes
    
    
    
    {
    
    
     owner_id: 20
    
    
    }
    
    
    
    
    BelongsToMany

    这里关系应在以下情况中使用:

    • api资源通过id引用另一个资源:
    {
     name:'...',
     brand:'...',
     parts_ids: [1,2]
    }
    • api资源作为内联属性包含另一个资源,并且不提供与嵌套url相同的对象:
    {
     name:'...',
     brand:'...',
     parts: [
     { id:1, user:'handlebar' },
     { id:2, user:'wheel' }
     ]
    }

    检索时,引用的实例将不绑定到主机的作用域。

    假设你有以下'部分'定义:

    module.factory('Part', function() {
     returnrestmod.model('/parts');
    });

    这与通过 belongsToMany 关系相关的'自行车'相关:

    Bike =restmod.model('/bikes').mix({
     parts: { belongsToMany:'Part', keys:'part_keys' } // default key would be 'parts_ids'});

    此外,你还拥有以下自行车资源:

    
    GET/bikes/1
    
    
    
    {
    
    
     id: 1,
    
    
     brand: 'Transition',
    
    
     parts_keys: [1, 2]
    
    
    }
    
    
    
    

    然后检索资源:

    bike =Bike.$find(1);

    将生成一个带有 parts 属性的bike 对象,该对象包含两个部分 object $pks 设置为 1和 2 ( 但空的)。

    这里关系还支持子对象数据在宿主对象数据中内联。 可以使用 map 属性选择内联属性 NAME。

    考虑到与前面相同的自行车模型,假设现在自行车API资源看起来如下所示:

    假设最后的自行车资源看起来像:

    
    GET/bikes/1
    
    
    
    {
    
    
     id: 1,
    
    
     brand: 'Transition',
    
    
     parts: [
    
    
     { id: 1, user: 'handlebar' },
    
    
     { id: 2, user: 'wheel' }
    
    
     ]
    
    
    }
    
    
    
    

    然后检索自行车资源:

    var bike =Bike.$find(1);

    Will属性produce包含 two Part objects对象的bike 对象,并将 $pks 设置为 1和 2.

    保存宿主对象时,将使用所选键在请求中发送引用主键。

    根据前面的模型定义,执行以下操作:

    var bike =Bike.$create({ parts: [Part.$find(1), Part.$find(2)] });

    将生成以下请求:

    
    POST/bikes
    
    
    
    {
    
    
     parts_keys: [1, 2]//remember we changed the keys property name before!
    
    
    }
    
    
    
    

    序列化,屏蔽和默认值。

    当你与API进行通信时,某些属性类型需要特殊处理( 比如日期,例如日期)

    解码

    当属性到达服务器时,可以指定解码属性的方法。

    假设你已经定义了这样的过滤器:

    Angular.factory('DateParseFilter', function() {
     returnfunction(_value) {
     date =newDate();
     date.setTime(Date.parse(_value));
     return date;
     }
    })

    然后将它用作标准解码器,如下所示:

    var Bike =restmod.model('/bikes').mix({
     createdAt: {decode:'date_parse' }
    });

    编码

    若要在将属性发送回服务器时指定对属性进行编码的方法: 前面的示例( 解码) 一样,你使用 Angular 过滤器。 在本例中,我们使用内置的'日期'过滤器。

    var Bike =restmod.model('/bikes').mix({
     createdAt: {encode:'date', param:'yyyy-MM-dd'}
    });

    你可以用一个内联函数代替过滤器的NAME,这两个代价都是和 decode。 还可以使用序列化器对象捆绑一个编码器和解码器,检查 API引用以获得更多信息。

    属性屏蔽

    按照 Angular 约定,以'$'符号开始的属性被视为 private,从不发送到服务器。 此外,你可以定义一个掩码,使你可以为其他属性指定更高级的行为:

    var Bike =restmod.model('/bikes').mix({
     createdAt: { ignore:'CU' }, // won't send on Create or Update viewCount: { ignore:'R' }, // won't load on Read (fetch) opened: { ignore:true }, // will ignore in every request and response});

    默认值

    你可以为属性定义默认值,static 和动态值。 使用一个函数来定义动态默认值,该函数将在记录创建时被调用。

    var Bike =restmod.model('/bikes').mix({
     wheels: { init:2 }, // every new bike will have 2 wheels by default createdAt: { init:function() {
     returnnewDate();
     }}
    });

    显式属性映射

    可以显式告诉restmod将给定的服务器属性映射到模型的某个属性:

    var Bike =restmod.model('/bikes').mix({
     created: { map:'stats.created_at' }
    });

    可变属性

    可以定义volatile属性,在发送到服务器后从记录实例中删除volatile属性,这对密码之类的事情很有用。

    var User =restmod.model('/users').mix({
     password: { volatile:true } // make password volatile});

    嵌套属性

    有时需要指定嵌套属性的行为,这是使用 . 符号与常规属性相同的方式进行的。

    给出以下json响应:

    {
     "id": 1,
     "serialNo": {
     "issued": "2014-05-05" }
    }

    可以使用以下方法为 issued 属性添加日期解码器:

    var Bike =restmod.model('/bikes').mix({
     'serialNo.issued': { decode:'date_parse' }
    });

    如果嵌套属性位于 array 内部,则可以使用 [] 符号对它的进行reffer处理。

    因此,如果json响应如下所示:

    {
     "id": 1,
     "tags": [
     { "name": "endurow", "weight": 20 },
     { "name": "offroad", "weight": 5 }
     ]
    }

    可以使用以下方法将 weight 属性的映射添加到 size 属性中:

    var Bike =restmod.model('/bikes').mix({
     'tags[].size': { map:'weight' }
    });

    自定义方法

    restmod对象由三个主要API组成,即模型 static。记录API和集合 API。

    可以使用对象定义中的$extend 块来扩展这些api中的每个 api:

    例如下面的命令将 pedal 方法添加到每个记录。

    var Bike =restmod.model('/bikes').mix({
     $extend: {
     Record: {
     pedal:function() {
     this.strokes+=1;
     }
     }
     }
    });

    尽管 $extend 块是扩展模型的首选方法,但对于小型模型,也可以在定义对象中直接定义该方法:

    var Bike =restmod.model('/bikes').mix({
     'Record.pedal':function() {
     this.strokes+=1;
     }
    });

    在最后一个例子中,'唱片。'前缀可以省略,因为默认方法被添加到记录api中。

    以下是可以用于扩展的api:

    • 模型:static api。
    • 记录:模型实例 api。
    • 集合:模型集实例 api。
    • 范围:与扩展模型和集合相同
    • 资源:与扩展记录and+集合相同
    • 清单:由任何记录列表实现的特殊 api,包括集合

    因此,要添加 static 方法,我们将使用:

    var Bike =restmod.model('/bikes').mix({
     $extend: {
     Model: {
     $searchByTeam:function(_team) {
     returnthis.$search({ team: _team });
     }
     }
     }
    });

    重写现有方法,引用重写的函数使用 this.$super 也是 posible:

    var Bike =restmod.model('/bikes').mix({
     $extend: {
     Scope: {
     $search:function(_params) {
     returnthis.$super(angular.extend({ time:SomeService.currentTime() }, _params);
     }
     }
     }
    });

    自定义方法和列表

    为集合和列表提供了 List 命名空间,这可以创建可以链接的列表方法。

    例如,假设你需要能够筛选一个记录集合,然后使用结果列表进行某些操作:

    var Part =restmod.model('/parts').mix({
     $extend: {
     List: {
     filterByCategory:function(_category) {
     returnthis.$asList(function(_parts) {
     return_.filter(_parts, function(_part) {
     return_part.category== _category;
     });
     });
     },
     filterByBrand:function(_brand) {
     returnthis.$asList(function(_parts) {
     return_.filter(_parts, function(_part) {
     return_part.brand== _brand;
     });
     });
     },
     getTotalWeight:function(_category) {
     return_.reduce(this, function(sum, _part) {
     return sum +_part.weight;
     };
     }
     }
     }
    });

    现在,由于 List 方法由两个集合和列表共享,你可以执行以下操作:

    Part.$search().filterByCategory('wheels').filterByBrand('SRAM').$then(function() {
     // use $then because the $asList method will honor promises.scope.weight=this.getTotalWeight();
    });

    钩子( 回调)

    就像使用ActiveRecord一样,你可以在对象生命周期的某些步骤上添加钩子,在对象定义的$hooks 块中添加钩子。

    var Bike =restmod.model('/bikes').mix({
     $hooks: {
     'before-save':function() {
     this.partCount=this.parts.length;
     }
     }
    });

    请注意,可以为类型。集合或者记录定义钩子。 此外,还可以使用 $decorate. 为给定的执行上下文定义钩子 检查钩子高级文档。

    mixin

    为了简化模型的定义,使事情保持干燥,Restmod为你提供了混合能力。 例如假设你已经将车辆模型定义为工厂:

    Angular.factory('Vehicle', function() {
     returnrestmod.model('/vehicle').mix({
     createdAt: {encode:'date', param:'yyyy-MM-dd'}
     });
    })

    然后你可以定义从车辆模型继承的自行车模型,并设置其他功能。

    var Bike =restmod.model('/bikes').mix('Vehicle', {
     pedal:function (){
     alert('pedaling')
     }
    });

    某些链接:

    REST API 设计指南:https://github.com/interagent/http-api-design REST json api标准: http://jsonapi.org

    • fork
    • 创建你的特征分支( git checkout -b my-new-feature )
    • 提交你的更改( git commit -am 'Add some feature' )
    • 推送到分支( git push origin my-new-feature )
    • 创建新的拉请求

    查看贡献者的详细信息

    Credits

    感谢你的贡献者。

    Platanus

    角度restmod由 platanus 维护。

    许可证

    Angular Restmod是 © 2015 platanus,spa。 它是免费软件,可以根据许可文件中指定的条款重新发布。


    API  angular  REST  rails  ORM  
    相关文章