使用CouchDB和Angular跟踪费用

在本教程中,我们将使用CouchDB作为后端并选择Angular作为前端技术来构建应用程序。 CouchDB是NoSQL数据库,而Angular是更新的JavaScript MVC框架之一。 令人兴奋和令人敬畏的是,CouchDB是具有HTTP API的数据库–我们的客户端应用程序将直接与该数据库通信:CouchDB将充当我们客户端应用程序所需的唯一后端!

我们将专注于一个小型应用程序来跟踪我们的费用。 每个步骤都会有一个提交,有时该提交还包括测试。 测试不是本教程中的主题,但是如果您对此感兴趣,则应该看看! 您将在GitHub的存储库中找到本教程中使用的全部代码。

为什么选择CouchDB?

你们中有些人可能会说我们可以使用客户端替代方法。 IndexedDB或Local Storage是在客户端本地工作以持久化数据的技术。 但是使用数据库服务器有几个优点:我们可以将许多客户端连接到我们的应用程序。 当您独自在另一个超市中时,您的伴侣可以更新费用清单,这也会增加费用。

使用CouchDB有很多优点:CouchDB本机“说” HTTP,因此我们在数据库和应用程序之间不需要其他层。 我们的JavaScript应用程序可以使用CouchDB提供的RESTful接口直接与CouchDB数据库对话!

而且,如果我们想对数据库使用复制,那就像切面包一样容易:因为CouchDB是为创建分布式数据库系统而设计的。

要求

对于本教程,您将需要安装最新版本的CouchDB(1.6)和最新的稳定Node.js(当前为0.10.x)版本。

安装Node.js和Yo

作为Mac用户,您可以在Node主页上获得官方安装程序。 在Linux和OSX上管理Node.js安装的另一种方法是Tim Caswell的出色nvm 。

我们将安装Yo来搭建我们的应用程序。 在创建骨骼的过程中,您会问我们一些问题。 Yo询问我们是否要使用SASS,如果不确定是否要回答“否”,但我们肯定要包括Bootstrap和预选的Angular-Modules。

在我们的外壳中,键入:

npm install -g yo generator-angular grunt-cli couchapp
mkdir expenses && cd expenses
yo angular expenses

作为我们脚手架的一部分,Yo为我们创建了一个Gruntfile(Gruntfile.js)。 Grunt是JavaScript的任务执行者,它具有许多已经编写的插件,可以自动执行任务并使您的生活更轻松。

使用grunt serve命令启动开发服务器,并在grunt任务完成后在浏览器中打开http://127.0.0.1:9000 。 下图显示了一个示例。

脚手架

安装CouchDB

有很棒的文档可以在很多平台上安装CouchDB –有适用于所有主要操作系统的软件包,在OSX上,您可以使用brew安装CouchDB。

CouchDB的第一步

让我们开始第一个CouchDB实例并创建一个数据库:

couchdb & # start a CouchDB
curl -X PUT http://127.0.0.1:5984/expenses # create the database expenses

CouchDB回答:

{"ok":true}

我们刚刚使用HTTP创建了第一个数据库!

让我们进一步探讨CouchDB的HTTP API:现在我们可以插入第一个文档,假设我们要跟踪购买的爆米花(稍后我们的应用程序需要对CouchDB的这些调用)。

curl -X POST http://127.0.0.1:5984/expenses -H "Content-Type: application/json" -d '{"name": "Popcorn", "price": "0.99"}'

CouchDB的答案:

{"ok":true,"id":"39414de82e814b6e1ca754c61b000efe","rev":"1-2b0a863dc254239204aa5b132fda8f58"}``

现在,我们可以使用GET请求和CouchDB分配给我们文档的ID来访问文档,因为我们没有提供特定的ID:

curl -X GET http://127.0.0.1:5984/expenses/39414de82e814b6e1ca754c61b000efe

CouchDB的答案:

{"_id":"39414de82e814b6e1ca754c61b000efe","_rev":"1-2b0a863dc254239204aa5b132fda8f58","name":"Popcorn","price":"0.99"}

之后,我们插入另一个文档:

curl -X POST http://127.0.0.1:5984/expenses -H "Content-Type: application/json" -d '{"name": "Washing powder", "price": "2.99"}'

