Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return empty field when Listfield has no elements to allow using ()$ … #348

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

jmatsushita
Copy link

…around a ()$ block.

This is my first ever Haskell pull request, so apologies for the lack of elegance. This PR is meant to allow using $if()$ blocks surrounding a $for()$ block in templates. It returns empty when a Listfield is empty. This helps for the following (quite common) pattern:

$if(list)$
  There is a list!
  $for(list)$
    It contains : $elem$
  $endfor$
$else$
  No list...
$endif$

@krsch
Copy link
Contributor

krsch commented May 16, 2015

This solution has several problems:

  1. It breaks compatibility.
  2. It introduces new overrides. Any empty list will be overridden by later contexts.

A better solution would be something like $if(notnull(list))$. It is possible to implement by modifying applyExpr in Hakyll.Web.Template though it looks a bit ugly.

A better choice might be implementing functions over lists in templates. But a change to the definition of Context would be needed which is pretty fundamental.

The simplest solution I can think of is to define listField' with the same type as listField. It would create not only a list field, but also a bool field with key equal key ++ "?" that checks for emptiness of the list. It solves this particular problem, but doesn't help to solve similar ones.

@jmatsushita
Copy link
Author

Hi @jaspervdj I just ran into this problem again trying simply check for empty strings in my template. Could it be that a maybeField with the same approach as the haskell Maybe would work? i.e. in $if(aMaybeField)$ it would just "pattern match" on Nothing or Just _ while with $aMaybeField$ it would return empty string for Nothing and value for Just value ?

@jaspervdj
Copy link
Owner

You can achieve this already in a reasonably clean way. The code I am using is based on the canonical example, i.e. the one generated by hakyll-init. In that site, we have the following templates/post-list.html:

<ul>
    $for(posts)$
        <li>
            <a href="$url$">$title$</a> - $date$
        </li>
    $endfor$
</ul>

Let's change that to:

$if(posts)$
    <ul>
        $for(posts)$
            <li>
                <a href="$url$">$title$</a> - $date$
            </li>
        $endfor$
    </ul>
$else$
    <p>There are no posts.</p>
$endif$

Now, an $if(...)$ in Hakyll templating will fail when an error occurs. We can achieve that behaviour by changing site.hs slightly as well. Currently we have, when generating the index page:

let indexCtx =
        listField "posts" postCtx (return posts) `mappend`
        constField "title" "Home"                `mappend`
        defaultContext

The code spitting out the list is simply return posts -- which will never raise an error. So we simply change that to:

let indexCtx =
        listField "posts" postCtx
            (if null posts then fail "No posts" else return posts)
                                  `mappend`
        constField "title" "Home" `mappend`
        defaultContext

Does this work for your use case?

@jmatsushita
Copy link
Author

Hi Jasper,

Thanks for the answer. I was aware that this worked with listFields (EDIT: it just worked because I use the hack in this pull request, your solution seems less hackish while still I wonder: is there any reason to not make this a default behavior?) but I’m trying to make this work for other type of fields… So for instance in your example how can I wrap the - $date$ in an $if(date) ? And to my point about making this work for other types of field, how do you make it work with $url$ or $title$ fields. So for instance, this would be fairly common (and fairly verbose for a common case of combined field templating):

<ul>
    $for(posts)$
        <li>
            $if(url)$
              <a href="$url$">$title$</a> - $date$
            $else$
              $if(title)$
                $title$ $if(date)$ - $date$ $endif$
              $else$
                $if(date)$ $date$ $endif$
              $endif$
            $endif$
        </li>
    $endfor$
</ul>

Another way might be to allow expressions in the if argument such as $if($date$ == 0 || $title$ == '')$...

If that's of any use, here's an example of what I'm trying to do which doesn't work because $twitter$ et al are strings.

Thanks a lot for your patience (and let me know if it's better to move this into a proper issue rather than stay on this half-baked pull request)!

Jun

@jaspervdj
Copy link
Owner

This sort of behaviour should work -- if twitter is missing from the metadata, then $if(twitter)$ should evaluate to false.

This does not work for date, though, if you are using defaultContext, since that will try to infer a date from your post file name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants