Sunday, February 21, 2010

CouchApp Create/Update—Now without Errors!

‹prev | My Chain | next›

I ended yesterday with a couchapp error. When I was editing a new document, I got "The document could not be retrieved: missing":



Not unexpectedly, this turns out to be my fault. A peak in the CouchDB log when I accessed the edit page reveals several successful requests (the page itself, CSS, javascript files, etc.):
[Sun, 21 Feb 2010 15:11:01 GMT] [info] [<0.1509.0>] 127.0.0.1 - - 'GET' /eee/_design/relax/_show/edit 304
[Sun, 21 Feb 2010 15:11:01 GMT] [info] [<0.6869.0>] 127.0.0.1 - - 'GET' /eee/_design/relax/style/main.css 304
[Sun, 21 Feb 2010 15:11:01 GMT] [info] [<0.6869.0>] 127.0.0.1 - - 'GET' /_utils/script/json2.js 304
[Sun, 21 Feb 2010 15:11:02 GMT] [info] [<0.6871.0>] 127.0.0.1 - - 'GET' /_utils/script/jquery.js?1.2.6 304
[Sun, 21 Feb 2010 15:11:02 GMT] [info] [<0.6872.0>] 127.0.0.1 - - 'GET' /_utils/script/jquery.couch.js?0.8.0 304

[Sun, 21 Feb 2010 15:11:02 GMT] [info] [<0.6873.0>] 127.0.0.1 - - 'GET' /eee/_design/relax/vendor/couchapp/jquery.couchapp.js 304
And finally, the missing document:
[Sun, 21 Feb 2010 15:11:02 GMT] [info] [<0.6874.0>] 127.0.0.1 - - 'GET' /eee/edit 404
The problem here is in the docForm() function in the edit.html template:
$.CouchApp(function(app) {

var docid = document.location.pathname.split('/').pop();
app.docForm("form#update-recipe", {
id: docid,
fields: ['title', 'summary', 'instructions'],
template: {type: "Recipe"},
beforeSave: function(doc) {
alert("Here!");
},
success: function(res, doc) {
alert("Success!");
}
});
});
I was most likely first exploring edits when I tried calculating the document ID from the URL, which would look something like:
http://localhost:5984/eee/_design/relax/_show/edit/2008-07-12-salmon
Editing a new document would have this URL:
http://localhost:5984/eee/_design/relax/_show/edit
With my current document ID code, couchapp would end up trying to pull back a document with an ID of "edit". So how to do this the idiomatic couchapp way?

To answer that question, I look at the edit show function and template in sofa. The show function contains a docid setting:
  return template(templates.edit, {
docid : toJSON((doc && doc._id) || null),
//...
In the template, sofa uses this value in the docForm() function:
        var postForm = app.docForm("form#new-post", {
id : <%= docid %>,
...
Ah, that explains the use of toJSON() in the show function. For a document ID of 2008-07-21-spinach, toJSON will produce "2008-07-21-spinach", which will be a valid value in the docForm() option hash. If there is no document, the toJSON() function will produce null—another valid javascript value.

While perusing the sofa code, I also noticed that it never passes in existing form values or uses them in templates like I have been:
<label>Title: <input type="text" id="title" name="title" value="" size="50"><%= title ></label>
This is because the docForm() method, which is responsible for mapping form values into JSON to be PUT into the CouchDB database, also looks up existing documents and populates form values for editing.

So my edit.html template become much simpler:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Recipe Edit</title>
<link rel="stylesheet" href="<%= asset_path %>/style/main.css" type="text/css" />
</head>

<body>
<%= header %>

<h1>Editing</h1>

<!-- form to create a post -->
<form id="update-recipe" action="update.html" method="post">

<p>
<label>Title: <input type="text" id="title" name="title" value="" size="50"></label>
</p>

<p>
<label>Summary:<br>
<textarea name="summary" rows="5" cols="80"></textarea>
</label>
</p>

<p>
<label>Instructions:<br>
<textarea name="instructions" rows="15" cols="80"></textarea>
</label>
</p>

<p>
<input type="submit" value="Save &rarr;"/> <span id="saved" style="display:none;">Saved</span>
</p>

</form>

<%= footer %>
</body>
<script src="/_utils/script/json2.js"></script>
<script src="/_utils/script/jquery.js?1.2.6"></script>
<script src="/_utils/script/jquery.couch.js?0.8.0"></script>
<script src="<%= asset_path %>/vendor/couchapp/jquery.couchapp.js"></script>
<script type="text/javascript" charset="utf-8">
$.CouchApp(function(app) {

app.docForm("form#update-recipe", {
id : <%= docid %>,
fields: ['title', 'summary', 'instructions'],
template: {type: "Recipe"},
beforeSave: function(doc) {
alert("Here!");
},
success: function(res, doc) {
alert("Success!");
}
});
});
</script>
</html>
The title, summary, and instructions fields are populated in the web form by couchapp, which uses the fields attribute to drive this. When I view the edit form for the 2008 spinach pie recipe I see all of the fields populated:



In the log, I see the request for the edit template itself, and the subsequent request made for the document to be edited:
[Mon, 22 Feb 2010 03:24:26 GMT] [info] [<0.18563.1>] 127.0.0.1 - - 'GET' /eee/_design/relax/_show/edit/2008-07-21-spinach 200

[Mon, 22 Feb 2010 03:24:27 GMT] [info] [<0.18714.1>] 127.0.0.1 - - 'GET' /eee/2008-07-21-spinach 200
And, when I access the edit show function without a document ID, I get an empty edit form:



Blank and with no error message this time! Removing code and getting better results—I do believe that I am getting the hang of this.

Day #21

No comments:

Post a Comment