[web-devel] [Yesod] widgets in default layout

Michael Snoyman michael at snoyman.com
Sun Feb 13 16:44:33 CET 2011


On Sun, Feb 13, 2011 at 5:28 PM, Dmitry Kurochkin
<dmitry.kurochkin at gmail.com> wrote:

> Still the argument remains. I want to have all menu-related code in a
> separate module.

OK. As I said, this is possible, and it sounds like you already got
90% of the way there. I don't know what's holding you up until I see
your code. My initial reaction is still that this is a bad idea in
general, though I *am* reconsidering that position. It might make
sense to alter the approach used by the scaffolded site.

>> [snip]
>>
>> >> > 2. Widget does not work in default layout.
>> >> >
>> >> > I guess this is a known and expected behavior. My feeling is that
>> >> > hamletToRepHtml can not embed widgets because it may be too late to add
>> >> > cassius and julius. As a workaround I split default layout into outer
>> >> > and inner layout. Outer layout renders just HTML <head> and <body>.
>> >> > While outer layout is rendered as a widget that embeds the actual page
>> >> > contents. Since outer layout is rendered as a widget, it may embed other
>> >> > widgets like menu.
>> >> >
>> >> > I imagine that hamletToRepHtml could render all embedded widgets before
>> >> > the main body. Though, it may be difficult to implement, have
>> >> > performance or other issues. Anyway, I think it is not uncommon to
>> >> > include a widget in default layout. So Yesod should provide an easy way
>> >> > to do it.
>> >>
>> >> You should try looking at the scaffolded site: the function you want
>> >> to use is widgetToPageContent[1]. It converts a complete Widget into
>> >> the individual pieces that you need.
>> >>
>> >
>> > Yes, widgetToPageContent is used to convert the widget from handler and
>> > produces a set of pieces for page generation (pc). If I use it for a
>> > menu widget, I will get another PageContent (pc1). Now I need to take
>> > body from pc1, and merge other pieces of pc1 with pc. E.g. menu widget
>> > can produce javascript and CSS which needs to be merged with the main
>> > PageContent. I did not find an existing function to do this. Did I miss
>> > it?
>>
>> Just combine the two widgets and call widgetToPageContent once:
>>
>>     defaultLayout widget = do
>>         pc <- widgetToPageContents $ do
>>             menuWidget
>>             widget
>>         hamletToRepHtml ...
>>
>> You can see an example of this in the Yesod docs site[1].
>>
>
> I thought this would result in menuWidget placed directly before the
> main widget body, right? In many cases simple concatenation is not
> enough.

Then just modify menuWidget to take a Widget as an argument:

menuWidget :: GWidget s m () -> GWidget s m ()
menuWidget w = do
    earlierStuff
    w
    laterStuff

This is the very reason why we have polymorphic hamlet, so you can even do:

menuWidget w = [$hamlet|
<p>Header
^{w}
<p>Footer
|]

In fact, if you put that into a separate menu-widget.hamlet file, you
might get the results you were looking for originally all the
back-bending.

[snip]

>> Just to confirm: are you talking for general widgets, or just widgets
>> to be called from defaultLayout? As I mention above, the former can
>> easily be put in separate modules, the latter would require more work.
>>
>
> I am talking about defaultLayout widgets. In general, reasons for
> putting widgets into separate modules does not depend on whether they
> are used in defaultLayout. Though, I agree that the fact that only
> defaultLayout widgets must be put into the Yesod instance module
> somewhat improves the situation.
>
> IMO from user point of view it does not matter much if widget is used in
> handler or in defaultLayout. In most cases, at least. I just create a
> menu widget and want be able to use it anywhere (even both in handlers
> and defaultLayout). E.g. in my case the only difference between handler
> and defaulLayout widget is the type signature, implementation does not
> change.

Nonetheless, these *are* the rules that exist in Haskell. To a certain
extent, separating out mkYesodDispatch into Controller.hs is a similar
hack to this. In that case, I think that the added benefit of separate
handler modules definitely justifies using this technique. I'm simply
not (yet) convinced that the benefit here is big enough to warrant a
similar approach.

Michael



More information about the web-devel mailing list