How do you Pass a Field Value to a Function?

Here is my function:

\function test-bg(param:empty)
    [<param>match[low]then[skyblue]]
	  :else<param>match[medium]then[purple]]
	  :else<param>match[high]then[red]]
	  :else<param>match[critical]then[gray]]
\end test-bg

How do you pass a field value to a function parameter? I have been trying differnet ways to do it but was unsuccessful.

For example this does not work:

<<test-bg {{!!task-priority}}>>

Ironically, you can call a function using the $macrocall. Is that a valid way of doing it?

<$macrocall $name=test-bg value={{!!task-priority}} />
1 Like

Yes, you can use filtered transclusion with function using the macrocall widget, or with the transclude widget.

1 Like

If you use at least one . in your function name, you can treat the function as a custom operator:

\function test.bg(param)
	[<param>match[low]then[skyblue]]
	~[<param>match[medium]then[purple]]
	~[<param>match[high]then[red]]
	~[<param>match[critical]then[gray]]
\end

This gives you some additional flexibility in parameter usage:

{{{ [test.bg{!!task-priority}] }}} or <$text text={{{ [test.bg{!!task-priority}] }}} />

{{{ [test.bg[high]] }}}

<$let variable="high">
{{{ [test.bg<variable>] }}}
</$let>

This is a general limitation of the <<short-form>> syntax shared by functions, procedures, and macros: in each case, they can only take literal strings as (named or sequential) parameters. So while <<test-bg {{!!task-priority}}>> isn’t valid, the following parameters are…

<<test-bg high>>
<<test-bg param:"high">>

And as you guessed, the following are both valid long-form “function-calls”:

<$macrocall $name=test-bg value={{!!task-priority}} />
<$transclude $variable=test-bg value={{!!task-priority}} />

I don’t typically find the widget syntax very useful for working with functions, though; it doesn’t lend itself to use in filters or attribute values. And conversely, all the above examples do still work with test.bg rather than text-bg, so there’s no real downside to using periods in your function names.

1 Like

Another approach is to just turn it into a variable first;

\function test-bg()
    [<param>match[low]then[skyblue]]
	  :else<param>match[medium]then[purple]]
	  :else<param>match[high]then[red]]
	  :else<param>match[critical]then[gray]]
\end test-bg
<$let param={{!!task-priority}}>
   <<test-bg>>
</$let>

Then remove param as a parameter (perhaps also rename param to task-priority). The function will “inherit” the variable unless it is defined, as test.bg(param) effectively defines param inside the function.

However I do favor using functions and custom operators.

Another approach

Depending on if you want to reuse your function to access other fields, you could just pass the fieldname

\function myfunc(fieldname) ....

<<myfunc fieldname>> and use [all[current]get<fieldname>... instead of the hardcoded {!!fieldname}

I couldn’t get the “inherit” mechanism to work. Every time I had to explicitly declare the parameter.

I am curious about your " Another approach" but I do not understand it.

I was able to find a working solution, I am building a task workflow and wanted the priority dropdown to be colored based on values. I had to use style.* in the $select macro because the CSS modpri was not dynamically changing (when it was, it was applying to all tiddlers calling tm-module-priority.

Here is my tiddler, tagged with $:/tags/Macro:

\procedure tm-module-priority()
    <$select field="task-priority" class="modpri" default="Choose Priority" style.background-color={{{[priority.color{!!task-priority}]}}} >
        <option class="modpri" disabled>Choose Priority</option>
    		<option class="modpri" style.background-color={{{ [priority.color[low]] }}}>low</option>
    				<option class="modpri" style.background-color={{{ [priority.color[medium]] }}}>medium</option>
    				<option class="modpri" style.background-color={{{ [priority.color[high]] }}}>high</option>
    				<option class="modpri" style.background-color={{{ [priority.color[critical]] }}}>critical</option>

    <style>
      .modpri {
    	  color: white;
    		font-weight: bold;
      }
    </style>
\end

\function priority.color(param:"empty")
    [<param>match[empty]then[black]]
    :else[<param>match[low]then[skyblue]]
		:else[<param>match[medium]then[blue]]
		:else[<param>match[high]then[red]]
		:else[<param>match[critical]then[gray]]
\end

That’s because your function has a named parameter of the same name. Here, you’re explicitly redefining the <<param>> variable as either the declared value or “empty”:

\function test-bg(param:empty)

But even if you didn’t provide a fallback value, it would be redefined as blank if not explicitly declared:

\function test-bg(param)

If you want the <<param>> value to be inherited from an existing variable <<param>>, you need to omit it from the function parameters, as Tony did (edited to add the missing brackets):

In this case, you might also want to build your fallback value into the function — and you can do this with filter logic, without defining a default “empty” value:

\function test-bg()
    [<param>match[low]then[skyblue]]
	  :else[<param>match[medium]then[purple]]
	  :else[<param>match[high]then[red]]
	  :else[<param>match[critical]then[gray]]
	  :else[[black]]
\end

or

\function test-bg()
    [<param>match[]then[black]]
    :else[<param>match[low]then[skyblue]]
    :else[<param>match[medium]then[blue]]
    :else[<param>match[high]then[red]]
    :else[<param>match[critical]then[gray]]
\end

As a side note, I don’t generally see much value in defining a parameter fallback like “empty” unless you need the visual cue for temporary testing purposes; since we have filter operators like match[] or is[blank], a null value is itself a value. So even in cases where I do want an explicitly declared parameter without a “meaningful” default value, I’d typically define it like this:

\function test-bg(param)