
On Sun, Feb 13, 2011 at 5:28 PM, Dmitry Kurochkin
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