How can I link to a dynamically generated label?

I’m currently have a vocab table and want to create the entries with a for loop and add tags to each of those entries when compiled. Then I can just link to the definitions whenever I use that word, but I get the error that typst cannot find a reference to the label.

Adding semantics to @Types <- (Label cannot be found) types be a priority.

#table(
  columns: (1fr, 1fr),
  align: center,
  table.header([*SQL*],[*Bosque*]),
  [CREATE DATABASE Classroom],
  [let classroom: SQLDB = createDatabase("./database.sql");]
)


= Vocabulary

#let vocabulary = (
  "Types" : "A set of values with operations that can be performed on those set of values.",
  "Static Checking": "The types of variables are know at compile time",
)

#for (word, definition) in vocabulary {
  [- *_  #word #label(word) _* : #definition #"\n"] <- Label defined here
}

What am I missing?

It’s because your labels are attached to plain texts, which are not referenceable.

Error: Cannot reference text

Headings are referenceable, so when you @heading, typst writes a Section X.Y and links it to the heading. However, when you @Types, typst does not know what to write.

If you just want to link to a labelled element and not get an automatic textual reference, consider using the link function instead.

Adding semantics to #link(<Types>)[types] be a priority.

Hi @Karidus, welcome and thanks for your question! If you feel the response you got has sufficiently answered your question, be sure to give it a checkmark :ballot_box_with_check:. This will help others find the solution in the future. If something is missing, please let us know what so we can resolve your issue. Thanks!

@Y.D.X is correct about how references are handled. But what you can also do is use metadata! Metadata is an invisible component, which can contain information and are referenceable. And to make them referenceable using the @... syntax, we additionally have to set a show rule for ref:

#show ref: it => {
  if "vocab:" in str(it.target) {
    link(it.element.location())[#it.element.value]
  } else {
    it
  }
}

Adding semantics to @vocab:Types

#table(
  columns: (1fr, 1fr),
  align: center,
  table.header([*SQL*],[*Bosque*]),
  [CREATE DATABASE Classroom],
  [let classroom: SQLDB = createDatabase("./database.sql");]
)

#pagebreak()

= Vocabulary

#let vocabulary = (
  "Types" : "A set of values with operations that can be performed on those set of values.",
  "Static Checking": "The types of variables are know at compile time",
)


#terms(..vocabulary.pairs().map((entry) => {
  ([#entry.first() #metadata(entry.first())#label("vocab:"+entry.first())],[#entry.last()])
}))

I use the label-preamble "vocab:" to catch the correct references.

I’ve also decided to print your list as a terms list, since these are exactly for lists of terms/vocabulary. But you don’t have to of course!

Also sorry about the terms list syntax → what I’m essentially doing is convert the vocabulary dictionary to an array (each entry contains another array, where key and value is assigned). Then I map (a compact form of a for-loop) the key and value to the required syntax of the terms list ((key,value)) and return it. The .. spreads the array into the function, meaning each pair is “manually” placed into the function.

2 Likes

I had a really hard time understanding this line, so I rewrote it to something more readable:

= Vocabulary

#let vocabulary = (
  "Types" : "A set of values with operations that can be performed on those set of values.",
  "Static Checking": "The types of variables are know at compile time",
)

#let voclist = ()

#for (key, value) in vocabulary { 
  voclist.push((
      [#key#metadata(key)#label("vocab:"+key)],
      value
    )
  )
}

voclist: #repr(voclist)

// undocumented terms syntax
// terms does not expect an array of (term, description) pairs
// instead, it expects each (term, description) pair as
// a separate, unnamed, argument.
// Thus, destructure the voclist to its pairs
#terms(..voclist)

Hope this might be of help to someone.

1 Like