Components
In general it is not opinionated on how you organize your components as files or as functions. This sub-package is optional and represents only one of the possible ways.
One of the key features is idea of reusable components borrowed from React. There is an in-build structure to embed other WLX scripts into the each other as Wolfram Language expressions
<<JerryI`WLX`Importer
Then you can import other wlx
scripts as components into your other scripts, which becomes basically a crafted WL function in the end.
Another benefit is to join the data representation template, controllers and other logic into a single component. It does not necessarily mean a single file, but a folder as well.
Passing named XML attributes
Let us start from the example. We have a component, that makes italic labels
Text = $Options["Text"];
<i>
<TextString>
<Text/>
</TextString>
</i>
Then, one can import it in some other .wlx
file
Label := ImportComponent["label.wlx"]
<body>
<Label Text={"Hello World"}/>
</body>
The difference between Set
and SetDelayed
is the same as in Wolfram Language. If imported component has to generate something with passed arguments - use SetDelayed
, if it generates the result immediately or other function - use Set
.
It looks similar to what we had in Basics / Options.
This is done in a way, that feels like a regular XML attribute. Or one can pass any valid Wolfram Expression as well
Label := ImportComponent["label.wlx"]
<body>
<Label Text={Now}/>
</body>
Do not be afraid to import many nested components, since the result of ImportComponent
is parsed once into a regular Wolfram Expression and stored in cache. See Caching
Passing children as down-values
To construct a component that uses a normal tags with possibly nested structure - use SetDelayed
on ImportComponent
. See an example
<i>
<TextString>
<$Children/>
</TextString>
</i>
Then modify the main script as
Label := ImportComponent["label.wlx"]
<body>
<Label>
<Now/>
</Label>
</body>
Now Label
works like a regular symbol with down-values, i.e. one can also do that
Label[Now]
There are a few predefined keywords to have an access to the child elements
$FirstChild
Gets the first passed argument
$Children
Gets all passed arguments (-child elements) as a list
$Options
Get all options passed to a component
Component with internal state
Quite often one might find useful to keep the whole logic within a single component. Needless to say to store an internal state this construction comes handy
(*/* !!! Will be executed once and stored */*)
State = False;
Component[OptionsPattern[]] := With[{Mutator = OptionsValue["Mutator"]},
(*/* !!! Will be executed each time you call */*)
State = Mutator[State];
(*/* so something with a given parameters and etc... */*)
<h1>
<State/>
</h1>
]
Options[Component] = {"Mutator" -> Identity}
Component (*/* exported */*)
Here the last line returns our generated function. In the main file we can import it once using basic Set
expression, i.e.
MyComponent = ImportComponent["Component.wlx"];
mutate[state_] := !state;
...
requestHandler[__] := With[{},
<body>
<MyComponent Mutator={mutate}/>
</body>
];
...
It comes handy, when you need to generate multiple responses for incoming requests and share the internal state of a single component between.
Export multiple entities
It is also absolute valid to return more than a single symbol like if it was a notebook's cell
State = ...
Component[OptionsPattern[]] := ...
Script = ...
Whatever = ...
...
{Component, Script, Whatever} (*/* exported */*)
{MyComponent, MyScript, MyWhatever} = ImportComponent["Component.wlx"];
*technically speaking we still got a single symbol in return, which is called List
;) *
Use it if you have controllers, views or Javascript code which has to be imported as a separate symbols
Context manipulation
When designing a package or paclet or just for local context isolation, all Wolfram Language built-in tools are valid to use here as well
Begin["MyContext`"]
Component = ImportComponent["Compoment.wlx"]
End[]
MyContext`Component
All rules are applied and variables generated in Component
will be in the given context.
Scoping
By the default it parses a script with Localize
option (see scoping), but one the importing function accepts this option pattern as well, so you can override it
Label := ImportComponent["label.wlx", "Localize"->False]
This is not recommended, since it will leak into the global scope
Caching
For the development / prototyping the caching is disabled. To improve the performance and lower the load for IO operations on a disk for many nested component - use global settings
JerryI`WLX`Importer`CacheControl[True]
or specify the time-interval
JerryI`WLX`Importer`Private`CacheControl["Minute"]
JerryI`WLX`Importer`Private`CacheControl["Hour"]
Path resolution
UNIX / WIN / WEB
To overcome an issue with different path representation implementations, a universal platform-dependent converter is used. Therefore ImportComponent
is indifferent for the way how you write the path to a file.
Nested folders
When you import a component inside other component, an automatic guess happens if the given file is located in the same folder or an absolute path is used, i.e.
- Root
- Main.wlx
- Label
- Label.wlx
- Components
- Someother.wlx
and
...
... = ImportComponent["Label/Label.wlx"]
...
... = ImportComponent["Components/Someother.wlx"]
are valid.