I am try to pass a parameter to a route at runtime inside a hamlet template.
buildFeedRow :: Item [Attribute Text] -> GWidget a a ()
buildFeedRow item = do
let f = unpackItem item
u :: Text = url f
[whamlet|
<tr>
<td>
<a href="#{url f}">#{url f}
<td>#{nextCrawlTime f}
<td>#{crawlDelay f}
<td>
<a href="#{DeleteFeedR u}">Delete
<td>Edit |]
It fails with a type error.
Couldn't match type `Route a' with `ConsoleRoute'
Expected type: ConsoleRoute -> [(Text, Text)] -> Text
Actual type: Route a -> [(Text, Text)] -> Text
Expected type: GHandler
a a (ConsoleRoute -> [(Text, Text)] -> Text)
Actual type: GGHandler
a
a
(Data.Enumerator.Iteratee Data.ByteString.Internal.ByteString IO)
(Route a -> [(Text, Text)] -> Text)
In the first argument of `lift', namely `getUrlRenderParams'
In the first argument of `(>>=)', namely `lift getUrlRenderParams'
My question is how do you pass parameters to routes in a hamlet template.
I think the the type signature of your widget might be too generic. Since you're using a route, it now depends on your foundation type so it should have the type
buildFeedRow :: Item [Attribute Text] -> GWidget YourFoundation YourFoundation ()
Related
I can not figure out what types should go in my Foundation.hs when implementing type classes from the authentication plugin / it's use of the auth subsite:
I can feel that I am very close, but I lack understanding. I am simply trying to use a different layout for the login/registration pages.
In Foundation.hs:
instance YesodAuthSimple App where
type AuthSimpleId App = UserId
...
-- Works
onRegisterSuccess :: YesodAuthSimple site => AuthHandler site Html
onRegisterSuccess = authLayout $ [whamlet|
$newline never
<div>
<h1>You Registered successfully.
<p>
Some text here.
|]
-- Works when I do not write a type signature
loginTemplate toParent mErr = $(widgetFile "authpartials/login")
-- Does not work with or without type signatures
customAuthLayout widget = do
master <- getYesod
mmsg <- getMessage
muser <- maybeAuthPair
mcurrentRoute <- getCurrentRoute
pc <- widgetToPageContent $ do
$(widgetFile "custom-auth-layout")
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
The 432:15 is referring to the widgetToPageContent call.
In the type class definition Simple.hs:
class (YesodAuth site, PathPiece (AuthSimpleId site)) => YesodAuthSimple site where
type AuthSimpleId site
...
customAuthLayout :: WidgetFor site () -> AuthHandler site Html
...
I pasted in the definition of customAuthLayout from defaultLayout from Foundation.hs
Here is the error I get from GHC:
Foundation.hs:432:15: error:
• Could not deduce: m ~ HandlerFor App
from the context: MonadAuthHandler App m
bound by the type signature for:
customAuthLayout :: WidgetFor App () -> AuthHandler App Html
at src/Foundation.hs:(427,5)-(434,79)
‘m’ is a rigid type variable bound by
the type signature for:
customAuthLayout :: WidgetFor App () -> AuthHandler App Html
at src/Foundation.hs:(427,5)-(434,79)
Expected type: m (PageContent (Route App))
Actual type: HandlerFor App (PageContent (Route App))
• In a stmt of a 'do' block:
pc <- widgetToPageContent
$ do (do do (asWidgetT GHC.Base.. toWidget)
((blaze-markup-0.8.2.2:Text.Blaze.Internal.preEscapedText
GHC.Base.. Data.Text.pack)
"<!-- custom-auth-layout -->
<body class="d-flex align-items-center bg-auth border-top border-top-2 border-primary">")
....)
In the expression:
do master <- getYesod
mmsg <- getMessage
muser <- maybeAuthPair
mcurrentRoute <- getCurrentRoute
....
In an equation for ‘customAuthLayout’:
customAuthLayout widget
= do master <- getYesod
mmsg <- getMessage
muser <- maybeAuthPair
....
|
432 | pc <- widgetToPageContent $ do
| ^^^^^^^^^^^^^^^^^^^^^^^^...
I have used this tutorial successfully for normal (non-subsite pages) https://ersocon.net/cookbooks/yesod/html-and-seo/custom-layouts
But I am getting tripped up by the subsite types. I have read Michael Snoyman's very good old blog post on subsite types but I cannot understand GHC's error message.
I suspect either the type signature in Simple.hs is wrong, or I am missing something from the function definition.
Try to add liftHandler before widgetToPageContent:
...
pc <- liftHandler $ widgetToPageContent $ do
$(widgetFile "custom-auth-layout")
...
Key lines in the error message are:
Could not deduce: m ~ HandlerFor App
...
Expected type: m (PageContent (Route App))
Actual type: HandlerFor App (PageContent (Route App))
It is basically telling us that it expected a more generic type m, but instead it got a HandlerFor App. So the solution is simply to lift the call to widgetToPageContent using the liftHandler function.
To elaborate further, if we look at the type signature of the function widgetToPageContent, we see that it returns HandlerFor site (PageContent (Route site)). In this case, site instantiates to App, and that is the HandlerFor App (PageContent (Route App)) you see in the error message.
Similarly, your customLayout function returns AuthHandler site Html. AuthHandler is just a type synonym that constrains site to a type equivalent to HandlerSite m which is also an instance of YesodAuth. This also resolves to App, and that is why we get MonadAuthHandler App m and m (PageContent (Route App)) in the error message.
I'm working on a project that builds on the simple yesod template. I am new to functional programming, haskell and Yesod so it's probably something obvious to anyone with Yesod experience. At the moment I am trying to make github API calls using this library. I am getting some type issues and I'm not even sure how to start approaching solving them.
You can find my handler here.
Handler/Home.hs:43:19:
Couldn't match expected type ‘HandlerT
App IO (Either a0 GitHub.User)’
with actual type ‘GitHub.Request k0 GitHub.User’
In a stmt of a 'do' block:
possibleUser <- GitHub.userInfoForR "mike-burns"
In the expression:
do { maid <- maybeAuthId;
possibleUser <- GitHub.userInfoForR "mike-burns";
result <- either (("Error: " <>) . tshow) formatUser possibleUser;
defaultLayout
(do { (asWidgetT GHC.Base.. toWidget)
((blaze-markup-0.7.1.1:Text.Blaze.Internal.preEscapedText
GHC.Base.. Data.Text.pack)
"<p>Your current auth ID: ");
(asWidgetT GHC.Base.. toWidget) (toHtml (show maid));
(asWidgetT GHC.Base.. toWidget)
((blaze-markup-0.7.1.1:Text.Blaze.Internal.preEscapedText
GHC.Base.. Data.Text.pack)
"</p>\n");
.... }) }
Handler/Home.hs:44:38:
Couldn't match type ‘Text’ with ‘HandlerT App IO a1’
Expected type: a0 -> HandlerT App IO a1
Actual type: a0 -> Text
In the second argument of ‘(.)’, namely ‘tshow’
In the first argument of ‘either’, namely
‘(("Error: " <>) . tshow)’
Handler/Home.hs:44:45:
Couldn't match type ‘Text’ with ‘HandlerT App IO a1’
Expected type: GitHub.User -> HandlerT App IO a1
Actual type: GitHub.User -> Text
In the second argument of ‘either’, namely ‘formatUser’
In a stmt of a 'do' block:
result <- either (("Error: " <>) . tshow) formatUser possibleUser
The GitHub library seems to be about building requests, and running them. The userInfoForR does such a thing :
userInfoForR :: Name User -> Request k User
Once you have a request, you can run it with one of the following functions, depending if you need to authenticate or not:
executeRequest :: Auth -> Request k a -> IO (Either Error a)
executeRequest' :: Request RO a -> IO (Either Error a)
I don't know about this specific case, but let's say you don't need authentication. So, the following expression would do the trick:
executeRequest' (userInfoForR "mike-burns") :: IO (Either Error User)
Now, in order to use it in a Handler, you'll need to learn about the fact that Handler is an instance of MonadIO, and you can thus do:
euser <- liftIO (executeRequest' (userInfoForR "mike-burns"))
case euser of
Left rr -> ...
Right user -> ...
I'm trying to write a custom Yesod registration form. The problem I'm having is that when it gets to fvInput it seems like it's using App instead of Auth. I'm not quite sure how this should be handled and I can't seem to find the terminology for it. I've tried lifting in various ways within the form and I can only get it to throw different errors. Also, the only time this error is thrown is if I have the fvInput line, but if I remove that no error is thrown and it compiles correctly.
Code:
registrationForm :: Html -> MForm (HandlerT Auth (HandlerT App IO)) (FormResult UserForm, Widget)
registrationForm extra = do
(emailRes, emailView) <- mreq textField "" Nothing
let userRes = UserForm <$> emailRes
let widget = do
[whamlet|
#{extra}
^{fvInput emailView}
<input type=Submit value="Registration">
|]
return (userRes, widget)
Error:
Foundation.hs:169:30:
Couldn't match type ‘App’ with ‘Auth’
In the second argument of ‘(GHC.Base..)’, namely ‘toWidget’
In the expression: asWidgetT GHC.Base.. toWidget
In a stmt of a 'do' block:
(asWidgetT GHC.Base.. toWidget) (fvInput emailView)
Thank you in advance for any help!
Edit
Error with a lift before the mreq:
Foundation.hs:166:49:
Couldn't match type ‘HandlerT Auth (HandlerT App IO)’
with ‘transformers-0.4.2.0:Control.Monad.Trans.RWS.Lazy.RWST
(Maybe (Env, FileEnv), HandlerSite m0, [Lang]) Enctype Ints m0’
Expected type: HandlerT
Auth (HandlerT App IO) (FormResult Text, FieldView App)
Actual type: MForm m0 (FormResult Text, FieldView App)
In the second argument of ‘($)’, namely ‘mreq textField "" Nothing’
In a stmt of a 'do' block:
(emailRes, emailView) <- lift $ mreq textField "" Nothing
Your returned Widget type is actually:
WidgetT App IO ()
Thus there's a mismatch between the Widget (which lives in just App) and the emailView (which lives in the lifted HandlerT Auth (HandlerT App IO) monad).
To solve this problem:
Change you type signature to registrationForm :: Html -> MForm (HandlerT App IO) (FormResult UserForm, Widget)
At the usage site, you'll need to employ a lift most likely, but after you've already called the appropriate run function for the form
I'm making a Yesod subsite and am getting a type error in some Template Haskell-generated code:
Yesod\DataSource\Data.hs:19:1:
Couldn't match type `[Char]' with `Text'
Expected type: () -> ([Text], [(Text, Text)]) -> Maybe (Route DataSource)
Actual type: () -> ([[Char]], [(Text, Text)]) -> Maybe (Route DataSource)
In the first argument of `\ f_amMs x_amMt -> f_amMs () x_amMt ::
forall a_amMu.
(() -> ([Text], [(Text, Text)]) -> Maybe (Route a_amMu))
-> ([Text], [(Text, Text)]) -> Maybe (Route a_amMu)', namely
`helper_amMr'
In the expression:
\ f_amMs x_amMt -> f_amMs () x_amMt ::
forall a_amMu.
(() -> ([Text], [(Text, Text)]) -> Maybe (Route a_amMu))
-> ([Text], [(Text, Text)]) -> Maybe (Route a_amMu)
helper_amMr
The problem is clear, but I don't understand why it's generating incorrect code.
The issue occurs in this TH call:
mkYesodSubData "DataSource" [parseRoutes|
/ SubHomeR GET
/datasource DataSourceInputR POST GET
|]
Specifically, it is caused by the line:
/datasource DataSourceInputR POST GET
Removing this line fixes the issue.
I'm using Stackage LTS 1.15:
remote-repo: stackage-lts-1.15:http://www.stackage.org/snapshot/lts-1.15
And I'm inside a cabal sandbox.
Here are the relevant files: https://gist.github.com/BeerendLauwers/774cc432c3ada5b597e1
Any idea?
I think that the generated code expects that you have the OverloadedStrings extension enabled in your source file. Try adding
{-# LANGUAGE OverloadedStrings #-}
to the top of the source file where you splice in the Template Haskell code (i.e. Data.hs).
If giving fancyTextField an explicit type will fix this problem, which type do I give it? It expects something in the form of Field m Text, but I'm not sure what m to substitute with.
Here's the error message:
Couldn't match type `HandlerSite m0' with `App'
The type variable `m0' is ambiguous
Possible cause: the monomorphism restriction applied to the following:
fancyTextField :: Field m0 Text
(bound at Widget/Input/Text.hs:13:1)
Probable fix: give these definition(s) an explicit type signature
Expected type: [Text]
-> [FileInfo]
-> m0 (Either (SomeMessage (HandlerSite m0)) (Maybe Text))
Actual type: [Text]
-> [FileInfo] -> m0 (Either (SomeMessage App) (Maybe Text))
In the `fieldParse' field of a record
In the expression:
Field
{fieldParse = parseHelper $ Right, fieldView = textInput,
fieldEnctype = UrlEncoded}
In an equation for `fancyTextField':
fancyTextField
= Field
{fieldParse = parseHelper $ Right, fieldView = textInput,
fieldEnctype = UrlEncoded}
And here's the code:
module Widget.Input.Text where
import Import
-- This is text Field calling the textInput widget below.
fancyTextField = Field {
fieldParse = parseHelper $ Right,
fieldView = textInput,
fieldEnctype = UrlEncoded
}
type FieldWidget =
Text -- Id.
-> Text -- Name.
-> [(Text, Text)] -- Attributes.
-> Either Text Text -- Value.
-> Bool -- Required?
-> Widget
textInput :: FieldWidget
textInput i name attrs val req = do
-- The following works:
-- [whamlet|<div>Test!|]
-- But the following doesn't!
$(widgetFile "fancy")
I believe you want to use Handler in place of m.