Found this great resource on Hacker News for Vim Recipes. I've been meaning to learn more about split screens, folds, and tabs in Vim and this resource gave me the push in the right direction I needed.
I've been working on a new project that has some content management features lately. It's a pretty big project and will take months to complete. I was messing with in-place editing with prototype and scriptaculous and found that I couldn't use the default editor for managing links. Normally when you're doing in-place editing you have a single piece of text/content that you want to edit. You click on it and it brings up the form. For editing links I need to edit the URL and the anchor text. I needed to have two separate fields for this and Ajax.InPlaceEditor from controls.js wasn't entirely up to the task.
I ended up inheriting from Ajax.InPlaceEditor and altering some of the behavior to handle link editing. I feel like it's pretty much a hack job but it does what I need it to do, so it seems good enough. I felt that it would be somewhat beneficial to upload the code for in-place link editing.
I only uploaded the javascript and a html demo. The server-side stuff isn't included since this will work across different web languages. I use Pylons and Mako, for example.
You can grab the demo here: http://alwaysmovefast.com/public_svn/in_place_link_editor.
To give a short example of what I did for the server-side stuff, this could be my controller/action:
class LinksController(BaseController):
def create(self):
c.link = {
'url': request.params['url'],
'anchor': request.params['anchor'],
'id': 'some_id'
}
return render('/links/create.mako')
And this is what '/links/create.mako' could look like to update the page:
<a href="${c.link['url']}">${c.link['anchor']}</a>
This is a very basic example. My create.mako actually looks something like this:
<script src="/javascripts/prototype.js"></script>
<script src="/javascripts/scriptaculous.js"></script>
<script src="/javascripts/application.js"></script>
<script>
li = $('newlink');
li.id = '${c.link['id']}';
li.innerHTML = '<a href="${c.link['href']}">${c.link['anchor']}</a>';
new_link = document.createElement('li');
new_link.id = 'newlink';
new_link.innerHTML = 'Insert Link';
li.parentNode.appendChild(new_link);
new InPlaceLinkEditor('newlink', '/links/create');
</script>
Basically what it does is update 'newlink' by changing its id to the id of the newly-created link on the server side and update the innerHTML of that object. It then creates a brand new link object and appends it to the old link's parent node. I then create a new InPlaceLinkEditor for the new 'newlink'. That's about it.
Hopefully you can find this stuff useful.
There's a lack of documentation on how to use FormEncode in real projects so I've been meaning to write this article for some time now. I use FormEncode in my Pylons apps and I created a small app just for this article. You can grab the source here: http://alwaysmovefast.com/public_svn/formencode_tutorial. To make using FormEncode easier, I also created a few form helper methods that can be found in formencode_tutorial/lib/form_helpers.py. I also used a little CSS to make the form somewhat pretty.
FormEncode makes it pretty easy to do form validations and to then display any errors to your users. The way it works is that you pass a string of HTML to htmlfill.render(), along with some other options, and it returns parsed HTML for use in your pages. htmlfill.render() uses an errors option where you pass in a dict consisting of field names and error values. A typical error dict might look like: {'firstname': Invalid(u'Please enter your firstname',), 'lastname': Invalid(u'Please enter your lastname',)}.
htmlfill.render() will parse those errors and inject them into your <form:error> tag for that particular element. As well as accepting an error dict, htmlfill.render() can accept a default value dict that will inject default values into your form elements.
When injecting errors into your HTML template, it will also use an error formatter. The default error formatter looks like this:
def default_formatter(error):
return '<span class="error-message">%s</span><br />' % html_quote(error)
You can also use custom error formatters for a little more control over the look and feel of your displayed errors. I like to use this as my formatter:
def p_error_formatter(error):
return '<p>%s</p>' % htmlfill.html_quote(error)
The reason I use this as my formatter is because I display label elements below my form elements and above the errors. It's mostly personal preference.
I created a helper to abstract away some of the finer details of using formencode. One of them helps me create text fields with the label and form:error tags prepackaged and ready to pass to htmlfill.render():
# return HTML string that can be passed to htmlfill.render()
def tfield(name, label=None, **options):
if label is None: label = name
s = text_field(name, **options)
id = ''
if options.has_key('id'): id = options['id']
else: options['id'] = name
# append a label to this field for good measure
s += '<label for="%s">%s</label>' % (id, label)
# use a custom error formatter. when htmlfill.render() parses this html
# it looks for the key 'p_error_formatter' and uses that formatter.
# remember that 'p_error_formatter' is looked for in the error_formatters dict
# defined above.
s += '<form:error name="%s" format="p_error_formatter"></form:error>' % name
return
Notice the form:error tag? That's where htmlfill.render() inserts error text (if there is any) and formats it according to the 'p_error_formatter'. So how does htmlfill.render() know how to handle 'p_error_formatter'? Easy. You create a dict with your error formatter(s) and pass it to htmlfill.render(). When htmlfill.render() parses your HTML, it will take 'p_error_formatter' and do a lookup in your error formatter dict and call that function with the error text.
Here's my error_formatter dict:
error_formatter = {
'default': htmlfill.default_formatter,
'p_error_formatter': p_error_formatter
}
error_formatter['p_error_formatter'] is the p_error_formatter() function I defined above.
I also created my own render() function that just calls htmlfill.render() with some predetermined args:
def render(html, defaults=None, errors=None):
return htmlfill.render(
html,
defaults=defaults,
errors=errors,
error_formatters=error_formatters,
auto_insert_errors=False
)
That's about it for the core htmlfill stuff. I have a couple other helper methods in form_helpers.py and some more CSS that makes the background of form elements red when there's an error.
If you're running Pylons, just go into the root formencode_tutorial directory and run 'paster serve ––reload development.ini' and check out how it's all tied together.
Feel free to leave comments if you have any questions or if I missed something. Happy hacking!
I'm leaving for Honolulu tomorrow morning so I may not respond to emails for a couple weeks. I hope every one is well!
I will be in Boston from August 9-12 for Affiliate Summit East. I'm Looking forward to meeting some new people and getting some work done. It should be a pretty good time, especially with people volunteering to buy drinks at the meet-ups.