This page is part of a static HTML representation of the TiddlyWiki at https://tiddlywiki.com/dev/

Widget attributes tutorial part I

5th February 2019 at 2:35am

So far none of the widgets we've implemented have had any code for handling widget attributes A vast majority of useful widgets will need to support attributes in order to make them useful.

As an example, let's change the Hello World widget so it can greet not just the world, but whoever/whatever the user wants. To do that we can add support for an attribute named what and call it like <$hello what="pale blue dot"/>.

The tiddlywiki widget class provides methods computeAttributes and getAttribute which can together be used by the widget code to discover what values are passed into the widget.

The computeAttributes and getAttribute methods can be called like this (the second parameter to getAttribute will be used as the default value if the user doesn't provide that attribute in the widget call):

this.computeAttributes();
var message = this.getAttribute("message", "World");

Then the message variable can be used to construct the Hello XXX string:

var textNode = this.document.createTextNode("Hello, " + message + "!");

The original hello.js code only implements a render method. The refresh method is not needed because the output from the widget can never be different...it will always be "Hello, World!".

Even with a message attribute, you might think the render by itself is enough. After all, the value of the input parameter message can only be changed by modifying the wiki text which means the tiddler will be redisplayed from scratch.

However, tiddlywiki has a syntax which allows parameter values to vary without modifying the wiki text. See https://tiddlywiki.com/#Widgets%20in%20WikiText for details. As one example, if the widget were called like <$hello message={{MyTiddler!!field}}/>, then every time the field field of MyTiddler were modified, only the refresh method would be called. Therefore, in order to get the widget display to update, the refresh method needs to be implemented.

If the computeAttributes and getAttribute calls are placed in the render method then we can implement a performance unoptimized version of refresh as was done in Widget refresh tutorial part II:

/*
A widget with optimized performance will selectively refresh, but here we refresh always
*/
MyWidget.prototype.refresh = function(changedTiddlers) {
	// Regenerate and rerender the widget and
	// replace the existing DOM node
	this.refreshSelf();
	return true;
};

The full code can be seen at hello-attribute.js and here is the result (Widget attributes demo I):

[ { "title": "$:/DefaultTiddlers", "text": "[[hello world widget]]" } ] [ { "title": "hello-attribute.js", "text": "/*\\\n\nHello, World widget\n\n\\*/\n(function() {\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nvar Widget = require(\"$:/core/modules/widgets/widget.js\").widget;\n\nvar MyWidget = function(parseTreeNode, options) {\n\tthis.initialise(parseTreeNode, options);\n};\n\n/*\nInherit from the base widget class\n*/\nMyWidget.prototype = new Widget();\n\n/*\nRender this widget into the DOM\n*/\nMyWidget.prototype.render = function(parent, nextSibling) {\n\tthis.parentDomNode = parent;\n\tthis.computeAttributes();\n\tvar message = this.getAttribute(\"message\", \"World\");\n\tvar textNode = this.document.createTextNode(\"Hello, \" + message + \"!\");\n\tparent.insertBefore(textNode, nextSibling);\n\tthis.domNodes.push(textNode);\n};\n\n/*\nA widget with optimized performance will selectively refresh, but here we refresh always\n*/\nMyWidget.prototype.refresh = function(changedTiddlers) {\n\t// Regenerate and rerender the widget and\n\t// replace the existing DOM node\n\tthis.refreshSelf();\n\treturn true;\n};\n\nexports.hello = MyWidget;\n\n})();\n", "created": "20190204020011193", "modified": "20190204030332147", "module-type": "widget", "tags": "", "type": "application/javascript" } ] [ { "title": "hello world widget", "text": "\n```\n<$hello/>\n```\n\nRenders as:\n\n<$hello/>\n\n---\n\n```\n<$hello message=\"pale blue dot\"/>\n```\n\nRenders as:\n\n<$hello message=\"pale blue dot\"/>\n\n---\n\n<$edit-text focus=yes tiddler=test tag=input/>\n\n```\n<$hello message={{test!!text}}/>\n```\n\nRenders as:\n\n<$hello message={{test!!text}}/>\n\n" } ] [ { "title": "test", "text": "Alice" } ]

The third example above is the only one which requires the refresh method in order to behave properly.