Maison > Article > interface Web > Créez une application de gestion de tâches à l'aide de Node.js et Geddy
Dans ce didacticiel en trois parties, nous examinerons en profondeur comment créer une application de gestion de liste de tâches à l'aide de Node.js et Geddy. Il s'agit de la deuxième partie de cette série dans laquelle nous allons créer une application simple de gestion de listes de tâches.
Pour récapituler rapidement, la dernière fois que nous avons installé Node et Geddy, généré une nouvelle application et appris à démarrer un serveur. Dans ce didacticiel, nous nous baserons sur ce que nous avons fait la dernière fois. Assurez-vous donc d'avoir terminé ce didacticiel avant de continuer.
Geddy dispose d'un générateur de ressources intégré ; cela nous permettra de générer automatiquement des modèles, des contrôleurs, des vues et des itinéraires pour des ressources spécifiques. Notre application de liste de tâches n'a qu'une seule ressource : todo
。要生成它,只需将 cd
放入应用程序的目录(cd path/to/your/todo_app
) et exécute :
geddy resource todo
Vous devriez maintenant avoir ces fichiers ajoutés à votre application :
Votre config/router.js
devrait également avoir ce qui suit en annexe :
router.resource('todos');
Si vous êtes nouveau sur MVC, tout cela pourrait être un peu intimidant pour vous. Mais ne vous inquiétez pas, c'est assez simple une fois que vous avez compris.
models/todo.js : Ce fichier est l'endroit où nous définissons de nombreuses propriétés que todo
模型的地方。我们将定义所有 todo
possède. Nous allons également écrire ici une validation des données.
controllers/todos.js : ce fichier est l'emplacement final de toutes les /todos/
routes. Chaque action dans ce contrôleur a un itinéraire correspondant :
GET /todos/ => index POST /todos/ => create GET /todos/:id => show PUT /todos/:id => update DELETE /todos/:id => remove GET /todos/:id/add => add GET /todos/:id/edit => edit
views/todos/ : Chaque fichier ici correspond à l'un des GET
itinéraires que nous vous avons montré ci-dessus. Ce sont les modèles que nous utilisons pour générer le front-end de notre application. Geddy utilise EJS (Embedded JavaScript) comme langage de création de modèles. Si vous avez déjà utilisé PHP ou ERB, cela devrait vous sembler familier. Fondamentalement, vous pouvez utiliser n'importe quel JavaScript de votre choix dans votre modèle.
Maintenant que nous avons généré un tas de code, vérifions que nous disposons de toutes les routes dont nous avons besoin. Lancez à nouveau l'application (geddy
) et pointez votre navigateur sur http://localhost:4000/todos. Vous devriez voir quelque chose comme ça
Continuez à essayer d'autres GET
itinéraires :
Est-ce que tout va bien ? Bon, continuons.
Dans Geddy (et la plupart des autres frameworks MVC), vous utilisez des modèles pour définir les types de données que votre application utilisera. Nous venons de générer un modèle pour todo
s, alors voyons ce que cela nous donne :
var Todo = function () { // Some commented out code }; // Some more commented out code Todo = geddy.model.register('Todo', Todo);
Les modèles de Geddy sont très simples. Nous créons simplement un nouveau constructeur pour todo
s 创建一个新的构造函数,并将其注册为 geddy 中的模型。让我们为 todo
s et l'enregistrons comme modèle dans geddy. Définissons quelques propriétés pour les
var Todo = function () { this.defineProperties({ title: {type: 'string', required: true} , id: {type: 'string', required: true} , status: {type: 'string', required: true} }); };
todo
s 将有一个标题、一个 id 和一个状态,这三个都是必需的。现在让我们为 todo
Nos auront un titre, un identifiant et un statut, les trois sont obligatoires. Maintenant, mettons en place une validation pour les . open
或 done
var Todo = function () { this.defineProperties({ title: {type: 'string', required: true} , id: {type: 'string', required: true} , status: {type: 'string', required: true} }); this.validatesPresent('title'); this.validatesLength('title', {min: 5}); this.validatesWithFunction('status', function (status) { return status == 'open' || status == 'done'; }); };Nous vérifions que le titre existe, la longueur minimale du titre est de 5 caractères, et nous utilisons une fonction pour vérifier que le statut est
Créer un adaptateur de modèle Todogeddy
Modifiez votre fichier init.jsconfig/init.js
// Add uncaught-exception handler in prod-like environments if (geddy.config.environment != 'development') { process.addListener('uncaughtException', function (err) { geddy.log.error(JSON.stringify(err)); }); }
在该代码块之后,让我们将数组挂在 geddy
全局上:
geddy.todos = [];
现在我们有了一个地方来存储 todo
s。请记住,它位于您的应用程序内存中,因此当您重新启动服务器时它将消失。
模型适配器提供模型所需的基本 save
、remove
、load
和 all
方法。我们的数据源非常简单(只是一个数组!),因此编写模型适配器也应该非常简单。
在 lib
中创建一个名为 model_adapters
的目录,并在 lib/model_adapters
中创建一个名为 todo.js
的文件。让我们打开该文件并添加一些样板代码:
var Todo = new (function () { })(); exports.Todo = Todo;
我们在这里所做的就是设置一个新的空白对象,将其导出到最终需要此文件的任何地方。如果您想更多地了解 Node 的 require 方法是如何工作的,这篇文章有一个很好的概述。在这种情况下,我们的 init.js
文件将满足要求。
因此我们设置了一个新的 Todo 模型适配器对象。现在还很贫瘠,但我们很快就会做到这一点。现在,我们必须返回 init.js 并添加一些代码,以便在启动时将其加载到我们的应用程序中。在 config/init.js
中的 geddy.todos = [];
之后添加这两行:
geddy.model.adapter = {}; geddy.model.adapter.Todo = require(process.cwd() + '/lib/model_adapters/todo').Todo;
我们创建了一个空白的模型适配器对象,并向其添加了 Todo 模型适配器。
现在我们已经有了模型和模型适配器,我们可以开始应用程序逻辑了。让我们首先将待办事项添加到我们的待办事项列表中。
处理数据时,首先应该去的地方是模型适配器。我们需要能够将 Todo 模型的实例保存到 geddy.todos 数组中。因此,打开 lib/model_adapters/todo.js
并添加保存方法:
var Todo = new (function () { this.save = function (todo, opts, callback) { if (typeof callback != 'function') { callback = function(){}; } todo.saved = true; geddy.todos.push(todo); return callback(null, todo); } })();
我们所要做的就是将实例的保存属性设置为 true 并将项目推送到 geddy.todos 数组中。在 Node 中,最好以非阻塞方式执行所有 I/O,因此养成使用回调传递数据的习惯是个好主意。对于本教程来说,这并不重要,但稍后当我们开始持久化事物时,它会派上用场。您会注意到我们确保回调是一个函数。如果我们不这样做并在没有回调的情况下使用 save,我们会收到错误。现在让我们继续控制器创建操作。
继续看一下 app/controllers/todos.js
中的 create
操作:
this.create = function (req, resp, params) { // Save the resource, then display index page this.redirect({controller: this.name}); };
很简单,对吧?盖迪已经帮你把它记下来了。那么我们来稍微修改一下:
this.create = function (req, resp, params) { var self = this , todo = geddy.model.Todo.create({ title: params.title , id: geddy.string.uuid(10) , status: 'open' }); todo.save(function (err, data) { if (err) { params.errors = err; self.transfer('add'); } else { self.redirect({controller: self.name}); } }); };
首先,我们使用 geddy.model.Todo.create
创建一个 Todo 模型的新实例,传入表单将发布给我们的标题,并设置 id 和状态的默认值。
然后我们调用在模型适配器上创建的 save 方法并将用户重定向回 /todos 路由。如果它没有通过验证,或者我们收到错误,我们使用控制器的 transfer
方法将请求传输回 add
操作。
现在是我们设置添加模板的时候了。看一下 app/views/todos/add.html.ejs
,它应该看起来像这样:
<div class="hero-unit"> <h3>Params</h3> <ul> <% for (var p in params) { %> <li><%= p + ': ' + params[p]; %></li> <% } %> </ul> </div>
我们不需要
<ul></ul>
对于我们的用例来说,所以让我们暂时摆脱它。让你的 add.html.ejs
看起来像这样:
<div class="hero-unit"> <%= partial('_form', {params: params}); %> </div>
Partials 为您提供了一种在模板之间共享代码的简单方法。
您会注意到我们在此模板中使用了部分内容。部分为您提供了一种在模板之间共享代码的简单方法。我们的添加和编辑模板都将使用相同的表单,所以现在让我们创建这个表单部分。在 views/todos/
目录中创建一个名为 _form.html.ejs
的新文件。我们使用下划线来轻松判断该模板是否是部分模板。打开它并添加以下代码:
<% var isUpdate = params.action == 'edit' , formTitle = isUpdate ? 'Update this To Do Item' : 'Create a new To Do Item' , action = isUpdate ? '/todos/' + todo.id + '?_method=PUT' : '/todos' , deleteAction = isUpdate ? '/todos/' + todo.id + '?_method=DELETE' : '' , btnText = isUpdate ? 'Update' : 'Add' , doneStatus = isUpdate ? 'checked' : '' , titleValue = isUpdate ? todo.title : '' , errors = params.errors; %> <form id="todo-form" class="form-horizontal" action="<%= action %>" method="POST"> <fieldset> <legend><%= formTitle %></legend> <div class="control-group"> <label for="title" class="control-label">Title</label> <div class="controls"> <input type="text" class="span6" placeholder="enter title" name="title" value='<%= titleValue %>'/> <% if (errors) { %> <p> <% for (var p in errors) { %> <div><%= errors[p]; %></div> <% } %> </p> <% } %> </div> </div> <% if (isUpdate) { %> <div class="control-group"> <label for="status">Status</label> <div class="controls"> <select name="status"> <option>open</option> <option>done</option> </select> </div> </div> <% } %> <div class="form-actions"> <input type="submit" class="btn btn-primary" value="<%= btnText %>"/> <% if (isUpdate) { %> <button type="submit" formaction="<%= deleteAction %>" formmethod="POST" class="btn btn-danger">Remove</button> <% } %> </div> </fieldset> </form>
哇,那里有很多代码!让我们看看是否可以通过它。由于两个不同的模板将使用此部分,因此我们必须确保表单在两个模板中看起来都正确。大部分代码实际上是来自 Twitter Bootstrap 的样板。这就是让这个应用程序立即看起来如此出色的原因(在移动设备上也是如此!)。
为了使此应用程序看起来更好,您可以使用演示应用程序下载中提供的 CSS 文件。
我们做的第一件事是设置一些变量供我们使用。在 add
操作中,我们将 params
对象传递给 respond
方法调用中的模板。这给了我们一些信息——它告诉我们这个请求被路由到哪个控制器和操作,并给我们提供了在 url 中传递的任何查询参数。我们设置 isUpdate
变量来查看当前是否正在进行更新操作,然后我们设置更多变量来帮助清理视图代码。
从那里,我们所做的就是制作一个表格。如果我们执行添加操作,我们只需按原样渲染表单即可。如果我们正在进行编辑操作,我们会填写表单以让用户更新字段。
请注意,该表单将使用 _method=PUT
参数向 /todos/
发送 POST
请求。 Geddy 使用标准方法覆盖参数,允许您从浏览器发送 PUT
和 DELETE
请求,而无需使用 JavaScript。 (至少在前端!)
我们需要看的最后一个小细节是“删除”按钮。我们使用 html5 的 formaction
属性来更改此表单的操作。您会注意到此按钮的 formaction
将 POST
请求发送到 /todos/:id
路由,并带有 _method=DELETE
参数。这将在控制器上执行 remove
操作,我们稍后会介绍。
重新启动服务器 (geddy
) 并访问 http://localhost:4000/todos/add 以查看正在运行的模板。创建一个待办事项。
现在我们已经将用户输入的待办事项添加到了 geddy.todos 数组中,我们可能应该将它们列出在某个地方。让我们从模型适配器中的 all
方法开始。
让我们再次打开 lib/model_adapters/todo.js
并在 save` 方法上方添加一个 all 方法:
this.all = function (callback) { callback(null, geddy.todos); }
这可能是我们今天要创建的最简单的模型适配器方法,它所做的就是接受回调并调用它并返回错误(目前始终为 null,我们将在下一个版本中升级此方法)教程)和 geddy.todos
。
再次打开 /app/controllers/todos.js
并查看 index
操作。它应该看起来像这样:
this.index = function (req, resp, params) { this.respond({params: params}); };
这部分非常简单,我们只需使用我们刚刚在模型适配器上定义的 all
方法来获取所有 todo
并渲染它们:
this.index = function (req, resp, params) { var self = this; geddy.model.adapter.Todo.all(function(err, todos){ self.respond({params: params, todos: todos}); }); };
这就是控制器的内容,现在进入视图。
看一下 /app/views/todos/index.html.ejs,它应该如下所示:
<div class="hero-unit"> <h3>Params</h3> <ul> <% for (var p in params) { %> <li><%= p + ': ' + params[p]; %></li> <% } %> </ul> </div>
看起来很像 add.html.ejs 模板,不是吗?同样,我们在这里不需要 params 样板,因此将其取出,并使您的 index.html.ejs 模板如下所示:
<div class="hero-unit"> <h2>To Do List</h2> <a href="/todos/add" class="btn pull-right">Create a new To Do</a></p> </div> <% if (todos && todos.length) { %> <% for (var i in todos) { %> <div class="row todo-item"> <div class="span8"><h3><a href="/todos/<%= todos[i].id; %>/edit"><%= todos[i].title; %></a></h3></div> <div class="span4"><h3><i class="icon-list-alt"></i><%= todos[i].status; %></h3></div> </div> <% } %> <% } %>
这个也非常简单,但是这次我们的模板中有一个循环。在标题中,我们添加了一个按钮来添加新的待办事项。在循环内,我们为每个 todo
生成一行,显示其标题(作为指向 edit
页面的链接)及其状态。
要查看它,请访问 http://localhost:4000/todos。
现在我们有了 edit
页面的链接,我们应该可以让它工作了!
再次打开模型适配器 (/lib/model_adapters/todo.js
)。我们将添加 load
方法,以便我们可以加载特定的 todo
并在我们的编辑页面中使用它。添加到哪里并不重要,但现在让我们将其放在 all
方法和 save
方法之间:
this.load = function (id, callback) { for (var i in geddy.todos) { if (geddy.todos[i].id == id) { return callback(null, geddy.todos[i]); } } callback({message: "To Do not found"}, null); };
此加载方法需要一个 id 和一个回调。它循环遍历 geddy.todos
中的项目,并检查当前项目的 id
是否与传入的 id
匹配。如果是,则调用回调,并将 todo
项传回。如果找不到匹配项,则会调用回调并返回错误。现在我们需要在 todos 控制器的 show 操作中使用此方法。
再次打开 todos
控制器并查看它的 edit
操作。它应该看起来像这样:
this.edit = function (req, resp, params) { this.respond({params: params}); };
让我们使用刚刚创建的加载方法:
this.edit = function (req, resp, params) { var self = this; geddy.model.Todo.load(params.id, function(err, todo){ self.respond({params: params, todo: todo}); }); };
我们在这里所做的就是加载待办事项并将其发送到要渲染的模板。那么让我们看一下模板。
打开/app/views/todos/edit.html.ejs
。我们再次不需要 params 样板,所以让我们删除它。让你的 edit.html.ejs
看起来像这样:
<div class="hero-unit"> <%= partial('_form', {params: params, todo: todo}); %> </div>
这应该与我们刚刚编辑的 add.html.ejs
文件非常相似。您会注意到,这次我们将 todo
对象发送到部分以及参数。很酷的是,由于我们已经编写了部分内容,因此这就是我们所要做的以使编辑页面正确显示。
重新启动服务器,创建一个新的 todo
并单击链接以查看其工作原理。现在让我们让更新按钮起作用!
再次打开模型适配器并找到 save
方法。我们将添加一些内容,以便我们可以保存现有的 todo
s。让它看起来像这样:
this.save = function (todo, opts, callback) { if (typeof callback != 'function') { callback = function(){}; } var todoErrors = null; for (var i in geddy.todos) { // if it's already there, save it if (geddy.todos[i].id == todo.id) { geddy.todos[i] = todo; todoErrors = geddy.model.Todo.create(todo).errors; return callback(todoErrors, todo); } } todo.saved = true; geddy.todos.push(todo); return callback(null, todo); }
这会循环 geddy.todos
中的所有待办事项,如果 id
已经存在,它将用新的 todo
实例替换 todo
。我们在这里做了一些事情,以确保我们的验证在更新和创建时都有效 - 为了做到这一点,我们必须从新模型实例中提取 errors
属性,并将其传递回回调中。如果它通过了验证,它只是未定义的,我们的代码将忽略它。如果没有通过,todoErrors
将是一个验证错误数组。
现在我们已经完成了,让我们来处理控制器的 update
操作。
继续再次打开控制器并找到“更新”操作,它应该如下所示:
this.update = function (req, resp, params) { // Save the resource, then display the item page this.redirect({controller: this.name, id: params.id}); };
您需要对其进行编辑,使其看起来像这样:
this.update = function (req, resp, params) { var self = this; geddy.model.adapter.Todo.load(params.id, function (err, todo) { todo.status = params.status; todo.title = params.title; todo.save(function (err, data) { if (err) { params.errors = err; self.transfer('edit'); } else { self.redirect({controller: self.name}); } }); }); };
我们在这里所做的是加载请求的 todo
,编辑它的一些属性,然后再次保存 todo
。我们刚刚在模型适配器中编写的代码应该处理其余的事情。如果我们收到错误,则意味着新属性未通过验证,因此我们会将请求传输回 edit
操作。如果我们没有收到错误,我们将把请求重定向回 index
操作。
来吧,尝试一下。重新启动服务器,创建一个新的todo
,点击它的编辑链接,将状态更改为done
,并在index
中看到它已更新。如果您想验证验证是否有效,请尝试将 title
更改为少于 5 个字符的内容。
现在让“删除”按钮发挥作用。
现在我们已经有了一个待办事项列表应用程序,但是如果您开始使用它一段时间,那么在该索引页面上找到您要查找的 todo
项目将会变得很困难。让我们让“删除”按钮起作用,这样我们就可以保持列表简洁明了。
让我们再次打开模型适配器,这次我们要在其中添加 remove
方法。在 save
方法之后添加此内容:
this.remove = function(id, callback) { if (typeof callback != 'function') { callback = function(){}; } for (var i in geddy.todos) { if (geddy.todos[i].id == id) { geddy.todos.splice(i, 1); return callback(null); } } return callback({message: "To Do not found"}); }
这个非常简单,它应该看起来很像 load 方法。它循环遍历 geddy.todos
中的所有 todo
以找到我们要查找的 id
。然后,它将该项目从数组中拼接出来并调用回调。如果在数组中找不到它,则会调用回调并返回错误。
现在让我们在控制器中使用它。
再次打开控制器并执行 remove
操作。它应该看起来像这样:
this.remove = function (req, resp, params) { this.respond({params: params}); };
编辑它,使其看起来像这样:
this.remove = function (req, resp, params) { var self = this; geddy.model.adapter.Todo.remove(params.id, function(err){ if (err) { params.errors = err; self.transfer('edit'); } else { self.redirect({controller: self.name}); } }); }
我们将从表单 post 中的参数中获取的 id
传递到我们刚刚创建的 remove
方法中。如果返回错误,我们将重定向回 edit
操作(我们假设表单发布了错误的信息)。如果我们没有收到错误,只需将请求发送到 index
操作。
就是这样!我们完成了。
您可以通过重新启动服务器,创建一个新的 todo
项目,单击它的链接,然后单击“删除”按钮来测试删除功能。如果您做得正确,您应该会返回索引页并删除该项目。
在下一个教程中,我们将使用 http://i.tv 很棒的 mongodb-wrapper 模块将 todo
持久保存到 MongoDB 中。有了 Geddy,这一切就会很容易;我们需要改变的只是模型适配器。
Si vous avez des questions, veuillez laisser un commentaire ici ou soulever un problème sur github.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!