配置:带有CouchDB的CORS

我们的客户将通过HTTP从CouchDB本身以外的其他位置进行通信。 为了使此功能在我们的浏览器中起作用,我们必须在CouchDB中启用CORS(跨域资源共享)。

在这种情况下,我们要为本地自定义更改修改local.ini 。 可以通过HTTP修改配置。 在https部分中,我们启用CORS,然后使用通配符配置源:

curl -X PUT http://localhost:5984/_config/httpd/enable_cors -d '"true"'
curl -X PUT http://localhost:5984/_config/cors/origins -d '"*"'

使用这两个命令,我们正在更改CouchDB的local.ini 。 您可以使用couchdb -c查找local.ini的位置。

重要! 请注意,如果您将应用程序部署到生产环境中,则可能需要更改“来源”部分。 此处提供的所有设置仅用于开发!

角度和依赖注入

app/scripts/app.js我们将找到应用程序的主要JavaScript文件,实际上这就是所谓的Angular模块。 此模块将其他一些模块作为依赖项加载(例如ngCookies )。 在此文件中,我们还使用$routeprovider找到了应用程序的客户端路由。

该文件中的$routeprovider是Angular依赖项注入(DI)的一个很好的例子。 通过定义您要使用的服务的名称,Angular将其注入给定的功能范围。 您可以在docs中找到有关Angular依赖注入的更多信息。

因为我们希望在一个中央位置拥有连接到CouchDB所需的数据,所以让我们尝试使用具有常数的DI。 我们使用链接将它们添加到我们的模块中:

.constant('appSettings', {db: 'http://127.0.0.1:5984/expenses'
});

我们迄今为止唯一控制器,它在初始支架期间创建,是MainCtrl位于app/scripts/controllers/main.jsMainCtrl被定义并且$scope被注入。 稍后我们将看到如何使用范围。

现在我们可以将appSettings添加到函数参数中以注入它们,就像我们之前使用$routeprovider看到的$routeprovider

.controller('MainCtrl', function ($scope, appSettings) {console.log(appSettings);
});

现在,您应该能够在浏览器的调试控制台上记录输出。 恭喜你! 您已成功使用依赖项注入。 您可以在以下位置找到完整的提交: https : //github.com/robertkowalski/couchdb-workshop/commit/d6b635a182df78bc22a2e93af86162f479d8b351 。

取得结果

在下一步中,我们将注入$http服务以从CouchDB中获取数据并更新视图。 当传统数据库使用分解为表的数据时,CouchDB使用的是非结构化文档,可以使用地图和归约函数(称为视图)来聚合,过滤和合并非结构化文档。 视图由设计文档(一种特殊的文档)定义。

您可以自行编写视图,然后通过curl将其发送到CouchDB,使用位于http://localhost:5984/_utils的图形界面或通过诸如CouchApp之类的工具–有许多诸如CouchApp之类的工具( npm install -g couchapp ),以npm install -g couchapp视图的开发和部署。

这是我们的视图:

{"_id":"_design/expenses","views": {"byName": {"map": "function (doc) {emit(doc.name, doc.price);}"}}
}

_id对我们很重要,因为它定义了以后查询视图的路径。 在创建设计文档时, _id属性以_design为前缀。 我们将视图byName ,它仅包含一个基本的map函数,该函数将发出数据库中每个文档的name属性作为键,并将price作为值。

让我们使用curl将其发送到CouchDB:

curl -X POST http://127.0.0.1:5984/expenses -H "Content-Type: application/json" -d '{"_id":"_design/expenses","views": {"byName": {"map": "function (doc) {emit(doc.name, doc.price);}"}}}'

CouchDB响应:

{"ok":true,"id":"_design/expenses","rev":"1-71127e7155cf2f780cae2f9fff1ef3bc"}

现在,我们可以在以下位置查询:

http://localhost:5984/expenses/_design/expenses/_view/byName

如果您对诸如CouchApp之类的工具感兴趣(提示:您稍后必须使用它),那么这里的提交显示了如何使用它(使用npm run bootstrap部署设计文档)。

您还记得起初的卷曲要求吗? 现在,我们将用JavaScript实现它们。 Angular提供$http服务,可以如下所示进行注入:

