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

Dmitry Kurochkin dmitry.kurochkin at gmail.com
Sun Feb 13 17:13:06 CET 2011


On Sun, 13 Feb 2011 17:44:33 +0200, Michael Snoyman <michael at snoyman.com> wrote:
> 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.
> 

I will try to send you some an example later. Unfortunately, I do not
have time for this right now. Playing with Yesod in my free time only.

> >> [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.
> 

This does not feel right in my case. Essentially, it moves part of
layout to widget. If I have many widgets to combine, e.g. mainMenu,
sideMenu, anotherCoolWidget, it becomes more complex. I think
introducing a separate layout (my original workaround) is a better
solution here. I just need to come up with a better name for it :)

    defaultLayout content = do
        mmsg <- getMessage
        pc <- widgetToPageContent $ do
            addCassius $(Settings.cassiusFile "default-layout")
            addWidget $(Settings.cassiusFile "inner-layout")
        hamletToRepHtml $(Settings.hamletFile "outer-layout")

> [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.
> 

Well, you have my vote for what it's worth :)

Regards,
  Dmitry

> Michael



More information about the web-devel mailing list