-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The $let widget #6148
The $let widget #6148
Changes from 3 commits
1b7b77a
47b6ca2
14ab533
edaf3b1
36cffc9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/*\ | ||
title: $:/core/modules/widgets/let.js | ||
type: application/javascript | ||
module-type: widget | ||
|
||
This widget allows defining multiple variables at once, while allowing | ||
the later variables to depend upon the earlier ones. | ||
|
||
``` | ||
\define helloworld() Hello world! | ||
<$let currentTiddler="target" value={{!!value}} currentTiddler="different"> | ||
{{!!value}} will be different from <<value>> | ||
</$let> | ||
``` | ||
|
||
\*/ | ||
(function(){ | ||
|
||
/*jslint node: true, browser: true */ | ||
/*global $tw: false */ | ||
"use strict"; | ||
|
||
var Widget = require("$:/core/modules/widgets/widget.js").widget; | ||
|
||
var LetWidget = function(parseTreeNode,options) { | ||
// Initialise | ||
this.initialise(parseTreeNode,options); | ||
}; | ||
|
||
/* | ||
Inherit from the base widget class | ||
*/ | ||
LetWidget.prototype = Object.create(Widget.prototype); | ||
|
||
/* | ||
Render this widget into the DOM | ||
*/ | ||
LetWidget.prototype.render = function(parent,nextSibling) { | ||
// Call the constructor | ||
Widget.call(this); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this call is now superfluous, likewise in $vars, otherwise it looks good. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done. |
||
this.parentDomNode = parent; | ||
this.computeAttributes(); | ||
this.execute(); | ||
this.renderChildren(parent,nextSibling); | ||
}; | ||
|
||
LetWidget.prototype.computeAttributes = function() { | ||
// Before computing attributes, we must make clear that none of the | ||
// existing attributes are staged for lookup, even on a refresh | ||
var changedAttributes = {}, | ||
self = this; | ||
this.currentValueFor = Object.create(null); | ||
$tw.utils.each(this.parseTreeNode.orderedAttributes,function(attribute,index) { | ||
var value = self.computeAttribute(attribute), | ||
name = attribute.name; | ||
if(name.charAt(0) !== "$") { | ||
// Now that it's prepped, we're allowed to look this variable up | ||
// when defining later variables | ||
self.currentValueFor[name] = value; | ||
} | ||
}); | ||
// Run through again, setting variables and looking for differences | ||
$tw.utils.each(this.currentValueFor,function(value,name) { | ||
if (self.attributes[name] !== value) { | ||
self.attributes[name] = value; | ||
self.setVariable(name,value); | ||
changedAttributes[name] = true; | ||
} | ||
}); | ||
return changedAttributes; | ||
}; | ||
|
||
LetWidget.prototype.getVariableInfo = function(name,options) { | ||
// Special handling: If this variable exists in this very $vars, we can | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should it be |
||
// use it, but only if it's been staged. | ||
if ($tw.utils.hop(this.currentValueFor,name)) { | ||
return { | ||
text: this.currentValueFor[name] | ||
}; | ||
} | ||
return Widget.prototype.getVariableInfo.call(this,name,options); | ||
}; | ||
|
||
/* | ||
Refresh the widget by ensuring our attributes are up to date | ||
*/ | ||
LetWidget.prototype.refresh = function(changedTiddlers) { | ||
var changedAttributes = this.computeAttributes(); | ||
if(Object.keys(changedAttributes).length) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The usual pattern in the core is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll change this in vars too, which doesn't use this pattern. |
||
this.refreshSelf(); | ||
return true; | ||
} | ||
return this.refreshChildren(changedTiddlers); | ||
}; | ||
|
||
exports["let"] = LetWidget; | ||
|
||
})(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
title: LetWidget | ||
created: 20211028115900000 | ||
tags: Widgets | ||
caption: let | ||
|
||
! Introduction | ||
|
||
The <<.wid let>> widget allows multiple variables to be set in one operation. In some situations it can result in simpler code than using the more flexible <<.wlink SetWidget>> widget. It differs from the <<.wlink VarsWidget>> widget in that variables you're defining may depend on earlier variables defined within the same <<.wid let>>. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO should start with: |
||
|
||
! Content and Attributes | ||
|
||
The content of the <<.wid let>> widget is the scope for the value assigned to the variable. | ||
|
||
|!Attribute |!Description | | ||
|//{attributes not starting with $}// |Each attribute name specifies a variable name. The attribute value is assigned to the variable | | ||
|
||
Attributes are evaluated in the order they are written. Attributes with the same name are allowed. Each time a duplicate attribute is encountered, it will replace the existing value set by the earlier duplicate. | ||
|
||
! Examples | ||
|
||
Consider a case where you need to set multiple variables, where some depend on the evaluation of others. | ||
|
||
Using the <<.wid let>> widget, this situation may be handled in the following way: | ||
|
||
``` | ||
\define helloworld() Hello world! | ||
|
||
<$let target="MyTiddler" currentTiddler={{{ [<target>prefix[$:/settings/for/]] }}} settings={{!!text}} currentTiddler=<<target>> > | ||
The settings for <<currentTiddler>> are: <<settings>> | ||
</$let> | ||
``` | ||
|
||
In contrast, here is the same example using the <<.wid set>> widget: | ||
|
||
``` | ||
<$set name="target" value="MyTiddler" > | ||
<$set name="currentTiddler" value={{{ [<target>prefix[$:/settings/for/]] }}} > | ||
<$set name="settings" value={{!!text}} > | ||
<$set name="currentTiddler" value=<<target>> > | ||
The settings for <<currentTiddler>> are: <<settings>> | ||
</$set> | ||
</$set> | ||
</$set> | ||
</$set> | ||
``` | ||
|
||
! Remarks | ||
|
||
This widget differs from <<.wid vars>> in the following way: | ||
|
||
* Each variable's definition will be immediately available to all proceeding variables in the same let widget. This differs from vars, in which definitions which depend on some variable will always look to the widget's outer scope for a value. | ||
|
||
This widget differs from <<.wid set>> in the following ways: | ||
|
||
* A fallback (also known as "emptyValue") cannot be specified | ||
* Filters cannot be used to produce a conditional variable assignment | ||
* Variable names must be literal strings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pattern we follow in all the other widgets is to call
new Widget()
here, which obviates the need to call the constructor later.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall I change $vars over too? I copied this code from there.