Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Can I use R variables in quarto YAML? #1391

Closed
giabaio opened this issue Jul 14, 2022 · 30 comments
Closed

Can I use R variables in quarto YAML? #1391

giabaio opened this issue Jul 14, 2022 · 30 comments
Milestone

Comments

@giabaio
Copy link
Contributor

giabaio commented Jul 14, 2022

I may be missing something obvious, but I would like to do that. I'm working on a quarto slide presentation (formatted in revealjs) and I have defined a bunch of objects in the params variable in my YAML

---
...
params:
   title: "My title"
   author: Gianluca Baio
   date: 27 November 2022
   institute: "[My work](Their website)"
   conference: "Where I'm speaking"
   location: "What town"
...
---

HERE GOES THE REST OF MY SLIDES

What I would like to do is to use the elements of params to feed to the footer that I want to use for the slides, something like

...
format: 
   revealjs:
      footer: `r params$author` | `r params$title` | `r params$date`

but it seems like I can't use the

`r command`

tag inside the yaml, as it returns an error

bad indentation of a mapping entry at line 9, column 10:
       footer: `r params$author`

Like I said, it's possible I am not doing this right --- but can anyone explain why and how I can do it instead?

Thanks
Gianluca

@mcanouil
Copy link
Collaborator

mcanouil commented Jul 14, 2022

Hi,

in theory, you have to use the keyword !expr as for the yaml chunk options and described in https://quarto.org/docs/computations/r.html#chunk-options

@giabaio
Copy link
Contributor Author

giabaio commented Jul 14, 2022 via email

@mcanouil
Copy link
Collaborator

I tried it and it does not work as for rmarkdown ...

---
params:
  title: "My title"
  author: Gianluca Baio
  date: 27 November 2022
  institute: "[My work](Their website)"
  conference: "Where I'm speaking"
  location: "What town"
title: !expr params$title
format: revealjs
---

produced an error:

Error: object 'params' not found
Error in yaml::yaml.load(..., eval.expr = TRUE) : 
  Could not evaluate expression: params$title
Calls: .main ... parse_yaml_front_matter -> yaml_load -> <Anonymous>
Execution halted

@giabaio
Copy link
Contributor Author

giabaio commented Jul 14, 2022

Nope --- I've tried again. I can specify in my YAML

---
params:
   title: Title here
   shorttitle: Short title here
   author: Gianluca Baio
   date: 27 November 2022
   institute: "[My department](the website) | My institution"
   conference: "Some conference"
   location: "Some place"
   shortlocation: "A shorter version of the place"
format: 
   revealjs:
     header-includes: |
      <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" rel="stylesheet"/>
     theme: [ucl-stats.scss]
     slide-number: true
     chalkboard: 
      buttons: false
      theme: whiteboard
     preview-links: auto
     width: 1800
     height: 1000
     footer: |
        &copy; !expr params$author
# Includes various LaTeX macros
     include-in-header: "latex_macros.html"
     css: styles.css
from: markdown+emoji
---

## Just to check that the text gets parsed OK
`r rmarkdown::metadata$params$author`

but what happens is that the footer is parsed "verbatim", so it appears as

© !expr params$author

but the text is parsed correctly in the slide body (so it turns the object params$author into my name, as given in the YAML).

I don't know if there's an issue with how the footer is defined --- in that it doesn't allow Rmarkdown, but only markdown? If so, would it be possible to implement a different strategy? I can, for example, create a footer for a specific slide, something like

