How to highlight an active link using Snap?

Can someone give an example of highlighting an β€œactive” link in a menu on a site using Snap ? Or at least tell me how you do it - I have no idea.

In other web frames, I usually set a context variable called active , what the active page should be, and then my html just checks it:

 <ul> {% ifequal active "home" %} <li class="active"> {% else %} <li> {% endifqual %} <a href="/">Home</a> </li> {% ifequal active "about" %} <li class="active"> {% else %} <li> {% endifequal %} <a href="/about">About Us</a> </li> </ul> 

There are splices in hist, but I'm not sure how you use them to figure out what the current url is, or to set a context variable.

My decision

Thanks to @mightybyte and @Adam Bergmark, I decided the following:

Haskell Code:

 menuenuEntrySplice :: MonadSnap m => HeistT m Template menuEntrySplice = do requestPath <- lift $ withRequest (return . rqURI) node <- getParamNode let setActive n = if getAttribute "href" node == Just (decodeUtf8 requestPath) then setAttribute "class" "active" n else n let aNode = Element "a" [("href", fromMaybe "/" $ getAttribute "href" node)] $ [TextNode (nodeText node)] let liNode = setActive $ Element "li" [] [aNode] return [liNode] app :: SnapletInit App App app = makeSnaplet "app" "An snaplet example application." Nothing $ do .... addSplices [ ("menuEntry", liftHeist menuEntrySplice) ] return $ App hsa 

And now it is used in HTML:

 <ul class="nav"> <menuEntry href="/">Home</menuEntry> <menuEntry href="/contact">Contact</menuEntry> </ul> 

which produces:

 <ul class="nav"> <li class="active"> <a href="/">Home</a> </li> <li> <a href="/contact">Contact</a> </li> </ul> 
+4
source share
2 answers

At snapframework.com, we do this using javascript. At the end of site.js you will find this code that will add the appropriate class to the corresponding link.

 if ($.url.segment(0)) { $('.nav li.'+$.url.segment(0)).addClass('active'); } else { $('.nav .home').addClass('active'); } 

If you want to do this with Heist, you will need to use a paradigm slightly different from what your template uses. The whole point of Heist (with a strong static system like Haskell) is to ensure the greatest possible separation between presentation and business logic, so you cannot use Haskell constructs, such as loops or conditional expressions, directly in your templates. The answer is to create a new tag (implemented using splicing) that does exactly what you want. In your template, you will use it as follows:

 <ul> <menuLink href="/">Home</menuLink> <menuLink href="/about">About Us</menuLink> </ul> 

First of all, pay attention to how much cleaner it is. In your approach, you had to repeat the same code for each link. Here we can save the DRY pattern, and it reads pretty well.

To implement the menuLink tag, you need a splice splice that looks something like this:

 import Control.Monad.Trans.Class (lift) -- transformers import Data.Text.Encoding (decodeUtf8) import Text.XmlHtml (getAttribute, setAttribute, elementTag) menuLinkSplice :: MonadSnap m => HeistT m Template menuLinkSplice = do requestPath <- lift $ withRequest (return . rqURI) node <- getParamNode let addActive n = if getAttribute "href" n == Just (decodeUtf8 requestPath) then setAttribute "class" "active" n else n return [addActive (node { elementTag = "a" })] 

Then, to put it all together, you need to associate this splicing with the menuLink tag. In the Snap app, you do this with:

 addSplices [ ("menuLink", liftHeist menuLinkSplice) ] 

You can also find the blog post "Looping and Control Flow in Heist" (like some of my other posts with "heist" tag).

+4
source

As far as I can tell, Heist requires that you even enter that logic into Haskell, not Heist. See, for example, this example from the docs for Text.Templating.Heist :

 link :: Text -> Text -> X.Node link target text = X.Element "a" [("href", target)] [X.TextNode text] loginLink :: X.Node loginLink = link "/login" "Login" logoutLink :: Text -> X.Node logoutLink user = link "/logout" (T.append "Logout " user) loginLogoutSplice :: Splice MyAppMonad loginLogoutSplice = do user <- lift getUser return [maybe loginLink logoutLink user] 

Here, instead of passing the Bool to the template and letting the template execute if / then logic, all the logic runs at the Haskell level. This is the best answer I can give. I am a stranger to Heist; I know that you can give arguments for splicing, but it is not clear to me how to accomplish your specific task.

0
source

All Articles