Javascript: Pass variable to e.g. filterTiddlers()

In a Javascript widget, I have a variable query with arbitrary input from the user. I also have a filter that includes <query>. What’s the best way to run $tw.wiki.filterTiddlers(filter) and make sure that <query> is expanded/transcluded with its value?

My ugly solution (that seems to work) is this,

function makeFakeVariableWidget(name, value) {
  return {
    getVariable: (_name) => {
      if (_name === name)
        return value;
      else
        return "";
    }
  };
}

var query = "arch";
var fakeWidget = makeFakeVariableWidget("query", query);
var filter = "[search:text<query>]";
$tw.wiki.filterTiddlers(filter , fakeWidget);

makeFakeVariableWidget() returns an object with only one method, getVariable(), that only returns a predetermined value for one variable name. After going through $:/core/modules/filters/filter.js, this seems to be the only thing it uses, even though it refers to the object as a “widget”.

I guess that I should use a LetWidget, but how?

As you know, the function signature for $tw.wiki.filterTiddlers() is exports.filterTiddlers = function(filterString,widget,source)

The second parameter is usually is this since it points to the widget that calls the function.

Every widget inherits all functions from the Widget prototype when it is defined

MyWidget.prototype = new Widget();

Widget has a lot of local functions. .setVariable() is one of them. … So it is possible to define as many variables as you need in your widget.

So the following code should work without your makeFakeVariableWidget hack

this.setVariable("query", "something")

var filter = "[search:text<query>]";
var tiddlers = $tw.wiki.filterTiddlers(filter)

BUT … without seeing the full code of your widget it’s hard to guess.
Hope that helps

Thank you!

However, it doesn’t work, and I fail to see how this is not a worse design than my “hack”.

First of all, if TiddlyWiki was implemented in e.g. Typescript, with typed function arguments, I don’t think that filterTiddlers() would receive an object from the class Widget (or any subclass), like so:

exports.filterTiddlers = function(filterString: string, widget: Widget, source: ???)

I’m no TW expert, but this would seem like unnecessarily tight coupling.

AFAIK, the Widget class requires a parse tree node, a “document” and many other things. There is, in fact, a $tw.fakeDocument that I guess is there just to work around this tight coupling.

It seems ridiculous to have to use $tw.fakeDocument and a fake parse tree node just to pass a variable to compileFilter() – and I just proved that you don’t have to.

Your solution is kind of a workaround to the problem, where I could pass the this instance of the class CommandPaletteWidget and set my temporary variable in that object – but to me, that is abusing the class. What if another method wanted to use the “query” variable for another purpose? I have no idea.

I hope that the signature of filterTiddlers would look something like this:

exports.filterTiddlers = function(filterString: string, widget: IVariableSource, source: ???)

Where IVariableSource is an interface that only contains getVariable(name: string, options: ???). Of course, the Widget base class would implement this interface. Then you could pass a widget, but it would be obvious that you don’t have to go through that many hoops and create a mess of your code.

So… What did I miss, and what other solutions are prettier? Maybe @Maurycy has some input?

The whole widget is here: TW-commandpalette/widgets_commandpalettewidget.js at master · Souk21/TW-commandpalette · GitHub – and my suggested changes are here: Search improvements (variation) by cdaven · Pull Request #20 · Souk21/TW-commandpalette · GitHub (in the searchStepBuilder() method).

As with many things in TiddlyWiki this is most likely a by product of the organic development of features - in this case the use cases for filters - and the commitment to backwards compatibility. I suspect that filters were originally implemented to always be evaluated in the context of a widget, such as the filter attribute for the list widget.

As filters have started to be used in other contexts, there has been some discordance in how filters are evaluated, and this includes but is not limited to the area you are touching upon.

The ongoing work on parametrized transclusions attempts to address this by adding a method to the Widget class.

Any further thoughts you might have on this matter would be very welcome on that PR.

Thank you! That method makeFakeWidgetWithVariables() is shockingly similar to my makeFakeVariableWidget() above. :smiley:

I left my thoughts on that method here: https://github.com/Jermolene/TiddlyWiki5/pull/6666/files#r1023907425