Lookup query parameters in Yesod - haskell

I just initialized a Yesod project (no database) using yesod init.
My HomeR GET handler looks like this:
getHomeR :: Handler Html
getHomeR = do
(formWidget, formEnctype) <- generateFormPost sampleForm
let submission = Nothing :: Maybe (FileInfo, Text)
handlerName = "getHomeR" :: Text
defaultLayout $ do
aDomId <- newIdent
setTitle "Welcome To Yesod!"
$(widgetFile "homepage")
When using yesod devel, I can access the default homepage at http://localhost:3000/.
How can I modify the handler listed above to retrieve (and display) a HTTP GET query parameter like id=abc123 when accessing this URL:
http://localhost:3000/?id=abc123
Note: This question was answered Q&A-style and therefore intentionally doesn't show research effort!

I'll show two different methods to achieve this. For both, you'll need to add this code to your template, e.g. in homepage.hamlet:
Note that it is not guaranteed there is any id parameter present when accessing the URL, therefore the type resulting from both methods is Maybe Text. See the Shakespearean template docs for a detailed explanation of the template parameters.
Method 1: lookupGetParam
The easiest way you can do this is using lookupGetParam like this:
idValueMaybe <- lookupGetParam "id"
When using the default setting as generated by yesod init, idValueMaybe needs to be defined in both getHomeR and postHomeR if idValueMaybe is used in the template.
Your HomeR GET handler could look like this:
getHomeR :: Handler Html
getHomeR = do
idValueMaybe <- lookupGetParam "id"
(formWidget, formEnctype) <- generateFormPost sampleForm
let submission = Nothing :: Maybe (FileInfo, Text)
handlerName = "getHomeR" :: Text
defaultLayout $ do
aDomId <- newIdent
setTitle "Welcome To Yesod!"
$(widgetFile "homepage")
Method 2: reqGetParams
Instead of looking up the query parameters by name, you can also retrieve a list of query key/value pairs using reqGetParams. This can be advantageous in certain situations, e.g. if you don't know all possible keys in advance. Using the standard lookup function you can easily lookup a certain key in that list.
The relevant part of your code could look like this:
getParameters <- reqGetParams <$> getRequest
let idValueMaybe = lookup "id" getParameters :: Maybe Text
Your getHomeR could look like this:
getHomeR :: Handler Html
getHomeR = do
getParameters <- reqGetParams <$> getRequest
let idValueMaybe = lookup "id" getParameters :: Maybe Text
(formWidget, formEnctype) <- generateFormPost sampleForm
let submission = Nothing :: Maybe (FileInfo, Text)
handlerName = "getHomeR" :: Text
defaultLayout $ do
aDomId <- newIdent
setTitle "Welcome To Yesod!"
$(widgetFile "homepage")

Related

Get a session value with lookupSession

I try to put a session value in a variable to display it in my .hamlet but it does not focntion!
getEtatR :: Handler Html
getEtatR = do
mSessionValue <- lookupSession "myKey"
let myValue = mSessionValue :: Maybe Text
defaultLayout $ do
aDomId <- newIdent
setTitle "mon titre"
$(widgetFile "etatWidget")
I need #{myValue} to put it in my etat.hamlet
The problem is the type of myValue, which is Maybe Text. In order for a variable to show up in the template, it has to be an instance of Text.Blaze.ToMarkup.... So Text, String, or Int would all work, but "Maybe a" does not.
There are many ways to convert a "Maybe Text" to a ToMarkup. If you know for sure that the Maybe will not be a "Nothing", just strip the maybe using fromJust (imported from Data.Maybe).... But beware that if it ever does come up as a Nothing the program will crash. Similarly you could use a case statement to fill in the Nothing case, like this
myVariable = case mSessionValue of
Just x -> x
Nothing -> "<No session value>"
You can also do a quick check by converting mSessionValue to a string using show.
The following works for me....
getEtatR :: Handler Html
getEtatR = do
mSessionValue <- lookupSession "myKey"
let myValue = show mSessionValue
defaultLayout $ do
aDomId <- newIdent
setTitle "mon titre"
$(widgetFile "etatWidget")
using etatWidget.hamlet
<h1>#{myValue}
If all you want is to display the value and get it out of Maybe, you can do this directly inside the hamlet
$maybe val <- mSessionValue
<p>#{val}
$nothing
<p>No Value Set

Yesod - Extract session data (non-String), store it and use it

Hi there.
Here is the code I'm trying to make work :
getGameR :: Handler Html
getGameR = do
sess <- getSession
defaultLayout $ do
setTitle "Game"
$(widgetFile "hamletFile")
where
person = read $ fromJust $ Map.lookup "person" sess :: Person
data Person = Person
{
name :: Text
}
deriving (Show, Read)
The error is the following:
Handler/MyHandler.hs:87:56: Not in scope: `sess'
What I'm trying to do, is to extract data from Yesod Session (data of type Person) and store it inside 'person', to be able to use it inside the hamlet file.
Is there a way to get around that error?
If it's not possible, can you suggest another way around?
Thanks in advance.
sess is local to the do block, and thus it is not in scope in the person definition. As far as that error goes, using let inside the do block should be enough:
getGameR :: Handler Html
getGameR = do
sess <- getSession
let person = read $ fromJust $ Map.lookup "person" sess :: Person
defaultLayout $ do
setTitle "Game"
$(widgetFile "hamletFile")
If you just want to lookup single value, consider using lookupSession instead. Also, fromJust throws exception if key is not in session, you might use fromMaybe:
getGameR :: Handler Html
getGameR = do
mbPersonName <- lookupSession "person"
let defaultPerson = Person "anonymous"
let person = fromMaybe defaultPerson (readMaybe .unpack =<< mbPersonName) :: Person
defaultLayout $ do
setTitle "Game"
$(widgetFile "hamletFile")
Here is my helpers for dealing with session:
import Text.Read (readMaybe)
import Data.Maybe (fromMaybe)
readSession :: Read a => Text -> Handler (Maybe a)
readSession name = do
textValue <- lookupSession name
return (readMaybe . unpack =<< textValue)
readSessionDef :: Read a => a -> Text -> Handler a
readSessionDef def name = do
mbValue <- readSession name
return $ fromMaybe def mbValue
readSession reads anything that can be readden and returns a Maybe. readSessionDef returns default value if such key is not present in session:
getGameR :: Handler Html
getGameR = do
person <- readSessionDef (Person "anonymous") "person"
defaultLayout $ do
setTitle "Game"
$(widgetFile "hamletFile")

yesod how to solve this error?

I am just started with Yesod, I am following this tutorial:http://yannesposito.com/Scratch/en/blog/Yesod-tutorial-for-newbies/
I get this error:
Handler/Blog.hs:32:17:
Couldn't match type `handler' with `GHandler App App'
`handler' is a rigid type variable bound by
the type signature for postBlogR :: handler RepHtml
at Handler/Blog.hs:29:14
Expected type: handler [Entity Article]
Actual type: GHandler App App [Entity Article]
In a stmt of a 'do' block:
articles <- runDB $ selectList [] [Desc ArticleTitle]
In the expression:
do { articles <- runDB $ selectList [] [Desc ArticleTitle];
(articleWidget, enctype) <- generateFormPost entryForm;
defaultLayout $ do { $(widgetFile "articles") } }
In an equation for `postBlogR':
postBlogR
= do { articles <- runDB $ selectList [] [Desc ArticleTitle];
(articleWidget, enctype) <- generateFormPost entryForm;
defaultLayout $ do { ... } }
this is my Blog.hs:
module Handler.Blog
( getBlogR
, postBlogR
)
where
import Import
-- to use Html into forms
import Yesod.Form.Nic (YesodNic, nicHtmlField)
instance YesodNic App
entryForm :: Form Article
entryForm = renderDivs $ Article
<$> areq textField "Title" Nothing
<*> areq nicHtmlField "Content" Nothing
-- The view showing the list of articles
getBlogR :: Handler Html
getBlogR = do
-- Get the list of articles inside the database.
articles <- runDB $ selectList [] [Desc ArticleTitle]
-- We'll need the two "objects": articleWidget and enctype
-- to construct the form (see templates/articles.hamlet).
(articleWidget, enctype) <- generateFormPost entryForm
defaultLayout $ do
$(widgetFile "articles")
postBlogR :: handler RepHtml
postBlogR = do
-- Get the list of articles inside the database.
articles <- runDB $ selectList [] [Desc ArticleTitle]
-- We'll need the two "objects": articleWidget and enctype
-- to construct the form (see templates/articles.hamlet).
(articleWidget, enctype) <- generateFormPost entryForm
defaultLayout $ do
$(widgetFile "articles")
my routes:
/static StaticR Static getStatic
/auth AuthR Auth getAuth
/favicon.ico FaviconR GET
/robots.txt RobotsR GET
/ HomeR GET POST
/echo/#Text EchoR GET
/mirror MirrorR GET POST
/blog BlogR GET POST
/blog/#ArticleId ArticleR GET
and my models:
User
ident Text
password Text Maybe
UniqueUser ident
Email
email Text
user UserId Maybe
verkey Text Maybe
UniqueEmail email
Article
title Text
content Html
deriving
-- By default this file is used in Model.hs (which is imported by Foundation.hs)
I think, you just have to fix your type signature for postBlogR to Handler RepHtml. Names starting with lower case letters are reserved for type variables in type signatures, so it cannot be deduced right here.

How to render Html algebraic data type in Yesod

I'm trying to use CKEditor in my Yesod application. Data from CKEditor is returned to the server via Textarea, I then store it as Html in database. My problem is I do know know how to display the Html algebraic data type once I retrieve it from database in the handler. I've been reading this tutorial, but it will only display the Html as a big long string, not as markup.
Note: titleA and contextA are the variable that I want to display in article-local-display.
contextA is the Html algebraic data type
PS: Do I need to transform Html to hamlet in order to render?
module Handler.Article where
import Import
import Data.Text (unpack)
import Data.Time (getCurrentTime)
import Data.String (fromString)
getArticleR :: Handler RepHtml
getArticleR = do
defaultLayout $ do
setTitle "Search For Article"
$(widgetFile "header")
$(widgetFile "article")
postArticleR :: Handler RepHtml
postArticleR = do
redirect ArticleR
getArticleLocalR :: Handler RepHtml
getArticleLocalR = do
articles <- runDB $ selectList ([] :: [Filter Article]) [Desc ArticleTime]
defaultLayout $ do
setTitle "Local Article"
$(widgetFile "header")
$(widgetFile "article-local")
getArticleLocalDisplayR :: ArticleId -> Handler RepHtml
getArticleLocalDisplayR articleId = do
article <- runDB $ get404 articleId
let titleA = articleTitle article
contextA = articleContext article
defaultLayout $ do
setTitle "Article"
$(widgetFile "header")
$(widgetFile "article-local-display")
getArticleLocalCreateR :: Handler RepHtml
getArticleLocalCreateR = do
defaultLayout $ do
setTitle "Create article"
addScript $ StaticR ckeditor_ckeditor_js
$(widgetFile "header")
$(widgetFile "article-local-create")
postArticleLocalCreateR :: Handler RepHtml
postArticleLocalCreateR = do
articleForm <- runInputPost $ ArticleForm <$> ireq textField "title" <*> ireq textareaField "editor1"
now <- liftIO getCurrentTime
let titleA = title articleForm
html = toHtml $ unTextarea $ context articleForm
_ <- runDB $ insert $ Article titleA html now
redirect ArticleLocalR
data ArticleForm = ArticleForm {
title :: Text,
context :: Textarea
}
deriving Show
models file:
Article
title Text
context Html
time UTCTime
deriving
article-local-display.hamlet
<h1>#{titleA}
<article>#{contexA}
So I changed the context from Html to Text.
Article
title Text
context Text
time UTCTime
deriving
Then added preEscapedText when using the value.
let contextA = preEscapedText $ articleContext article
Now it's displaying properly.

How is it used translated messages inside a Haskell file?

I have searched in internet, in the Yesod Web ebook and other tutorials (Yesod Tutorial) but I have not been able to clarify this problem. I am using the scaffolded site.
I have a handler, inside it returns a value, the email if the user is authenticated or a string if he is not. What I want is to return the localized message instead the string "(Unknown User ID)". My problem is to use a value from the message file (ex. MsgHello), if I do this, it returns errors like:
Couldn't match expected type AppMessage' with actual typeText'
I have tried using (show MsgHello) or (pack MsgHello), even calling msg <- getMessageRender but I have not been able to do what I expect. If you have any suggestions, they are welcome.
Thanks!!
PD: This is part of the code that I am working on, line :
getUserProfileR :: Handler RepHtml
getUserProfileR = do
maid <- maybeAuth
let user = case maid of
Nothing -> "(Unknown User ID)"
Just (Entity _ u) -> userEmail u
defaultLayout $ do
setTitleI MsgUserProfile
$(widgetFile "nhUserProfile")
Thanks to Tickhon Jelvis for pointing out those web pages, also I found this one: Poly Hamlet i18n where I was able to get the solution to the problem.
So, if I would like to use a localized message, I would do:
getUserProfileR :: Handler RepHtml
getUserProfileR = do
maid <- maybeAuth
msg <- getRenderMessage
let user = case maid of
Nothing -> msg MsgNoUser --"(Unknown User ID)"
Just (Entity _ u) -> userEmail u
defaultLayout $ do
setTitleI MsgUserProfile
$(widgetFile "nhUserProfile")
Also remember that there is a helper function "setTitleI" which takes directly a Msg value and avoids the use of "msg MsgThisPageTitle"
My understanding of the I18N module is that you want to take your AppMessage value and use renderMessage on it.
You need to pass in a type specifying your translation type and a list of languages as well as your message. The translation type is created using the mkMessage function and the list of languages looks something like ["en-US", "en-GB", "fr"].

Resources