3부로 구성된 이 튜토리얼에서는 Node.js와 Geddy를 사용하여 할 일 목록 관리 앱을 만드는 방법을 자세히 살펴보겠습니다. 시리즈의 두 번째 부분에서는 간단한 할 일 목록 관리 애플리케이션을 만들어 보겠습니다.
간단히 요약하자면, 지난번에 Node와 Geddy를 설치하고, 새 애플리케이션을 생성하고, 서버를 시작하는 방법을 배웠습니다. 이 튜토리얼에서는 지난 시간에 수행한 작업을 기반으로 하므로 계속하기 전에 해당 튜토리얼을 완료했는지 확인하세요.
Geddy에는 리소스 생성기가 내장되어 있어 특정 리소스에 대한 모델, 컨트롤러, 뷰 및 경로를 자동으로 생성할 수 있습니다. 우리의 할 일 목록 앱에는 리소스가 하나만 있습니다: todo
。要生成它,只需将 cd
放入应用程序的目录(cd path/to/your/todo_app
) 그리고 실행:
이제 앱에 다음 파일이 추가되어야 합니다.
귀하의 config/router.js
에는 다음도 추가되어야 합니다:
MVC를 처음 사용하는 경우 이 모든 것이 다소 어려울 수 있습니다. 하지만 걱정하지 마세요. 일단 알고 나면 꽤 간단합니다.
models/todo.js: 이 파일은 todo
模型的地方。我们将定义所有 todo
이 가지고 있는 많은 속성을 정의하는 곳입니다. 여기에는 몇 가지 데이터 유효성 검사도 작성하겠습니다.
controllers/todos.js: 이 파일은 모든 /todos/
경로의 최종 위치입니다. 이 컨트롤러의 모든 작업에는 해당 경로가 있습니다:
views/todos/: 여기에 있는 각 파일은 위에서 보여드린 GET
경로 중 하나에 해당합니다. 이는 애플리케이션의 프런트 엔드를 생성하는 데 사용하는 템플릿입니다. Geddy는 템플릿 언어로 EJS(Embedded JavaScript)를 사용합니다. PHP나 ERB를 사용해 본 적이 있다면 이것이 익숙할 것입니다. 기본적으로 템플릿에 원하는 JavaScript를 사용할 수 있습니다.
이제 많은 코드를 생성했으므로 필요한 경로가 모두 있는지 확인해 보겠습니다. 애플리케이션을 다시 실행하고(geddy
) 브라우저에서 http://localhost:4000/todos를 가리키도록 하세요. 이런 걸 봐야 해
다른 GET
경로를 계속 시도하세요:
괜찮나요? 좋아, 계속하자.
Geddy(및 대부분의 다른 MVC 프레임워크)에서는 모델을 사용하여 애플리케이션에서 사용할 데이터 유형을 정의합니다. 방금 todo
에 대한 모델을 생성했으므로 이것이 우리에게 무엇을 제공하는지 살펴보겠습니다.
Geddy의 모델은 매우 간단합니다. todo
s 创建一个新的构造函数,并将其注册为 geddy 中的模型。让我们为 todo
s에 대한 새 생성자를 만들고 이를 geddy의 모델로 등록합니다.
으아아아
todo
s 将有一个标题、一个 id 和一个状态,这三个都是必需的。现在让我们为 todo
저희
에 대한 유효성 검사를 설정해 보겠습니다. open
或 done
으아아아
Todo 모델 어댑터 만들기geddy
init.js 파일을 편집하세요config/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,这一切就会很容易;我们需要改变的只是模型适配器。
궁금하신 점은 여기에 댓글을 남겨주시거나 github에 이슈를 올려주세요.
위 내용은 Node.js 및 Geddy를 사용하여 작업 관리자 애플리케이션 구축의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!