.controller('MainCtrl', function ($scope, $http, appSettings) {

然后,我们添加一个函数以使用$http服务获取项目:

function getItems () {$http.get(appSettings.db + '/_design/expenses/_view/byName').success(function (data) {$scope.items = data.rows;});
}
getItems();

$http服务返回一个Promise,它将从CouchDB视图提供给我们JSON数据。 我们正在将数据添加到$scope.items 。 使用$scope我们可以在视图中设置和更新值。 如果模型上的值发生更改,则视图将自动更新。 Angular的双向绑定使视图和模型之间的数据同步。 控制器更改模型后,它将立即更新视图,并且当视图中的值更改时,还将更新模型。

删除大部分样板标记后,让我们添加一些带有表达式的HTML来在app/views/main.html显示我们的项目:

{{ item[0].key }}
{{ item[0].value }}

我们将在“使用CouchDB的第一步”部分中看到添加的第一项:

应用程式检视

该部分的提交可在GitHub上获得。

使用指令: ng-repeat

现在,我们应该看到第一个项目,但是其他所有项目呢?

我们可以在此处使用ng-repeat指令,它将为我们从更长的列表中构建标记。 通常,我们可以说Angular中的指令将行为附加到DOM元素。 Angular中还有许多其他预定义的指令,您也可以定义自己的指令。 在这种情况下,我们将ng-repeat="item in items"到外部div ,然后它将从$scope.items迭代数组items

pull-leftpull-right是Bootstrap CSS的一部分,并为我们提供了浮动元素。 由于元素是浮动的,因此我们正在应用一个clearfix ,它也包含在Bootstrap中:

{{ item.key }}{{ item.value }}

如果刷新页面,则项目将在DOM检查器中呈现为:


Popcorn0.99


Washing powder2.99

我们现在有一个不错的小清单,但是除了使用curl之外,还没有办法通过我们的应用程序提交新项目。 到目前为止, 此提交中的应用程序可用,如下图所示。

列表显示

创建用于提交项目的表格

我们将添加一个包含两个输入的表单:一个输入商品名称,另一个输入价格。 该表格还具有一个用于提交我们的物品的按钮。

Bootstrap中带有class="row"div用于以响应方式对我们的应用程序进行样式设置。 Bootstrap类(如form-controlbtn btn-primary用于设置按钮和输入的样式。

表单还获得了novalidate属性:它禁用了浏览器的本机表单验证,因此我们稍后可以使用Angular来验证表单:

Save

表单的提交位于https://github.com/robertkowalski/couchdb-workshop/commit/d678c51dfff16210f1cd8843fbe55c97dc25a408 。

在CouchDB中保存数据

使用ng-model我们可以观察并访问控制器中输入的值,然后将它们发送到CouchDB。 对于我们的价格输入,我们将添加属性ng-model="price"

名称的输入将获得ng-model="name"属性。 看起来像这样:

我们还在最后一项下面添加了一个小状态框。 我们将需要它来显示错误。

{{ status }}

现在,我们可以使用$scope.price$scope.name访问控制器中的值。 范围将视图连接到我们的控制器。 看一下Model-View-Controller(MVC)模式,范围就是我们的模型。 Angular有时也称为MVVM(模型-视图-视图-模型)框架-所有这些JavaScript MVC框架通常被称为MVW(模型-视图-任何模型),因为它们之间有很多细微的差异。

但是我们如何提交表格?

发送表单的常用方法是在$scope上定义一个函数,并在视图中结合ng-submit指令。 我们的函数将构建要发送到CouchDB的JSON。 创建JSON之后, processForm将调用postItem ,它将把JSON发送到CouchDB:

$scope.processForm = function () {var item = {name: $scope.name,price: $scope.price};postItem(item);
};
function postItem (item) {// optimistic ui update$scope.items.push({key: $scope.name, value: $scope.price});// send post request$http.post(appSettings.db, item).success(function () {$scope.status = '';}).error(function (res) {$scope.status = 'Error: ' + res.reason;// refetch items from servergetItems();});
}

我们的函数postItem发生了很多事情:

在将HTTP请求发送到数据库之前,我们正在对用户界面进行乐观更新,因此用户可以立即看到更新,并且我们的应用程序感觉更加敏捷。 为此,我们将项目添加到合并范围内的其他项目。 Angular将为我们更新视图。

然后,我们在后台对商品进行POST请求,成功后,我们将从状态字段中删除所有(先前的)错误消息。

如果发生错误,我们正在向视图中写入错误消息。 CouchDB将告诉我们为什么在返回的JSON的reason属性中发生错误。 为了再次获得一致的视图,我们在收到错误后重新获取项目列表。

现在,我们可以在表单上添加指令ng-submit ,当提交表单时,它将在作用域上调用我们的函数:

就是这样! Angular帮助我们保持了最新观点! 查看最新提交 。

添加验证

您可能已经注意到,我们可以在费用应用程序中放入各种值。 人们可以在价格中添加诸如foo之类的无效字符串,然后将其发送到服务器。 因此,让我们添加一些服务器端验证:CouchDB能够在更新时验证文档。 我们只需要在设计文档中添加一个带有函数的validate_doc_update字段即可。 如果数据无效,则此函数应引发异常。

该函数具有四个参数,如下所示:

validate_doc_update: function (newDoc, oldDoc, userCtx, secObj) {// ...
}

newDoc是将被创建或用于更新的文档。 还有参数oldDocuserCtxsecObj用于更复杂的验证,但是我们将仅使用newDoc进行验证:

如果您还没有使用已经提到的CouchApp,我真的建议您现在使用,因为它使处理更大的设计文档变得更加容易。 这是CouchApp的设计文档:

var ddoc = {_id: '_design/expenses',views: {},lists: {},shows: {},validate_doc_update: function (newDoc, oldDoc, userCtx, secObj) {if (newDoc._deleted === true) {return;}if (!newDoc.name) {throw({forbidden: 'Document must have an item name.'});}if (!newDoc.price) {throw({forbidden: 'Document must have a price.'});}if (!/\d+\.\d\d/.test(newDoc.price)) {throw({forbidden: 'Price must be a number and have two decimal places after a dot.'});}}
};// _design/expenses/_view/byName
ddoc.views.byName = {map: function (doc) {emit(doc.name, doc.price);}
};module.exports = ddoc;

在我们的验证中不能undefined字段nameprice 。 此外,我们正在使用正则表达式测试价格格式。 如果我们只想删除文档,则不需要任何验证。 我们正在使用以下命令更新设计文档:

couchapp push couchdb/views.js http://localhost:5984/expenses

现在,当我们尝试保存无效值时,应该看到错误,如下图所示:

错误处理

这是相关的提交 。

向前端添加验证

我们现在在服务器上进行了一些验证真是太棒了,但是如果我们不需要请求来验证我们的文档,那岂不是更棒了吗? 让我们使用Angular添加一些验证。

我们的两个输入都是必需的,因此它们都具有required属性。 您还记得设计文档的validate函数中的正则表达式吗? 指令ng-pattern使用正则表达式检查我们的输入:

使用name-of-the-form.$invalid我们可以测试输入之一是否无效。 由于我们的表单具有名称-属性form我们将使用form.$invalid 。 我们可以将此值与ng-disabled类的指令结合使用,如果表单的值无效或缺失,它将禁用我们的提交按钮:

Save

而已! 仅用几行HTML,我们就获得了很好的验证。 查看最新的提交 ,包括测试。

结论

我们已经学习了如何使用CouchDB和Angular构建一个小型应用程序。 Angular和CouchDB为我们做了很多繁重的工作。 我们看了看:

  • CouchDB HTTP接口
  • CouchDB视图和验证
  • Angular的依赖注入
  • Angular的双向数据绑定
  • 角度指令
  • 在Angular中使用验证

Angular和CouchDB是很好的开发工具,它们在帮助我们正常工作的应用程序方面大有帮助。 希望您对CouchDB和Angular有一个初步的了解,并且如果您感兴趣的话,您仍然可以查看许多主题:

  • 在CouchDB本身上托管应用程序
  • 更新文件
  • 编写自己的指令
  • 复写
  • 在我们看来使用reduce函数
  • 测试Angular应用

From: https://www.sitepoint.com/tracking-expenses-couchdb-angular/


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部