First Stab at Widgets
July 1, 2010
Having finished a bunch of minor maintenance on Yesod (more consistent API names, that kind of stuff), I've begun to focus in on the big new feature brewing for Yesod 0.4.0: widgets. I'm really not a big fan of designing awesome technology out in the middle of nowhere; instead, I like to think of real-world problems I want to solve, and then build the tools around it. So let's start with a motivating example: forms.
Instead, we're going to create composable widgets. They keep track of not just HTML to be placed in the body of a document, but also scripts and stylesheets to load, extra HTML to throw in the head, CSS for <style> tags, and the title of the page. It also should supply me with unique identifiers to use.
I've implemented this as a monad transformer stack; you can see the implementation on Github. The Yesod.Widget module also contains a bunch of functions for creating widgets. For example, addStylesheet adds a stylesheet, addBody adds some content to the body of the page, etc. There's also a cool function
applyLayoutW which is able to display a widget with the default page layout.
Since a widget is just a monad stack, it composes very easily. There's also a Monoid instance to make life simpler. The trickiest part of it right now is when you want to wrap the body HTML code up somehow (for example, sticking everything in a div id=wrapper tag). For this, check out the wrapWidget and extractBody functions in the Yesod.Widget module.
The next big step is converting Yesod.Form over to use widgets. The original inspiration for this module is formlets; however, I'm veering farther and farther from the API, as my goals are quite different than the formlets API. As an example, formlets does not support inline error messages; all validation messages must be shown together. I don't find that an acceptable tradeoff.
The API is in flux right now, so I don't want to spend too much time talking about it. However, here's a high-level approach: there is a GForm monad which takes an "xml" type parameter. There are then two type synonyms: Form sets that xml type parameter to be a widget. This is what you'll usually want to embed in a page.
The other type synonym is FormField, which instead sets that xml type to FieldInfo. A FieldInfo contains information on the label, error messages, and various other things in a field. Of course, you can't display this directly; there will be various helper functions to convert a FieldInfo into a widget. The reason to split this up is so users have more control of how to layout their forms. For example, there's a function currently to display a FieldInfo as a row in a table; we could also provide a list and paragraph version.
And I still intend to have all this tie in with some typeclass for automatic creation of forms for PersistEntity instances. Just imagine: you could declare your entity structure once, and Yesod will create a database schema, generate SQL code, and create HTML forms for you automatically. I have some examples of this actually working with various versions of Yesod, and most likely the release of Yesod 0.4.0 will be accompanied by a screencast showing how to use all these features together. Most likely it will be the inevitable blog example ;).