We already have a working environment – it’s the best possible moment to get started with teh codez!
Yesod, like many other web frameworks, has a Model-View-Controller architecture. It has a central controller instance which routes requests to proper handlers. In the scaffolded site these routes are conveniently stored in config/routes
file. If you didn’t make any changes to it, it probably looks like this:
/static StaticR Static appStatic /auth AuthR Auth getAuth /favicon.ico FaviconR GET /robots.txt RobotsR GET / HomeR GET POST /comments CommentR POST
it contains a lot of magic. It is a syntax which is later processed by Template Haskell to generate quite some code that guarantees type safety. For starters let’s skip first two routes and focus on the rest.
The syntax is fairly simple: route route-type handled-methods
. So, for example, line /comments CommentR POST
means: from all requests directed to route /comments
accept only POST requests and define type CommentR
which will be used to redirect to this route (in Yesod routes are type-safe, so you need types for them). Additional feature is that route handlers are invoked automatically – in out example it would be postCommentR
function (you can find its implementation in scaffolded site in Handler/Comment.hs
file. Handler names are created from lowercase method name concatenated with route type name. It’s a nice example of Convention over Configuration pattern, and it frees us from writing an awful amount of code which we don’t really want to write (at least I don’t). Adding R postfix (shortcut for Route) to route type is also a Yesod convention, which does not need to be kept, but may help others read your code.
Ok, enough chit-chat, let’s write some codez!
First, let’s edit config/routes
. Just stuff in there /projects ProjectsR GET
. Great, now your code doesn’t compile and complains about lack of getProjectsR
function. Let’s fix it the way scaffolded site does it – create a Handler/Projects.hs
file with the following content:
module Handler.Projects where import Import import Yesod.Form.Bootstrap3() getProjectsR :: Handler Html getProjectsR = defaultLayout $ do setTitle "Projects"
add the handler to <code>Application.hs</code> as well:
-- Import all relevant handler modules here. -- Don't forget to add new modules to your cabal file! import Handler.Common import Handler.Home -- new one import Handler.Projects
as the comment says, you also need to add this file to your <code>.cabal</code> file to this block:
exposed-modules: Application Foundation Import Import.NoFoundation Model Settings Settings.StaticFiles Handler.Common Handler.Home Handler.Comment Handler.Projects
that’s important, because if you won’t do that, you’ll get an unknown symbol
error at startup. Now run stack exec -- yesod devel
and go to localhost:3000/projects
. The result might not be very impressive (an empty page), but you might guess that it’s already using our route – it has our title (not very impressive either).
That’s all nice and fine, but if you take a look at the page source, you’ll notice that there’s quite some stuff already, including Bootstrap and jQuery. Where did it code from?
If you recall the handler code, it was really simple: getProjectsR = defaultLayout $ do setTitle "Projects"
. All the magic lies in defaultLayout
call. It picks up a default (for the project) layout file and fills it with data from the argument. This default template layout is split between two files: default-layout.hamlet
and default-layout-wrapper.hamlet
, both in templates
directory. .hamlet
is an extension for Hamlet files, which are Haskell form of HTML templates, serving roughly the same purpose as Jade, Jinja or Smarty. I’ll cover Hamlet templates in greater detail in the next post – for now let’s just treat a .hamlet
file as a HTML without closing tags. Since pretty much everything seems to be in default-layout-wrapper.hamlet
, you might wonder why are there two – this is generally related to the purposes – default-layout-wrapper.hamlet
is roughly a static structure that will be filled with data, while default-layout.hamlet
is more dynamic and can contain some conditional logic and similar stuff. Additionally, CSS and JavaScript files with the same name are automatically added to default-layout.hamlet
(this is because default-layout.hamlet
is treated as a usual Widget. We’ll talk about Widgets later in the series). Go ahead, change something – for instance, add a new div
in default-layout.hamlet
and some text in default-layout-wrapper.hamlet
and refresh the page. See how default-layout.hamlet
was inserted in place of ^{pageBody pc}
?
If you don’t, go ahead and call stack exec -- yesod devel
. Remember that stack
might be able to reload Hamlet templates, but is not really able to hot-swap actual Haskell code, so you might need to restart it, especially when adding new files.
That’s it for today – in the next episode we’re gonna create a template view for our projects.
Stay tuned!
code
more code
~~~~