...
::: footer
&copy; `r rmarkdown::metadata$params$author | 
`r some_function_to_add_my_twitter()` `r some_function_to_add_my_github()` | 
`r rmarkdown::metadata$params$shorttitle` |
`r rmarkdown::metadata$params$shortlocation` |
`r rmarkdown::metadata$params$date`
:::

and that works perfectly. But then I would need to copy the same text for each slide, which is obviously not ideal...

Any suggestions?

Many thanks again!
Gianluca

@mcanouil
Copy link
Collaborator

mcanouil commented Jul 14, 2022

You can't mix expression and string, it's one or the other.
For your last example, it should be: footer: !expr paste("&copy;", params$author).
But anyway, currently it seems params can't be used in the yaml header of a Quarto document.

@giabaio
Copy link
Contributor Author

giabaio commented Jul 14, 2022

I tried it and it does not work as for rmarkdown ...

---
params:
  title: "My title"
  author: Gianluca Baio
  date: 27 November 2022
  institute: "[My work](Their website)"
  conference: "Where I'm speaking"
  location: "What town"
title: !expr params$title
format: revealjs
---

produced an error:

Error: object 'params' not found
Error in yaml::yaml.load(..., eval.expr = TRUE) : 
  Could not evaluate expression: params$title
Calls: .main ... parse_yaml_front_matter -> yaml_load -> <Anonymous>
Execution halted

Interesting --- it works if you do

title: "`r params$title`"

(so enclosed in quotes). But it fails if you try with !expr...

@giabaio
Copy link
Contributor Author

giabaio commented Jul 14, 2022

You can't mix expression and string, it's one or the other. For your last example, it should be: footer: !expr paste("&copy;", params$author). But anyway, currently it seems params can't be used in the yaml header of a Quarto document.

If I try your suggestion, the resulting footer is processes but not rendered as I would like, ie it is like this:

!expr paste(‘©’, params$author)

(It does also work with

"`r paste('&copy;', params$title)`"

This makes me think that the problem is with footer (and that actually you can use

`r ...`

in the yaml)?

@mcanouil
Copy link
Collaborator

mcanouil commented Jul 14, 2022

This is confusing ... 😅 i.e., having to switch between !expr ... and "`r ...`" depending on if you are modifying document yaml or yaml chunk option.
It's related to #606 and #1334

It seems that all the yaml options are not parsed the same way ...

@cderv
Copy link
Collaborator

cderv commented Jul 15, 2022

In a way, the !expr vs `r ...` difference is the same as with R Markdown. It was already the case where

  • YAML fields processed by rmarkdown before knitting required using !expr as fields are loading in R using yaml package for rmarkdown process to use them.
  • YAML fields only processed by Pandoc (as pandoc variable) like title or date could use knitr inline code chunk, because the YAML fields were not processed before knitting, and not processed in R.

The latter is why title: "`r params$title`" works but not with !expr as not loaded by R.

With Quarto, the logic is quite similar: knitr inline code chunk can't be used as fields will be processed by Quarto directly.
Summary for here is quite good: #606 (comment)

So yes, for now, I am not sure it is possible to use params in other YAML configuration, probably because of the the order or evaluation. params are really designed to be used computation engine (Jupyter and Knitr) as the doc located in computation engine section shows: https://quarto.org/docs/computations/parameters.html

Quarto introduces Variables (https://quarto.org/docs/authoring/variables.html#var) that can be used in YAML fields too, only in projects for now though: #1252

Did you already tried using variables ?

But in short, to answer your issue question: "Can I use R variables in quarto YAML?", the answer is no you cannot use R variables as Quarto will need the value of the variable and the YAML header is not processed by knitr.

Maybe we can find a way to make params accessible as variables though ? Or at least improve using parameterized YAML value .

@giabaio
Copy link
Contributor Author

giabaio commented Jul 15, 2022

Thank you for this. I'll look at the summary you mention and the other bits and pieces. I guess the specific question is probably a bit wider than the reason why I asked it, which is in fact: "can I construct the footer in a quarto presentation by combining elements from the yaml?"

In a way, the answer to this is yes --- I have done it, but this means having to replicate the footer on each slide... It is probably beyond my understanding/knowledge of reveal/javascript/html/css/scss etc, but I guess there probably are ways to "inject" this compiled code onto every slide?

I made a comment on #809 last night --- I was trying to hack a function that can be used for xaringan presentations to specify an image to be used as logo, as well as its features and where to position it, and then use some javascript (I think!) to make sure that that code is replicated on every slide. You pass that as an include_after_body and then you've basically created a shortcut that avoids having to replicate that same code for every slide. In addition, I haven't quite figured out how to do it with reveal/quarto slides, but I think you can define classes (=slides) over which you don't want the code/logo to be included, which may be handy...

I wonder whether something similar could be used for the footer issue?
Gianluca

@mcanouil
Copy link
Collaborator

With shortcodes and meta (https://quarto.org/docs/authoring/variables.html#meta) you can almost do the footer part, but I can't retrieve the author name ...
@cderv Is it a bug in the meta shortcode for author (i.e., author and any subkey including the author.affiliation from the documentation) or am I missing something?
image

@cderv
Copy link
Collaborator

cderv commented Jul 15, 2022

In a way, the answer to this is yes --- I have done it, but this means having to replicate the footer on each slide... It is probably beyond my understanding/knowledge of reveal/javascript/html/css/scss etc, but I guess there probably are ways to "inject" this compiled code onto every slide?

You mean the div solution shown in #1391 (comment) ?
Regarding reducing copy pasting, Quarto has an "includes" feature: https://quarto.org/docs/authoring/includes.html
You can set the content in an external file, and you should be able to include it in all slide using the shortcode.
Leveraging knitr chunks and its option would be a way to also only copy paste the code for insertion and not the whole block.

Are the value in params you are setting only useful for the YAML footer ? and not used in R code chunk ?
Because, maybe it would be more suited to use https://quarto.org/docs/authoring/variables.html#var instead.
You should try it.

You could also leverage Lua filters to add some component in each slides (https://quarto.org/docs/extensions/filters.html).
include_in_header strategy may not work for footer because of the way it created in Quarto.

In addition, I haven't quite figured out how to do it with reveal/quarto slides, but I think you can define classes (=slides) over which you don't want the code/logo to be included, which may be handy...

Lua filters could be the solution, otherwise some JS inserted in header to remove the logo when class is detected. But the latter would be done in browser and not before the documented is rendered to HTML.

@cderv
Copy link
Collaborator

cderv commented Jul 15, 2022

@mcanouil this is because author keys is processed specifically in Quarto (we do some normalization) and the metadata key will not be the one you used at the end. We don't have documentation ready on that yet but will be soon.

Maybe this is a bug also and we don't put back the author field to be accessible in the meta shortcode. If you build a simple reproducible example with html format, you can open a new issue.

@giabaio
Copy link
Contributor Author

giabaio commented Jul 15, 2022

With shortcodes and meta (https://quarto.org/docs/authoring/variables.html#meta) you can almost do the footer part, but I can't retrieve the author name ... @cderv Is it a bug in the meta shortcode for author (i.e., author and any subkey including the author.affiliation from the documentation) or am I missing something? image

Thanks --- that's helpful. I think the issue is that you can only use "standard" fields in the meta shortcodes? I know this is getting a bit more complex/specific than the original question let out (and apologies for this!), but basically, I think the reason for wanting to use params is so that I can create a separate title page, which I can then process and format the way I want to.

For instance, I've created a separate title-page.qmd file, reading like this:

:::: {.title-slide}
::: {.title}
`r rmarkdown::metadata$params$title`
:::

::: {.author}
`r rmarkdown::metadata$params$author`
:::

::: {.institute}
`r rmarkdown::metadata$params$institute`
:::

::: {.social}
A bunch of `html`/`R` code to produce icons + links to my social accounts
:::

::: {.conference}
`r rmarkdown::metadata$params$conference`, `r rmarkdown::metadata$params$location` 
:::

::: {.date}
`r if(!is.null(rmarkdown::metadata$params$date)){rmarkdown::metadata$params$date} else {format(Sys.Date(),"%e %B %Y")}`
:::

A bunch of `R` code that defines the sticky post-its with extra icons & text

::: {.logo-stats}
:::

::: footer
# Because I don't want the footer in the title page --- this doesn't work perfectly because the first time you open the slides
# the "overall" footer still shows...
:::

::::

and defined a suitable class .title-slide with various components (.logo-stats, .date, .conference, .social, etc) that define how each should be formatted/typeset. In my main .qmd file, after the yaml (which does not contain the fields title and author so as to not create the default title page), I then load this as

{r include=FALSE}
source("setup.R")

{r child="title-slide.qmd"}

(setup.R contains a bunch of functions I have written to generate the "social" text + other utilities I use in other parts of the slides). This process generates the following title page
image
which is what I want.

@cderv: yes I meant #1391 (comment). When I create that footer, all works well. I haven't played with vars yet, but can have a go...

@cderv
Copy link
Collaborator

cderv commented Jul 15, 2022

@giabaio I just want to add that you are not really using the parameters feature by using rmarkdown$metatata.
Using are using an internal R Markdown mechanism within the Quarto context. If it works, I can't be sure there won't be side effect. rmarkdown$metadata was the way to access the metadata with R Markdown, {{< meta ...>}} is the way to do it with Quarto.

Regarding parameters, this is supposed to be used this way :

---
title: "My Document"
params:
  alpha: 0.1
  ratio: 0.2
---

```{r}
params$alpha
```

The ratio is  `r params$ratio`

image

Same as with R Markdown: https://bookdown.org/yihui/rmarkdown/params-use.html but also working for Jupyter in Quarto.

Regarding you title page, that is clever. {{< include ... >}} would be the quarto syntax, but knitr child document indeed works ok.

@giabaio
Copy link
Contributor Author

giabaio commented Jul 15, 2022

Thank you! I am still learning about quarto and had no idea about shortcodes and meta (though I knew how to use shortcodes from blogdown...). I think this actually solves my problem (almost entirely!).

This

footer: |
        &copy; {{< meta params.author >}} ({{< meta params.affiliation >}}) &nbsp; | &nbsp; 
        {{< meta params.social >}} &nbsp; | &nbsp; 
        {{< meta params.shorttitle >}} &nbsp; | &nbsp; 
        {{< meta params.shortlocation >}} &nbsp; | &nbsp; {{< meta shortdate >}}

does exactly what I want (I've defined suitable fields shortdate with R code to manipulate and format the date and social to add essentially all HTML code to produce the icons and links)!

There are only two immediate little nuisances (which again may be due to me not knowing full well what I'm doing...):

  1. When I open the resulting html for the first time, even though the title page contains a customised (empty) footer ::: footer :::, the overall one still shows. It only takes to move to the next slide and then back to have it disappear. I am not sure why or whether I'm making this behaviour by doing something fundamentally wrong/weird?
  2. Because I don't have a title field in the yaml (but it's "hidden" in the params), the resulting html file's header when I open it in my browser is the name of the actual file (in this case "slides" as I've called the file "slides.qmd"). I guess this is not a big issue and I suppose I can "hack" the .html to correct that, almost manually... But could I just as easily point quarto to process params.title instead?

Many thanks for your help on this!
Gianluca

@cderv
Copy link
Collaborator

cderv commented Jul 15, 2022

When I open the resulting html for the first time, even though the title page contains a customised (empty) footer ::: footer :::, the overall one still shows. It only takes to move to the next slide and then back to have it disappear. I am not sure why or whether I'm making this behaviour by doing something fundamentally wrong/weird?

This would require a specific reproducible example to look into it closer.

Because I don't have a title field in the yaml (but it's "hidden" in the params), the resulting html file's header when I open it in my browser is the name of the actual file (in this case "slides" as I've called the file "slides.qmd"). I guess this is not a big issue and I suppose I can "hack" the .html to correct that, almost manually... But could I just as easily point quarto to process params.title instead?

EIther

title: {{< meta params.title >}} 

or

title: {{< var title >}} 

works.

or you could just use pagetitle variable if you need to fill the tab title without providing a title.

pagetitle: ...

Usually pagetitle = title when only the latter is provided.

Probably the setting pagetitle is what you need. This is mentioned in Pandoc's MANUAL
https://pandoc.org/MANUAL.html#metadata-variables

The page title in HTML is set by pagetitle, which is equal to title by default.

@giabaio
Copy link
Contributor Author

giabaio commented Jul 15, 2022

Thanks again! Using pagetitle does work like a charm --- thanks.
I'll make a minimal example for the footer issue! Thanks

@giabaio
Copy link
Contributor Author

giabaio commented Jul 16, 2022

Thanks again for all your help. This should have the files for a minimal reproducible example. When you compile the presentation, it opens the title page with the footer. After you navigate out of the title page and go back, the footer won't show on page 1 any more...

Everything else, I think I should have fixed, wrt what I was originally asking in the last couple of days. Many thanks for your support!
Gianluca

@cderv
Copy link
Collaborator

cderv commented Jul 18, 2022

Why do you have a empty footer in title-slide.qmd ?
https://github.com/giabaio/quarto-slides/blob/bb9ba2c1673c541478223040e9d2e23e86dad1b9/assets/title-slide.qmd#L34-L36
If you want to use the default footer, just donc add the custom div with footer class.

I believe by doing this you are saying you want no footer (empty footer). To me the issue is that at first when opening the presentation, the footer should be empty. (which is not because we are adding a hook regarding footer on slide change, which does not happen when first opening the presentation).

Car you clarifying the behavior you are expecting ?

@giabaio
Copy link
Contributor Author

giabaio commented Jul 18, 2022

Sorry --- I probably wasn't very clear... Yes: I do want to specify a footer that should appear from slide 2 onwards (but not on slide 1). I thought I needed to overrule the general

footer: ...

statement in the yaml, by explicitly saying something in the title-slide?

Sorry --- I hope this is clearer?
Thanks again!

@cderv
Copy link
Collaborator

cderv commented Jul 18, 2022

Yes this is clearer, and I think this may be a bug.

For now you could add a JS script in your presentation that would add display: none on the footer in the slide .title-slide.

I need to see closer as by design we are expecting footer on title slide, but allow to remove on other slides.

@giabaio
Copy link
Contributor Author

giabaio commented Jul 18, 2022

Thank you @cderv. I don't expect you to do it for me, of course, but I'm not too sure how I would write the JS script... Do you mean something like the insert-logo.html macro that would go in insert-after-body, like in xaringan?

<style>
.footer {
  display: none;
}
</style>

<script>
document
  .querySelectorAll(
    '.title-slide'
  )
  .forEach(el => {
    el.innerHTML += '<div class="footer"></div>';
  });
</script>

I've tried to hack that, but this doesn't work (mostly because I don't know what I'm doing, of course... :-) ). I'll keep at it, though it's a relatively minor issue, TBF and, for now, I can certainly live without it...

@cderv
Copy link
Collaborator

cderv commented Jul 18, 2022

Sorry I forgot that footer was like Logo or page number and it is not that straightforward. You can adapt what I did there: #557 (comment)

Something like:

---
format: 
  revealjs:
    footer: Custom footer
include-after-body: 
  text: |
    <script type="text/javascript">
      Reveal.on( 'ready', event => {
        // remove footer when prez start on first slide
        if (event.indexh === 0) {
          document.querySelector("div.footer").style.display = "none";
        }
      })
      Reveal.addEventListener('slidechanged', (event) => {
        // Aslo remove footer when prez start go back to first slide
        if (event.indexh === 0) {
          document.querySelector("div.footer").style.display = "none";
        }
      });
    </script>
---

# Slide 1 {#title-slide}

# Slide 2

content 

Hopefully with not to much side effect as Quarto should handle putting the correct footer on all other slides.

@giabaio
Copy link
Contributor Author

giabaio commented Jul 19, 2022

Thank you, @cderv. This seems to work --- though like you say it probably would be nice to have some more built-in command. I hope this is useful feedback and doesn't sound like constant whining (:wink:), but both footer and logo do appear on the title slide, they just are sort of deleted almost immediately, rather than being taken out altogether?...

Thanks again!
Gianluca

@cderv
Copy link
Collaborator

cderv commented Jul 19, 2022

but both footer and logo do appear on the title slide, they just are sort of deleted almost immediately, rather than being taken out altogether?...

Exactly. They are included by hidden in browser once slides are loaded. There is currently no other way to "not include" footer div in slides

@giabaio
Copy link
Contributor Author

giabaio commented Jul 19, 2022

OK! Thanks.

@dragonstyle dragonstyle added this to the Future milestone Jul 19, 2022
@giabaio
Copy link
Contributor Author

giabaio commented Jul 19, 2022

Can I possibly continue to be very dumb and ask why the following doesn't work?

include-after: |
  <style>
  .logo {
    background-image: url("PATH_TO_FILE");
    background-size: 14% 7%;
    background-repeat: no-repeat;
    position: absolute;
    top:  1.85%; /* 2.65%em */
    left: 86%;
    height: 100%;
    width: 100%;
    z-index: 1000; !default
  }
  </style>
  
  <script>
  document.querySelectorAll(
      ':not(.title-slide)'
    );
    .forEach(el => {
      el.innerHTML += '<div class="logo"></div>';
    });
  </script>

When I use

 <script>
  document.querySelectorAll(
      ':slides'
    );
    .forEach(el => {
      el.innerHTML += '<div class="logo"></div>';
    });
  </script>

(which I think selects all slides because the whole presentation is wrapped in a <div class="slides>"), all works OK. The image specified in the `<style> is displayed in the position I want with the size that I want. But when I try to add a class for which I don't want the logo to be included, then the logo just disappears from all slides...

