Friday, February 19, 2010

CouchApp Updates (with a Slight Conflict)

‹prev | My Chain | next›

Tonight I continue my exploration of couchapp by attempting to update CouchDB documents. Reading through documentation has left me with the sense that this is rather involved, so I am not sure how far I can get tonight. Only one way to find out!

First up, I copy my recipe show function over to an edit function, removing the textile conversion so that raw textile can be edited:
function(doc, req) {
// !json templates.edit
// !json templates._header
// !json templates._footer
// !code vendor/couchapp/template.js
// !code vendor/couchapp/path.js
// !code lib/super_textile.js

var image;
for (var prop in doc._attachments) {
image = prop;
}

return template(templates.edit, {
title: doc.title,
docid: (doc && doc._id),
asset_path: assetPath(),
summary: doc.summary,
instructions: doc.instructions,
image: image,
header: template(templates._header, {}),
footer: template(templates._footer, {})
});
}
Then some simple form HTML goes into the new templates/edit.html:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Recipe: <%= title %></title>
<link rel="stylesheet" href="<%= asset_path %>/style/main.css" type="text/css" />
</head>

<body>
<%= header %>

<h1>Editing <%= title %></h1>

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

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

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

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

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

</form>

<img src="../../../../<%= docid %>/<%= image %>" />

<%= footer %>
</body>
</html>
Easy enough, that gives me this edit screen:


Unfortunately, it is not quite that easy. Clicking the Save button does not work:


Forms in couchapp are not old-fashioned POSTs. They need to perform PUTs, DELETEs and POSTs of JSON data. Web browsers won't do that, so some javascript is needed. Fortunately, couchapp takes care of much of the heavy lifting for me. I add this to the bottom of the edit template:
<script src="<%= asset_path %>/vendor/couchapp/jquery.couchapp.js"></script>
<script type="text/javascript" charset="utf-8">
$.CouchApp(function(app) {

var docid = document.location.pathname.split('/').pop();
app.docForm("form#update-recipe", {
fields: ['title', 'summary', 'instructions'],
template: {type: "Recipe", _id: docid},
beforeSave: function(doc) {
alert("Here!");
},
success: function(res, doc) {
alert("Success!");
}
});
});
</script>
I am using a never-gets-old debug through javascript alerts in there—the classics never really go out of style. Actually, that is not so much an example of debugging through alerts as it is an example of using tracer bullets. At least that is what I will tell myself when I try to sleep tonight.

Anyhow...

The two <script> tags pull in some necessary javascript (jquery and some couchapp javascript built with jQuery). With that, I can call the $.CouchApp function to attach RESTful behavior to my form. Specifically, I create a couchapp document form (docForm()) that will convert the form contents into a JSON document to be PUTted into the DB.

The field option supplied to docForm() describes which fields need to be converted from elements into JSON attributes before being PUTted onto the database. The template attribute describes default fields to be submitted (the '_id' is definitely needed to PUT onto the correct document). Lastly, the beforeSave and success callbacks contain my awesome alert() tracer bullets. Once I have the rest of the code hitting them correctly, I can replace the alerts with code to manipulate the JSON document before PUT, and custom code to handle successful updates.

For now, when I submit, I expect to see the beforeSave's "Here!" alert, and a JSON document submitted to the CouchDB database. Unfortunately, what I get is Cannot call method 'db' of undefined at:


It turns out that the following lines are very important:
...</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) {
...
I had ignored those _utils files because I could not find them on the filesystem and just assumed that they were part of sofa. Not so, they are provided by CouchDB itself and, yeah, kinda important.

With those in place, when I submit, I do see the beforeSave() "Here!" tracer bullet, but find that I am not saving the document because of a 409/Conflict:


That actually makes perfect sense—I have not supplied a _rev attribute in the PUT document so CouchDB has no way to apply optimistic locking against the save. That should be easy enough to address, but I will leave that until tomorrow (along with getting the remaining recipe document elements into the form).

Day #19

No comments:

Post a Comment