# Org-capture template expansion properties & prompt references

**URL:** https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993
**Category:** Requests
**Created:** [February 23, 2023, 8:22pm UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993 "2023-02-23T20:22:27Z")
**Posts on this page:** 13
**Page:** 1

<div class="post-metadata">

### Author: ![mnb100](https://avatars.discourse-cdn.com/v4/letter/m/5f8ce5/32.png) [@mnb100](https://org-roam.discourse.group/u/mnb100)
#### Post date: [February 23, 2023, 8:22pm UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993/1 "2023-02-23T20:22:27Z")

</div>

Hello,

When creating various org-capture template files (to be used as `(file /path/to/template-file)` in `org-roam-capture-templates`), the need to include properties came up, along with the need to refer to the previous prompts in the template.

In ordinary `org-caputre-templates`, these issues do not exist (insert the property drawer inside the file itself, and use `%\N` to refer to the Nth prompt in the capture template).

In org-roam-capture-templates however, as described [over here](https://org-roam.discourse.group/t/how-to-prevent-org-roam-capture-from-adding-id-property/2669), always creates the ID (and inserts it into the property drawer – creating it if needed) before expanding and inserting the capture template file – resulting in 2 property drawers overall.

There is the option to parse the template file to include its property drawer in the header (`file+head` `:target` option) – which seems to be inserted before the property drawer is inserted – causing the ID property to be placed in the correct drawer. But then the other issue occurs – from my understanding of the code, the insertion and expansion of the header is a different one from the insertion and expansion of the template file itself – as a result, they do not share prompt references (and I don’t know if there are other effects).

Another option is to delete the ID property and add it after the capture process is complete. However, I do not really like this as it seems risky, I can’t ensure other things don’t rely on it – whether it is org-roam or any 3rd party library I might want to use in the future.

Is there another way to solve both these issues (properties and prompt references in capture template files)?

---

<div class="post-metadata">

### Author: ![nobiot](https://yyz2.discourse-cdn.com/free1/user_avatar/org-roam.discourse.group/nobiot/32/159_2.png) [@nobiot](https://org-roam.discourse.group/u/nobiot)
#### Post date: [February 23, 2023, 9:39pm UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993/2 "2023-02-23T21:39:59Z")

</div>

Not sure if I understand what you are trying to achieve but why not use the ordinary org-capture instead of the org-roam one?

---

<div class="post-metadata">

### Author: ![mnb100](https://avatars.discourse-cdn.com/v4/letter/m/5f8ce5/32.png) [@mnb100](https://org-roam.discourse.group/u/mnb100)
#### Post date: [February 24, 2023, 12:10pm UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993/4 "2023-02-24T12:10:02Z")

</div>

> Not sure if I understand what you are trying to achieve

Being able to use prompt references and properties in the org-capture template. If you use a normal template file, it would be inserted in a way that creates 2 property drawers (because org-roam-capture) generates one for the ID in a previous step of the capture.

Separating the property drawer and the rest of the template would result in the reference prompts (if I have a prompt `%^{my prompt}` I can use `%\1` later in the template, it would expand to the first prompt in the template, instead of prompting again). This is caused by the fact the expansion of the head (the `head` part of `file+head`) and the expansion of the template file itself are performed as separate steps.

> why not use the ordinary org-capture instead of the org-roam

If I am not mistaken, I lose a lot of built-in functionality (such as `org-roam-node-*` functions) – which I might have to implement again by myself. The bigger issue is that I cannot control the usage of these functions by 3rd party packages.

Moreover, the additional `${}` syntax is very handy, at least for my use cases.

It is mainly the first point, though.

---

<div class="post-metadata">

### Author: ![nobiot](https://yyz2.discourse-cdn.com/free1/user_avatar/org-roam.discourse.group/nobiot/32/159_2.png) [@nobiot](https://org-roam.discourse.group/u/nobiot)
#### Post date: [February 24, 2023, 12:34pm UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993/5 "2023-02-24T12:34:10Z")

</div>

Your issue seems to be coming from your advanced understanding and usage of org-capture-templates and is well beyond me. I tried to understand what you wrote a few hours ago (the morning in my time zone) and tried it just now after your update just now. I still don’t know what the issue is. Now here is how I read it and why I mentioned org-capture, instead of org-roam-capture – you can ignore my rambling, really. Hopefully someone more knowledgeable can jump in and help you.

1. `org-roam-capture-templates` do not work in the way you want.

2. `org-capture-templates` do what you want, but they do not have a useful `${}` (but it is not essential for your use case)

3. The reason why 2. is not a solution is because  
a. You will lose “a lot of built-in functionality (such as `org-roam-node-*` functions)”  
b. You cannot control the usage of these functions by 3rd party packages

I am saying don’t worry about 3. altogether.

For point a. `org-roam-node-*` functions work if you have an ID (Org-ID) no matter how you add them. I even use `denote` package to create files with IDs together with Org-roam. The only requirement for a file and a headline to be an Org-roam node is an ID (Org-ID).

For point b. I am not sure if this is a material issue at the moment. If it becomes a material issue, then you can think of a solution then. I would choose to move on with using `org-capture-templates` as you seem to know that it works for your use case. b. is non-issue at the moment; merely a future possibility.

---

<div class="post-metadata">

### Author: ![mnb100](https://avatars.discourse-cdn.com/v4/letter/m/5f8ce5/32.png) [@mnb100](https://org-roam.discourse.group/u/mnb100)
#### Post date: [February 24, 2023, 2:16pm UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993/7 "2023-02-24T14:16:12Z")

</div>

> [@nobiot](#):
>
> I still don’t know what the issue is

Perhaps a small example would help?  
Let’s say I want to include the property `FOO` in my template.

```auto
("d" "default" plain 
":PROPERTIES:
:FOO: bar
:END:
%?" 
:target (file "%<%Y%m%d%H%M%S>-${slug}.org")
:unnarrowed t)

```

The resulting capture buffer is:

```auto
:PROPERTIES:
:ID: 0893b791-ab10-4f06-b84b-7b077fd84a4a
:END:
:PROPERTIES:
:FOO: bar
:END:
<cursor position>

```

Which is not intended.

Moving the property drawer to the head, helps:

```auto
("d" "default" plain 
"%?"
:target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" 
":PROPERTIES:
:FOO: bar
:END:
")
  :unnarrowed t)

```

The resulting capture buffer is:

```auto
:PROPERTIES:
:FOO: bar
:ID: 33538392-1be8-4d0c-89ba-7579500c1329
:END:
<cursor position>

```

But then I cannot use prompt references, for example:

```auto
("d" "default" plain 
"This is a note about %\\1
%?"
:target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" 
":PROPERTIES:
:FOO: %^{foo prompt}
:END:
")
  :unnarrowed t)

```

This results in an error (`org-capture-fill-template: Wrong type argument: stringp, nil`) – as it cannot find the 1st prompt in the template to refer to. This is because the template and the head are expanded at different times. For clarification, this works as expected:

```auto
("d" "default" plain 
"%?"
:target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" 
":PROPERTIES:
:FOO: %^{foo prompt} %\\1
:END:
")
  :unnarrowed t)

```

At the prompt (which happens once), I can enter `bar` and the result would be:

```auto
:PROPERTIES:
:AUTHOR: bar bar
:ID: e773b30b-028e-42fa-a539-6bf2cf33c9ef
:END:
<cursor position>

```

To conclude, my issue is how to solve these problems at the same time.

> [@nobiot](#):
>
> `org-capture-templates` do what you want, but they do not have a useful `${}` (but it is not essential for your use case)

Sadly, it is:

> [@mnb100](#):
>
> Moreover, the additional `${}` syntax is very handy, at least for my use cases.

Mainly for `org-roam-node` properties such as `${title}` – if there is a nice alternative, I am open to suggestions.

> [@nobiot](#):
>
> `org-roam-node-*` functions work if you have an ID (Org-ID) no matter how you add them.

My fault for not being clear enough – but I meant for functions like `org-roam-node-find`, which I know would work if I have an ID like you have said – but sometimes do rely on `org-roam-capture-`. For example, entering a nonexistent node at the prompt of `org-roam-node-find` invokes `org-roam-capture-` – which is a behavior I would like to have (falling back to a capture, just not necessarily `org-roam-capture-`).  
This specific functionality is easy to reimplement to work with `org-capture` – but I am not so sure about the other `org-roam-node-*` functions.

> [@nobiot](#):
>
> b. I am not sure if this is a material issue at the moment. If it becomes a material issue, then you can think of a solution then.

Fair enough, that is a good point.

---

<div class="post-metadata">

### Author: ![nobiot](https://yyz2.discourse-cdn.com/free1/user_avatar/org-roam.discourse.group/nobiot/32/159_2.png) [@nobiot](https://org-roam.discourse.group/u/nobiot)
#### Post date: [February 24, 2023, 3:42pm UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993/8 "2023-02-24T15:42:58Z")

</div>

To be honest, I would have thought the following worked, but I trust that you have confirmed it does not.

> [@mnb100](#):
>
> ```auto
> ("d" "default" plain 
> ":PROPERTIES:
> :FOO: bar
> :END:
> %?" 
> :target (file "%<%Y%m%d%H%M%S>-${slug}.org")
> :unnarrowed t)
> 
> ```

I’m away for the weekend without my computer so I cannot test the idea below; I’d create a custom function and add it to org-capture-after-finalize-hook. It’s a simple variation of the solution discussed in the thread you linked above.

The custom function should be something like this below. I am writing it on iPhone so am not checking the function name, parenthesis, or syntax. I hope something like this works

```elisp
(let ((var (read-string “Enter author: “)))
  (org-add-property “AUTHOR” var)
;; I assume the point is already in the correct location
;; if not, move point, probably (plont-max) will work
;; if the capture buffer is narrow
  (insert var)))

```

---

<div class="post-metadata">

### Author: ![mnb100](https://avatars.discourse-cdn.com/v4/letter/m/5f8ce5/32.png) [@mnb100](https://org-roam.discourse.group/u/mnb100)
#### Post date: [February 24, 2023, 4:35pm UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993/9 "2023-02-24T16:35:45Z")

</div>

I don’t fully understand how this approach would solve the second problem (referencing the prompt in the template):

> [@mnb100](#):
>
> ```auto
> ("d" "default" plain 
> "This is a note about %\\1
> %?"
> :target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" 
> ":PROPERTIES:
> :FOO: %^{foo prompt}
> :END:
> ")
> :unnarrowed t)
> 
> ```

But I guess I need to compromise and enter the same input for multiple prompts (discard the usage of prompt references altogether).

I thought there might be a solution which is less risky than what you have suggested [here](https://org-roam.discourse.group/t/how-to-prevent-org-roam-capture-from-adding-id-property/2669/6).

> [@nobiot](#):
>
> I’d create a custom function and add it to org-capture-after-finalize-hook.

> [@nobiot](#):
>
> `; if not, move point,`

I think finding the correct point would be rather difficult and unintuitive in the general case (as the references can occur at arbitrary places in the template).

---

<div class="post-metadata">

### Author: ![nobiot](https://yyz2.discourse-cdn.com/free1/user_avatar/org-roam.discourse.group/nobiot/32/159_2.png) [@nobiot](https://org-roam.discourse.group/u/nobiot)
#### Post date: [February 24, 2023, 5:29pm UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993/10 "2023-02-24T17:29:35Z")

</div>

> [@mnb100](#):
>
> I thought there might be a solution which is less risky than what you have suggested [here](https://org-roam.discourse.group/t/how-to-prevent-org-roam-capture-from-adding-id-property/2669/6).

Maybe. I don’t have the same level of understanding of the “risk” as yours, so I can’t really comment.

> [@mnb100](#):
>
> I don’t fully understand how this approach would solve the second problem (referencing the prompt in the template):

This example does not. I would put the whole node-capture/node-find function in a let form (and create a custom command). Then, I believe, you can evaluate the let-bound-variable in the template and place it anywhere in the template (?) This way you only would need a prompt once.

---

<div class="post-metadata">

### Author: ![mnb100](https://avatars.discourse-cdn.com/v4/letter/m/5f8ce5/32.png) [@mnb100](https://org-roam.discourse.group/u/mnb100)
#### Post date: [February 24, 2023, 10:05pm UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993/11 "2023-02-24T22:05:15Z")

</div>

> [@nobiot](#):
>
> I don’t have the same level of understanding of the “risk” as yours, so I can’t really comment.

In this case, I cannot be sure that someday the code of `org-roam-capture.el` or its dependencies would depend on the ID property to be present at capture time, just as it is now.

I can advise functions to return the ID of the node instead ([like you have done](https://github.com/nobiot/md-roam/blob/9ccd8fee00ebea28dfdc1f6d6ffe3d312973ceb7/md-roam.el#L209)), but I cannot prevent all possible methods of retrieving this property (and advise them accordingly), because there are a lot of ways.

My (irrational?) fear of advices stems from the fact it’s always another place to look when a bug is found – as I change internal logic. That is why I try to the best of my ability to not use advices or private (double-dashed `--`) symbols.

I would like to hear your take about it, though – as I’ve seen a lot of people and package authors in particular advising functions. How can you ensure it won’t be a [cat-and-mouse game](https://en.wikipedia.org/wiki/Cat_and_mouse)?

> [@nobiot](#):
>
> This example does not. I would put the whole node-capture/node-find function in a let form (and create a custom command). Then, I believe, you can evaluate the let-bound-variable in the template and place it anywhere in the template (?) This way you only would need a prompt once.

Interesting. I need to think a little about how to implement it – as I don’t think it’s very intuitive (the capture template depend on the let-bound-variables which are created based on the template itself – a somewhat circular behavior).

Also, it would change the capture template to have implementation details (the calls for the evaluation of the let-bound-variables instead of the “normal” way of referencing prompts) – not a dealbreaker by any means, but still worth noting.

---

<div class="post-metadata">

### Author: ![nobiot](https://yyz2.discourse-cdn.com/free1/user_avatar/org-roam.discourse.group/nobiot/32/159_2.png) [@nobiot](https://org-roam.discourse.group/u/nobiot)
#### Post date: [February 27, 2023, 8:48pm UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993/12 "2023-02-27T20:48:17Z")

</div>

I see there are a couple of different topics; let me unpick them.

#### Custom function with let-bound-variables for `org-roam-capture`

> [@mnb100](#):
>
> (the capture template depend on the let-bound-variables which are created based on the template itself – a somewhat circular behavior).

> [@mnb100](#):
>
> Also, it would change the capture template to have implementation details (the calls for the evaluation of the let-bound-variables instead of the “normal” way of referencing prompts)

I think the implementation below avoids the “circular behavior” and prompts for a property. It works on my end.

I tried to implement something different with a let-bound-variable, I failed. I needed to define the variable anyway with `defvar`, so perhaps my original idea was not possible syntactically.

```emacs-lisp
(add-to-list 'org-roam-capture-templates
             '("s" "special" plain ""
               :target
               (file+head "./${id}.org" "
#+title: ${title}
#+author: %(read-string \"Author: \")
#+id: ${id}

")))

```

#### The code `org-roam-capture.el` may change someday

> [@mnb100](#):
>
> In this case, I cannot be sure that someday the code of `org-roam-capture.el` or its dependencies would depend on the ID property to be present at capture time, just as it is now.

In general, I would agree. For the case of `org-roam-capture`, however, I personally do not worry about this because `org-roam` is in maintenance since the end of 2021. See this [blog article](https://blog.jethro.dev/posts/org_roam_2021/) from Jethro, Org-roam’s author. The API and internals have been stable since then.

`org-roam-capture` also relies on `org-capture`, which is more actively developed but with backward compatibility carefully considered.

#### Use of advice in general and in `md-roam`

> [@mnb100](#):
>
> My (irrational?) fear of advices stems from the fact it’s always another place to look when a bug is found – as I change internal logic. That is why I try to the best of my ability to not use advices or private (double-dashed `--`) symbols.
> 
> I would like to hear your take about it, though – as I’ve seen a lot of people and package authors in particular advising functions.

I don’t think it’s irrational. I would avoid advising for the same reason you mention: “as I change internal logic”.

In the particular case of `md-roam`, the concern for this risk might be exaggerated. If we follow this definition:

```auto
(R)isk = (P)robability × (M)agnitude

```

I consider P and M are very small, and thus R negligible (for me).

For P, Md-roam is advising functions from stable Org-roam, as noted in the previous point above, the probability of a major change is low.

For M, `md-roam` is my “personal config” shared via GitHub. It’s not distributed more widely; not available in MELPA and ELPA. Should advised functions change, I should not have to worry about much of the impact. In the end, I can just create notes in markdown without using `md-roam` until I have some time to fix the problem (which should be very infrequent anyway).

---

<div class="post-metadata">

### Author: ![nobiot](https://yyz2.discourse-cdn.com/free1/user_avatar/org-roam.discourse.group/nobiot/32/159_2.png) [@nobiot](https://org-roam.discourse.group/u/nobiot)
#### Post date: [February 28, 2023, 7:36am UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993/13 "2023-02-28T07:36:30Z")

</div>

> [@nobiot](#):
>
> I think the implementation below avoids the “circular behavior” and prompts for a property. It works on my end.
> 
> I tried to implement something different with a let-bound-variable, I failed. I needed to define the variable anyway with `defvar`, so perhaps my original idea was not possible syntactically.
> 
> ```auto
> (add-to-list 'org-roam-capture-templates
> '("s" "special" plain ""
> :target
> (file+head "./${id}.org" "
> #+title: ${title}
> #+author: %(read-string \"Author: \")
> #+id: ${id}
> 
> ")))
> 
> ```

Please disregard this. I keep forgetting about your “template” requirement.

This below works on my end.

I am not really concerned with the “circular behavior” or that the “capture template to have implementation details (the calls for the evaluation of the let-bound-variables instead of the “normal” way of referencing prompts)”.

An inner scope can work with variables in the outer scope. In this case, it’s only me using this let-binding, and can guarantee that it works for me and I don’t need to worry about usage by others.

```emacs-lisp
(defvar var "")

(setq org-roam-capture-templates
             '(("s" "special" plain ""
               :target
               (file+head "./${id}.org" "
#+title: ${title}
#+author: %(identity var)
#+id: ${id}

This is a template %(identity var)

"))))

(let ((var (read-string "Author: ")))
  (org-roam-capture nil "s"))

```

---

<div class="post-metadata">

### Author: ![lyndhurst](https://avatars.discourse-cdn.com/v4/letter/l/91b2a8/32.png) [@lyndhurst](https://org-roam.discourse.group/u/lyndhurst)
#### Post date: [April 24, 2024, 12:37pm UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993/14 "2024-04-24T12:37:22Z")

</div>

Hi, I am sorry if my question is dumb, but I am new to (doom) emacs, and I kind of struggle with elisp to be frank.

This snippet seems to do exactly what I am looking for, but if I copy it to my config, what I understand is a variable binding method `let` at the end of the code gets executed on startup prompting me for the variable’s value.

Can someone please show me how to modify the code so I can re-use it ?  
Thanks for your time.

PS: I am also aware of the guidelines, and this reply is obviously an ask for help, and does not improve the conversation but I thought starting a new thread to reference this one was overkill and who knows it can help some other beginner like me in the future.

EDIT: Question moved to [this thread](https://org-roam.discourse.group/t/org-roam-templates-reuse-prompted-values-within-a-template/3443).

---

<div class="post-metadata">

### Author: ![nobiot](https://yyz2.discourse-cdn.com/free1/user_avatar/org-roam.discourse.group/nobiot/32/159_2.png) [@nobiot](https://org-roam.discourse.group/u/nobiot)
#### Post date: [April 24, 2024, 12:47pm UTC](https://org-roam.discourse.group/t/org-capture-template-expansion-properties-prompt-references/2993/15 "2024-04-24T12:47:29Z")

</div>

It is not an overkill to create a new thread. I do not remember the context (I am assuming you are referring to the code snippet I gave above??).

It would be great to summarize the issue, what you wish to achieve, and reference the snippet.

> [@lyndhurst](#):
>
> This snippet seems to do exactly what I am looking for, but if I copy it to my config, what I understand is a variable binding method `let` at the end of the code gets executed on startup prompting me for the variable’s value.

I can’t be certain what “this snippet” exactly is even — I am sorry, but it appears to be too old a thread for my memory (I respond to many of the questions here, so I don’t remember them).
