Skip to content

Latest commit

 

History

History
190 lines (136 loc) · 6.35 KB

10.md

File metadata and controls

190 lines (136 loc) · 6.35 KB

十、Node Express 后端集成

在本章中,我们将探讨将 Angular.js 与 Node.js Express 框架相结合时常见问题的解决方法。本章中使用的示例基于联系人应用程序来管理联系人列表。另外,我们使用 MongoDB 作为我们联系人的后端,因为它需要进一步定制,以便与 Angular 的$resource服务结合使用。

使用休息原料药

问题

您希望使用在您的 Express 应用程序中实现的 JSON REST API。

溶液

使用$resource服务,我们将从定义我们的联系人模型和所有 RESTful 操作开始:

    app.factory("Contact", function($resource) {
    return $resource("/api/contacts/:id", { id: "@_id" },
    {
    'create': { method: 'POST' },
    'index': { method: 'GET', isArray: true },
    'show': { method: 'GET', isArray: false },
    'update': { method: 'PUT' },
    'destroy': { method: 'DELETE' }
    }
    );
    });

我们现在可以使用Contact.index()获取联系人列表,使用Contact.show(id)获取单个联系人。这些动作可以直接映射到app.js中定义的 API 路线:

    var express = require('express'),
    api = require('./routes/api');

    var app = module.exports = express();

    app.get('/api/contacts', api.contacts);
    app.get('/api/contacts/:id', api.contact);
    app.post('/api/contacts', api.createContact);
    app.put('/api/contacts/:id', api.updateContact);
    app.delete('/api/contacts/:id', api.destroyContact);

我喜欢将路线保存在一个单独的文件(routes/api.js)中,并在app.js中引用它们,以保持其较小。应用编程接口实现首先初始化猫鼬库,并为我们的联系人模型定义一个模式:

    var mongoose = require('mongoose');
    mongoose.connect('mongodb://localhost/contacts_database');

    var contactSchema = mongoose.Schema({
    firstname: 'string', lastname: 'string', age: 'number'
    });
    var Contact = mongoose.model('Contact', contactSchema);

我们现在可以使用Contact模型来实现 API。让我们从索引操作开始:

    exports.contacts = function(req, res) {
    Contact.find({}, function(err, obj) {
    res.json(obj)
    });
    };

跳过错误处理,我们用 Mongoose 提供的find函数检索所有联系人,并以 JSON 格式呈现结果。show 动作非常相似,只是它使用 URL 参数中的findOneid来检索单个联系人。

    exports.contact = function(req, res) {
    Contact.findOne({ _id: req.params.id }, function(err, obj) {
    res.json(obj);
    });
    };

作为最后一个例子,我们将创建一个传入请求体的新的 Contact 实例,并调用save方法来保持它:

    exports.createContact = function(req, res) {
    var contact = new Contact(req.body);
    contact.save();
    res.json(req.body);
    };

你可以在 GitHub 上找到完整的例子。

讨论

让我们再来看看 contact 函数的例子,它检索一个 Contact。它使用_id而不是id作为findOne函数的参数。这个下划线是有意的,被 MongoDB 用于其自动生成的标识。为了自动从id映射到_id参数,我们使用了$resource服务的一个好技巧。看一下触点$resource定义的第二个参数:{ id: "@_id" }。使用该参数,Angular 将根据模型属性_id的值自动设置 URL 参数id

实现客户端路由

问题

您希望将客户端路由与快速后端结合使用。

溶液

对后端的每个请求最初都应该呈现完整的布局,以便加载我们的 Angular 应用程序。只有到那时,客户端渲染才会接管。让我们先来看看app.js中这条“包罗万象”路线的路线定义:

    var express = require('express'),
    routes = require('./routes');

    app.get('/', routes.index);
    app.get('*', routes.index);

它使用通配符来捕获所有请求,以便通过routes.index模块进行处理。此外,它还定义了使用同一模块的路线。该模块再次驻留在routes/index.js中:

    exports.index = function(req, res){
    res.render('layout');
    };

该实现仅呈现布局模板。它使用翡翠模板引擎:

    !!!
    html(ng-app="myApp")
    head
    meta(charset='utf8')
    title Angular Express Seed App
    link(rel='stylesheet', href='/css/bootstrap.css')
    body
    div
    ng-view

    script(src='js/lib/angular/angular.js')
    script(src='js/lib/angular/angular-resource.js')
    script(src='js/app.js')
    script(src='js/services.js')
    script(src='js/controllers.js')

现在我们可以实际呈现初始布局,我们可以从app.js中的客户端路由定义开始:

    var app = angular.module('myApp', ["ngResource"]).
    config(['$routeProvider', '$locationProvider',
    function($routeProvider, $locationProvider) {
    $locationProvider.html5Mode(true);
    $routeProvider
    .when("/contacts", {
    templateUrl: "partials/index.jade",
    controller: "ContactsIndexCtrl" })
    .when("/contacts/new", {
    templateUrl: "partials/edit.jade",
    controller: "ContactsEditCtrl" })
    .when("/contacts/:id", {
    templateUrl: "partials/show.jade",
    controller: "ContactsShowCtrl" })
    .when("/contacts/:id/edit", {
    templateUrl: "partials/edit.jade",
    controller: "ContactsEditCtrl" })
    .otherwise({ redirectTo: "/contacts" });
    }
    ]
    );

我们定义路由定义来列出、显示和编辑联系人,并使用一组分支和相应的控制器。为了正确加载这些部分,我们需要在后端添加另一条快速路由,为所有这些部分提供服务:

    app.get('/partials/:name', function (req, res) {
    var name = req.params.name;
    res.render('partials/' + name);
    });

它使用部分的名称作为 URL 参数,并使用来自partial目录的给定名称呈现部分。请记住,您必须在捕获路线之前定义该路线,否则它将不起作用。

你可以在 GitHub 上找到完整的例子。

讨论

与 Rails 相比,通过为部分定义路由,对部分的处理是显式的。另一方面,能够为我们的部分使用 Jade 模板也是相当不错的。