Creating a Todo List App With Node.js and Geddy

Creating a Todo List App With Node.js and Geddy

Tutorial Details
  • Topic: Node.js, Geddy, ejs, JavaScript
  • Difficulty: Medium
  • Estimated Completion Time: 1.5 hours

In this three part tutorial, we’ll be diving deep into creating a to do list management app in Node.js and Geddy. This is the second part in the series, where we’ll be creating a simple to do list management app.


Recap

As a quick refresher, last time we installed Node and Geddy, generated a new app, and learned how to start up the server. In this tutorial we’ll build upon what we did last time, so make sure you’ve completed that one before continuing.


Generating the Todo Resource

Geddy has a built in resource generator; this will allow us to automatically generate a model, controller, views, and routes for a specific resource. Our to do list app will only have one resource: todo. To generate it, just cd into your app’s directory (cd path/to/your/todo_app) and run:

geddy resource todo

You should now have these files added to your app:

  • app/models/todo.js
  • app/controllers/todos.js
  • app/views/todos/
    • index.html.ejs
    • show.html.ejs
    • edit.html.ejs
    • add.html.ejs

Your config/router.js should also have this appended to it:

router.resource('todos');

What it all does

If you’re new to MVC this all might seem a little daunting to you. Don’t worry though, it’s really simple once you figure it out.

models/todo.js: This file is where we’ll define our todo model. We’ll define a number of properties that all todo’s have. We’ll also write some data validations here.

controllers/todos.js: This file is where all the /todos/ routes end up. Each action in this controller has a corresponding route:

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/: Each file in here corresponds to one of the GET routes that we showed you above. These are the templates that we use to generate the front end of the app. Geddy uses EJS (embedded JavaScript) as it’s templating language. It should look familiar if you’ve ever used PHP or ERB. Basically, you can use any JavaScript that you’d like in your templates.

Getting a feel for the routes

Now that we’ve generated a bunch of code, let’s verify that we’ve got all the routes that we need. Start the app again (geddy), and point your browser to http://localhost:4000/todos. You should see something like this

Go ahead and try that for the other GET routes too:

  • http://localhost:4000/todos/something
  • http://localhost:4000/todos/add
  • http://localhost:4000/todos/something/edit

All good? Alright, let’s continue.


Creating the Todo Model

In Geddy (and most other MVC frameworks), you use models to define the kind of data that your app will work with. We just generated a model for our todos, so let’s see what that gave us:

var Todo = function () {
  // Some commented out code
};

// Some more commented out code

Todo = geddy.model.register('Todo', Todo);

Models are pretty simple in Geddy. We’re just creating a new constructor function for our todos and registering it as a model in geddy. Let’s define some properties for our todos. Delete all the commented out code and add this to the contructor function:

var Todo = function () {
  this.defineProperties({
    title: {type: 'string', required: true}
  , id: {type: 'string', required: true}
  , status: {type: 'string', required: true}
  });
};

Our todos will have a title, an id, and a status, and all three will be required. Now let’s set some validations for our todos.

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';
  });

};

We’re validating that the title is present, that the title has a minimum length of 5 characters, and we’re using a function to validate that the status is either open or done. There are quite a few valitation functions that are built in, go ahead and check the project out on http://github.com/mde/geddy to learn more about them.


Creating the Todo Model Adapter

Now that we’ve set up our todo model, we can create somewhere to store our models. For the purposes of this tutorial, we’re just going to keep the data in memory. We’ll hang a todos array off of our global geddy object to stick the data in. In the next part of this series, we’ll start to get these persisted in a database.

Editing Your init.js File

Open up your config/init.js file. All that should be in there now is a global uncaught exception handler:

// Add uncaught-exception handler in prod-like environments
if (geddy.config.environment != 'development') {
  process.addListener('uncaughtException', function (err) {
    geddy.log.error(JSON.stringify(err));
  });
}

Right after that block of code, let’s hang our array off the geddy global:

geddy.todos = [];

There, now we’ve got a place to store our todos. Remember, this is in your application-memory, so it will disappear when you restart the server.

Creating the Model-adapter

A model-adapter provides the basic save, remove, load, and all methods a model needs. Our data source is pretty simple (just an array!), so writing our model adapter should be pretty simple too.

Create a directory in lib called model_adapters and create a file in lib/model_adapters called todo.js. Let’s open up that file and add in some boilerplate code:

var Todo = new (function () {
})();
exports.Todo = Todo;

All we’re doing here is setting up a new blank object to be exported out to whatever ends up requiring this file. If you’d like to know a bit more about how Node’s require method works, this article has a pretty good overview. In this case, our init.js file will do the requiring.

Require the model adapter in init.js

So we set up a new Todo model-adapter object. It’s pretty barren right now, but we’ll get to that soon. For now, we’ll have to go back to init.js and add some code so that it’s loaded into our app when it starts up. After the geddy.todos = []; in config/init.js add these two lines:

geddy.model.adapter = {};
geddy.model.adapter.Todo = require(process.cwd() + '/lib/model_adapters/todo').Todo;

We created a blank model-adapter object, and added the Todo model adapter onto it.


Saving Todos

Now that we have our model and model adapter in place, we can start in on the app logic. Let’s start with adding to do items to our to do list.

Edit the save method on the adapter to save a todo instance

When working with data, the first place you should go is the model adapter. We need to be able to save an instance of our Todo model to our geddy.todos array. So open up lib/model_adapters/todo.js and add in a save method:

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);

  }
})();

All we have to do is set the instance’s saved property to true and push the item into the geddy.todos array. In Node, it’s best to do all I/O in a non-blocking way, so it’s a good idea to get in the habit of using callbacks to pass data around. For this tutorial it doesn’t matter as much, but later on when we start persisting things, it’ll come in handy. You’ll notice that we made sure that the callback is a function. If we don’t do that and use save without a callback, we’d get an error. Now let’s move on to the controller create action.

Edit the create action to save a todo instance

Go ahead and take a look at the create action in app/controllers/todos.js:

this.create = function (req, resp, params) {
  // Save the resource, then display index page
  this.redirect({controller: this.name});
};

Pretty simple, right? Geddy has stubbed it out for you. So let’s modify it a little bit:

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});
    }
  });
};

First, we create a new instance of the Todo model with geddy.model.Todo.create, passing in the title that our form will post up to us, and setting up the defaults for the id and status.

Then we call the save method that we created on the model adapter and redirect the user back to the /todos route. If it didn’t pass validation, or we get an error, we use the controller’s transfer method to transfer the request back over to the add action.

Edit add.html.ejs

Now it’s time for us to set up the add template. Take a look at app/views/todos/add.html.ejs, it should look like this:

<div class="hero-unit">
  <h3>Params</h3>
  <ul>
  <% for (var p in params) { %>
    <li><%= p + ': ' + params[p]; %></li>
  <% } %>
  </ul>
</div>

We won’t be needing that

    for our use case, so let’s get rid of it for now. Make your add.html.ejs look like this:

    <div class="hero-unit">
      <%= partial('_form', {params: params}); %>
    </div>

    An Intro to Partials

    Partials give you an easy way to share code between your templates.

    You’ll notice that we’re using a partial in this template. Partials give you an easy way to share code between your templates. Our add and edit templates are both going to use the same form, so let’s create this form partial now. Create a new file in the views/todos/ directory called _form.html.ejs. We use an underscore to easily tell if this template is a partial. Open it up and add in this code:

    <%
      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>

    Whoa, that’s a lot of code there! Let’s see if we can walk through it. Since two different templates are going to be using this partial, we’ve got to make sure the form looks right in both of them. Most of this code is actually boilerplate from Twitter’s Bootstrap. It’s what allows this app to look so good right off the bat (and on mobile devices too!).

    To make this app look even better, you can use the CSS file provided in the demo app download.

    The first thing we did was set up some variables for us to use. In the add action we’re passing a params object down to the template in the respond method call. This gives us a few things – it tells us what controller and action this request has been routed to, and gives us any query parameters that were passed in the url. We set up the isUpdate variable to see if we’re currently on the update action, and then we set up a few more variables to help clean up our view code.

    From there, all we did was make a form. If we’re on the add action, we just render the form as is. If we’re on the edit action, we fill in the form to let the user update the fields.

    Notice that the form will send a POST request to the /todos/ with a _method=PUT parameter. Geddy uses the standard method override parameter to allow you to send PUT and DELETE requests up from the browser without having to use JavaScript. (on the front end at least!)

    The last little detail we need to take a look at is that “Remove” button. We’re using html5’s formaction attribute to change the action for this form. You’ll notice that this button’s formaction sends a POST request up to the /todos/:id route with a _method=DELETE parameter. This will hit the remove action on the controller, which we’ll get to later.

    Restart your server (geddy) and visit http://localhost:4000/todos/add to see your template in action. Create a To Do item while you’re at it.


    Listing all Todos

    Now that we have user input To Do items being added into our geddy.todos array, we should probably list them somewhere. Let’s start in on the all method in the model-adapter.

    Edit the all method on the adapter to list all todos

    Let’s open lib/model_adapters/todo.js again and add an all method right above thesave` method:

    this.all = function (callback) {
      callback(null, geddy.todos);
    }

    This is probably the simplest model-adapter method that we’ll create today, all it does is accept a callback and call it with the an error (which is always null for now, we’ll upgrade this method in the next tutorial), and geddy.todos.

    Edit the index action to show all todos

    Open up /app/controllers/todos.js again and take a look at the index action. It should look something like this:

    this.index = function (req, resp, params) {
      this.respond({params: params});
    };

    This part is really simple, we just use the all method that we just defined on the model-adapter to get all the todos and render them:

    this.index = function (req, resp, params) {
      var self = this;
      geddy.model.adapter.Todo.all(function(err, todos){
        self.respond({params: params, todos: todos});
      });
    };

    That’s it for the controller, now onto the view.

    Edit index.html.ejs

    Take a look at /app/views/todos/index.html.ejs, it should look like this:

    <div class="hero-unit">
      <h3>Params</h3>
      <ul>
      <% for (var p in params) { %>
        <li><%= p + ': ' + params[p]; %></li>
      <% } %>
      </ul>
    </div>

    Looks a lot like the add.html.ejs template doesn’t it. Again, we won’t need the params boilerplate here, so take that out, and make your index.html.ejs template look like this:

    <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 &amp;&amp; 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>
      <% } %>
    <% } %>

    This one is also pretty simple, but this time we’ve got a loop in our template. In the header there we’ve added a button to add new todo’s. Inside the loop we’re generating a row for each todo, displaying it’s title (as a link to it’s edit page), and it’s status.

    To check it out, go to http://localhost:4000/todos.


    Editing a Todo

    Now that we have a link to the edit page, we should probably make it work!

    Create a load method in the model adapter

    Open up your model adapter again (/lib/model_adapters/todo.js). We’re going to add in a load method so that we can load a specific todo and use it in our edit page. It doesn’t matter where you add it, but for now let’s put it between the all method and the save method:

    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);
    };

    This load method takes an id and a callback. It loops through the items in geddy.todos and checks to see if the current item’s id matches the passed in id. If it does, it calls the callback, passing the todo item back. If it doesn’t find a match, it calls the callback with a error. Now we need to use this method in the todos controller’s show action.

    Edit the edit action to find a todo

    Open up your todos controller again and take a look at it’s edit action. It should look something like this:

    this.edit = function (req, resp, params) {
      this.respond({params: params});
    };

    Let’s use the load method that we just created:

    this.edit = function (req, resp, params) {
      var self = this;
      geddy.model.Todo.load(params.id, function(err, todo){
        self.respond({params: params, todo: todo});
      });
    };

    All we’re doing here is loading the todo and sending it down to the template to be rendered. So let’s take a look at the template.

    Edit edit.html.ejs

    Open up /app/views/todos/edit.html.ejs. Once again we’re not going to need the params boilerplate, so let’s remove it. Make your edit.html.ejs look like this:

    <div class="hero-unit">
      <%= partial('_form', {params: params, todo: todo}); %>
    </div>

    This should look very similar to the add.html.ejs file we just edited. You’ll notice that we’re sending a todo object down to the partial as well as the params this time. The cool thing is, since we already wrote the partial, this is all we’ll have to do to get the edit page to show up correctly.

    Restart the server, create a new todo and click the link to see how this works. Now let’s make that update button work!

    Edit the save method in the model-adapter

    Open up the model-adapter again and find the save method. we’re going to be adding a bit to it so that we can save over existing todos. Make it look like this:

    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);
    }

    This loops over all the todo’s in geddy.todos and if the id is already there, it replaces that todo with the new todo instance. We’re doing some stuff here to make sure that our validations work on update as well as create – in order to do this we have to pull the errors property off of a new model instance and pass that back in the callback. If it passed validations, it’ll just be undefined and our code will ignore it. If it didn’t pass, todoErrors will be an array of validation errors.

    Now that we have that in place, let’s work on our controller’s update action.


    Edit the update action to find a todo, change the status, and save it

    Go ahead and open up the controller again and find the ‘update’ action, it should look something like this:

    this.update = function (req, resp, params) {
      // Save the resource, then display the item page
      this.redirect({controller: this.name, id: params.id});
    };

    You’ll want to edit it to make it look like this:

    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});
          }
        });
      });
    };

    What we’re doing here is loading the requested todo, editing some of it’s properties, and saving the todo again. The code we just wrote in the model-adapter should handle the rest. If we get an error back, that means the new properties didn’t pass validation, so we’ll transfer the request back to the edit action. If we didn’t get an error back, we’ll just redirect the request back over to the index action.

    Go ahead and try it out. Restart the server, create a new todo, click on it’s edit link, change the status to done, and see that it get’s updated in the index. If you want to verify that you have your validations working, try changing the title to something shorter than 5 characters.

    Now let’s get that “Remove” button working.


    Removing a Todo

    By now we’ve got a working to do list application, but if you start using it for a while, it’s going to get tough to find the todo item that you’re looking for on that index page. Let’s make that “Remove” button work so we can keep our list nice and short.

    Create a remove method in the model-adapter

    Let’s open up our model-adapter again, this time we’re going to want to add a remove method in there. Add this right after the save method:

    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"});
    }

    This one is pretty simple, it should look a lot like the load method. It loops through all the todos in geddy.todos to find the id that we’re looking for. It then splices that item out of the array and calls the callback. If it doesn’t find it in the array, it calls the callback with an error.

    Let’s use this in our controller now.

    Edit the remove action

    Open up your controller again and fing the remove action. It should look something like this:

    this.remove = function (req, resp, params) {
      this.respond({params: params});
    };

    Edit it to make it look like this:

    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});
        }
      });
    }

    We pass the id that we got from the params in the form post into the remove method that we just created. If we get an error back, we redirect back to the edit action (we’re assuming the form posted the wrong info). If we didn’t get an error back, just send the request over to the index action.

    Thats it! We’re done.

    You can test the remove feature by restarting your server, creating a new todo item, clicking on it’s link, then clicking on the “Remove” button. If you did it right, you should be back on the index page with that item removed.


    The Next Steps

    In the next tutorial we’ll use http://i.tv’s awesome mongodb-wrapper module to persist our todo’s into MongoDB. With Geddy, this will be easy; all we’ll have to change is the model-adapter.

    If you have any questions, please leave a comment here, or open up an issue on github.

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://fabiojgrocha.com Fábio Rocha

    I will be testing geddy for sure, but, will node.js be reliable for high trafic apps?
    Thanks for the good work!

    • http://yammer.com/jobs Daniel Erickson
      Author

      We use Node at Yammer for two separate features, both of which see quite a bit of traffic. So yes :) It’s why we built Geddy.

      • http://fabiojgrocha.com Fábio Rocha

        Thanks Daniel, I’ve seen that Geddy uses Clustering, amazing :)
        To the future!

  • Darren

    Cracking tutorial, Dan

    So far I’ve used Mongoose/Express & MongoDb, I’m fairly new to MVC, I’m just wondering what the key gotcha’s are in moving to Geddy/MVC – I’m definitely going to try Geddy out next. Any key points would be appreciated..

    • http://yammer.com/jobs Daniel Erickson
      Author

      Thanks for taking a look! Geddy’s main advantage is that it provides you with a goods structure and base for your app. It allows you to forget about all the setup and just focus on your app’s logic. You have to do a lot of things yourself if you use express. Geddy’s does a lot of stuff for you.

    • Joshua

      I think the biggest issue for me with geddy is it locks you in to ejs … which, in my opinion at least, is a step backwards… with jade or hamljs you’re gaining a lot in their use, ejs doesn’t bring nearly the value to the table that actual templating languages do.

      • http://yammer.com/jobs Daniel Erickson
        Author

        It’s actually fairly simple to switch out the templating language. We’re working on adding docs for this.

  • http://www.udgwebdev.com Caio Ribeiro Pereira

    I won’t change Express to Geddy so soon.

    ExpressJS + Mongoose + Jade are the best!

  • Ryan

    Is the 1 in “geddy resource todo1″ a typo?

    • http://yammer.com/jobs Daniel Erickson
      Author

      Yes, sorry! We’ll get that resolved ASAP.

      • Ryan

        No worries! Just kind of wasn’t sure.

    • http://yammer.com/jobs Daniel Erickson
      Author

      This is fixed now.

  • Brenden

    I’ve been playing with Geddy for about 2 weeks now, and I’ve noticed a potentially huge flaw.

    It seems anyone would be able to POST, PUT, or even DELETE to //:id

    How would someone go about stopping that kind of thing?

    • http://yammer.com/jobs Daniel Erickson
      Author

      Geddy has session support and before filters built in. You can use those to set up your own auth system. Neither of them are very well documented, but post an issue on http://github.com/mde/geddy and we’ll help you through it.

  • Dan Smart

    How does Geddy compare to Monorail.js (https://github.com/runexec/Monorail.js)?

    • http://yammer.com/jobs Daniel Erickson
      Author

      Geddy doesn’t use express for anything. Geddy was built from the ground up to be a MVC framework, which means you don’t have to use express style routes. From personal experience I know that express routes can easily get out of hand in a large app. It uses EJS for templating, which is the defacto template engine in node. Geddy also enforces a structure for you, so your developers know exactly what to expect when the start working with your code base. We also have a unified model system, so no matter what DB you decide to use, the only thing you’d have to change is the model adapter – we’ll go over that in the next tutorial.

      That said, Monorail seems pretty cool :)

      • Dan Smart

        Great response. I think i’m going to give them both a try.

  • yelongren

    I got an error running the To Do List view. This is fixed by changing && to && in the index.html.ejs file. Yes, I’m a complete beginner, in geddy, in Node and in programming ;)

    Thank you very much for that tutorial. It is the only Node resource I’ve found that my skills can tackle.

  • http://wwww.webflysoftware.com adumpaul

    Nice descriptive article.Code are so useful.Thanks.

  • AlexCass

    I’ve had to restart node.js server numerous times trying to get through the tutorial. Every error incurs a restart. How do dev’s work around this. A huge time constraint when trying to test and develop.

    • Joshua

      npm install -g nodemon

      then, instead of starting the server with node, do nodemon app.js

      It monitors the folder for file changes and restarts the server

      • http://yammer.com/jobs Daniel Erickson
        Author

        Unfortunately, node-mon and node-dev type tools won’t work with geddy because geddy actually uses the geddy command to start the server. Like I said though, we’re working on making the developer’s life better in this aspect.

    • http://yammer.com/jobs Daniel Erickson
      Author

      Right now you’re just going to have to restart the server whenever you change a server side component (you don’t have to if you’re just editing views in development mode). We’re working on adding in automatic server restarts in development mode, but there’s a bug in that system right now.

  • Joshua

    Geddy is a non-starter for me – it doesn’t easily support different rendering engines…

    I’m a front-end developer for a rails shop… so i prefer to work in haml… express lets me do that… geddy doesnt (i know i can technically do it, but its not automagical)

    • http://yammer.com/jobs Daniel Erickson
      Author

      See my comments above :)

      tl;dr – We’re working on documenting it.

  • Nik

    Can Geddy handle associations easily? I understand that all models and all the CRUD actions are really javascript code, so sky is the limit. But I frankly do not want to build everything from scratch. I am currently using Mongoid with Rails and I am tempted to experiment with anything Node, but I can’t seem to find articles about how associations are done. — any ideas? –Thanks for this intro!

    • http://yammer.com/jobs Daniel Erickson
      Author

      We’ve got a plan to implement assiciations in a not insane way, but it’s not implemented yet. If you want to help out, or would like to contribute to the discussion, chech out the project at http://github.com/mde/geddy.

  • adam

    There seems error in the code on section
    Edit index.html.ejs

    The console said: SyntaxError: Unexpected token ;

    Any idea?

    • http://yammer.com/jobs Daniel Erickson
      Author

      Does it tell you which line it broke on? If not, I’ll try to figure out which one didn’t work for you. As a rule of thumb though, you’ll want to end all ejs tags with a semi-colon: instead of .

      • http://yammer.com/jobs Daniel Erickson
        Author

        hmmm, code didn’t format properly.

        <%= some.method(); %>

        instead of

        <%= some.method() %>

  • http://www.newtnetnews.com Christoph Rumpel

    Hi,

    thx for the tutorial. Unfortunately i get this error when i try to add an item to the list. Since i’m new to nodejs and geddy, it is not obvious to met where to look for the mistake. Maybe someone can help met :-)

    TypeError: Cannot call method ‘save’ of undefined
    at [object Object].save (/usr/local/lib/node_modules/geddy/lib/model/index.js:104:36)
    at [object Object].create (/Users/ChristophR/Projects/WorkingStuff/Coding/nodejs/firstapp/todo/app/controllers/todos.js:20:8)
    at /usr/local/lib/node_modules/geddy/lib/base_controller.js:512:22
    at [object Object]._handleAction (/usr/local/lib/node_modules/geddy/lib/base_controller.js:528:7)
    at /usr/local/lib/node_modules/geddy/lib/app.js:265:34
    at /usr/local/lib/node_modules/geddy/lib/app.js:423:19
    at /usr/local/lib/node_modules/geddy/lib/sessions/index.js:72:9
    at [object Object].read (/usr/local/lib/node_modules/geddy/lib/sessions/stores/memory.js:35:5)
    at [object Object].init (/usr/local/lib/node_modules/geddy/lib/sessions/index.js:75:19)
    at Timer._callback (/usr/local/lib/node_modules/geddy/lib/sessions/index.js:47:10)

    • http://yammer.com/jobs Daniel Erickson
      Author

      Hi Christoph,

      Go ahead and take a look at https://github.com/mde/geddy/tree/master/examples/todo_app and make sure that your code matches the code in the example app. The places that you’ll want to look are in config/init.js and lib/model_adapters/todo.js – Let me know if that doesn’t help. I’ll try to dig deeper with you.

      • http://www.newtnetnews.com Christoph Rumpel

        Hi,

        sorry i didn’t see that the code is on GitHub. Now it works, thx! One more question i got. After starting “geddy”, how can i end the server and return in terminal again?

  • Éric

    One of the routes for todos seems to be wrong, for the add action, isn’t it GET /todos/add instead of GET /todos/:id/add ?

    • http://yammer.com/jobs Daniel Erickson
      Author

      Yep, you’re totally right. it should be GET /todos/add

  • http://victorbello.com Victor Bello

    Awesome tutorial. Great help, I’m going to setup a geddy server and start playing a bit more with it.

    I found an issue with the code, it didn’t load the status selected when the item was saved. I fixed it by changing the status dropdown to look like this:

    <option selected=”selected”>open
    <option selected=”selected”>done

    • http://victorbello.com Victor Bello

      Well, the code doesn’t look right, let me try agaib.

      <select name="status">
      <option <% if(statusValue == ‘open’) {%> selected="selected"<% } %>>open</option>
      <option <% if(statusValue == ‘done’) {%> selected="selected"<% } %>>done</option>
      </select>

      • http://yammer.com/jobs Daniel Erickson
        Author

        Good idea! I’ll update the example code on github with this (or you can send a pull request!) – Thanks!

  • Ricardo

    If I have this in my nav bar:

    <div class=”navbar navbar-fixed-top”>
    <div class=”navbar-inner”>
    <div class=”container”>
    <a class=”btn btn-navbar” data-toggle=”collapse” data-target=”.nav-collapse”>
    <span class=”i-bar”><i class=”icon-chevron-down icon-white”></i></span>
    </a>
    <a class=”brand” href=”/”>Geddy App</a>
    <div class=”nav-collapse”>
    <ul class=”nav”>
    <li><a href=”/”>Home</a></li>
    <li><a href=”/todos”>Index</a></li>
    <li><a href=”/todos/add”>New</a></li>
    <!–<li><a href=”http://geddyjs.org/docs.html”>Docs</a></li>–>
    </ul>
    </div><!–/.nav-collapse –>
    </div>
    </div>
    </div>

    How can I make it so when I’m at Index, the index li gets a class=”active”?

    • http://yammer.com/jobs Daniel Erickson
      Author

      You can pass the params object into your view – it has both the action and the controller in it. If you need more help, go ahead and open an issue on http://github.com/mde/geddy.

  • Brad Roodt

    Great tuts! Thanks.

    Daniel, I’d be incredibly grateful if you could point me toward those docs for switching out ejs and implementing jade as the templating engine for geddy. thanks.

  • Brad Roodt

    One other question. When mapping a route to a controller, what is the correct way of registering the controller with the app. I don’t want to create a resource, just a simple route such as:

    router.match(‘/stinky’).to({controller: ‘Poo’, action: ‘index’});

    I thought that the above code would automatically register the controller, but it’s not working. I tried looking for the code that couples Main to ‘/’, to work my way backwards from that, but it all seems to work automagically.

    Thanks!

    • http://yammer.com/jobs Daniel Erickson
      Author

      That should work automatically, if it’s not for you, can you file an issue here: http://github.com/mde/geddy?

  • http://localhost Juan

    Hi,

    My server gets into an infinite loop when try to run localhost:4000/todos/add, even when i go to localhost;4000 it starts to loop and never gets nothing, can you help me with this?

    Ah, thanks for this awesome tutorial ! :)

    Thanks.

    • http://yammer.com/jobs Daniel Erickson
      Author

      Do you get any errors in your server’s console? If not, I’ll have to see your code in order to help.

  • Bill

    Are there any examples out there on the web of how to make an app involving multiple objects such as setting a parameter of object to another object

    foo.bar = baz
    baz.ba = “Hello World”

    bar = foo.bar
    print bar.ba // “Hello World”

  • Urvish

    Hello,

    Thanks a lot…

    I have created a demo todo app as per your demo but when i try to add a todo and click on button then i get a error “POST method is not allowed.”. Please help me for the same.

    Thanks

    • http://yammer.com/jobs Daniel Erickson
      Author

      Odd, not sure what would be causing this. Can you post a gist of your controller and view code?

      • http://www.i-avington.com Steve Moseley

        Urvish,
        Check your form tag in your _form.html.ejs file.

        I had the same issue and when I check my code, for some reason when I copied pasted the view code, my tag looked like this.

        <form id="todo-form" class="form-horizontal" action=""

        method=”POST”>
        .
        I fixed that tag to be

        <form id="todo-form" class="form-horizontal" action="” method=”POST”>
        .

        And it started working.

        Hope that helps.

  • http://mauromarano.it Mauro

    Does this tutorial is the same of this https://github.com/mde/geddy/wiki/Geddy-Tutorial ?

    • http://yammer.com/jobs Daniel Erickson
      Author

      This is a more in depth and updated version of that tutorial. The “official” tutorial should be getting updated soon.

  • Alex

    Hey I have a quick question. I just created the resources using the geddy resource todo command and did not get any filled added under the vies/todo folder. So no GET ot any other methods like that are available :(

  • john

    This tutorial should be updated as geddy resource no longer generates view templates

    https://github.com/mde/geddy/issues/167

  • Coda Cat

    I was missing ejs files in my todo_app/app/views/todos directory after typing [geddy resource todo]. To remedy this, I opened a new terminal window, cd to the path of my todo_app then typed [geddy scaffold todo title:default status]. Doing this populated the directory with all the ejs files (add.html.ejs, edit.html.ejs, form.html.ejs, index.html.ejs, show.html.ejs). I’m not sure if this is the correct workaround for the purpose of this tutorial but hope this helps someone out there.

    • Simon Douglas

      Thanks for this. I was scratching my head

  • Sorin Carbunaru