BTW: I may be wrong/mistaken here, but I have defined the class title-class in my scss file --- in fact, the title page is formatted as

::: {.title-page}
...

but the resulting html file wraps it around a <div class="title-slide center">

@mcanouil
Copy link
Collaborator

I think (not tested the code) the "error" is coming from the wrong key used in your yaml:

include-after: |
  ...

instead of

include-after-body: 
  text: |
    ...

Also you could use the .slide-logo logo class related to the yaml key logo.
For example, I defined a template extensio where the logo is placed top-right on the slides.
https://github.com/umr1283/quarto-revealjs-umr1283

Note: there is an issue (#1441) in the quarto extension install mechanism for this particular case, but with a manual workaround.

@giabaio
Copy link
Contributor Author

giabaio commented Jul 20, 2022

Actually, I think I figured it out.
I think I understand that the various classes need to be defined completely in the querySelector. In fact, they get embedded within a <section ...> and so the right call is something like

    <script>
    document.querySelectorAll(
        'section.slide.level2:not(section.slide.level2.title-slide.center):not(section.slide.level2.nobar)'
      )
      .forEach(el => {
        el.innerHTML += '<div class="logo"></div>';
      });
    </script>

--- this does exclude the logo from the title slide, which is embedded in a <section id="..." class="slide level2 title-slide center"> and from those slides associated with class nobar, which are embedded in a <section id="section" class="slide level2 nobar" ...> (here ... indicate some text, for example, the text associated with the slide level 2 symbol or the content of the nobar slide).

@mcanouil mcanouil converted this issue into discussion #6262 Jul 18, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants