I am modifiyng exisiting code which currently does
currentDocument.setValue("field", requestScope.variable);
This works well, but I now need to make it a little more dynamic so, I have the requestScope.variable stored as a string. I know I can do:
requestScope["variable"]
but this relies on me knowing which scope it is in. Given that the string could be requestScope.variable or even applicationScope.variable I think this route is closed to me.
I had also thought of splitting the string based on the . but think that there must be a more straightforward option.
How can I go about doing this?
Use ${javascript:'requestScope.variable'} to get the value of the scope variable which is defined as a string.
Your example would be
currentDocument.setValue("field", ${javascript:'requestScope.variable'});
${javascript:...} works here a preprocessor and inserts the result into the code.
Here is a complete example:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.beforePageLoad><![CDATA[#{javascript:
sessionScope.test = "this is the value of sessionScope.test";
viewScope.other = "this is the value of viewScope.other";
requestScope.myScopeVariable = "viewScope.other";
}]]></xp:this.beforePageLoad>
<xp:text
escape="true"
id="computedField1"
value="#{javascript:
var valueFromSessionScope = ${javascript: 'sessionScope.test'};
var valueFromViewScope = ${javascript: requestScope.myScopeVariable};
valueFromSessionScope + ' ... ' + valueFromViewScope;
}">
</xp:text>
</xp:view>
It shows as result:
this is the value of sessionScope.test ... this is the value of viewScope.other
Hmm, I've never seen requestScope["variable"] though that makes sense. I typically use something like requestScope.get("variable").
Also my scoped variables always get a prefix to indicate the scope. rsVariable, ssVariable, vsVariable, asVariable. If you did something like that then you could maybe inspect your variable name and pick the right scope. BUT the reason I did that is I had some kind of scope collision once. I forget the circumstances. But remember, XPages does a lot to "resolve the variable". I'm not fully sure how this "variableRolver" as we call it works in SSJS. But similar to how I had that problem once, there's likely someway to get a variable without knowing the scope.
Worse case, as long as your variables have unique names, remember that scoped variables are just Java HashMaps. So you can do something like : viewScope.containsKey("ssMyVariable") so see if it exists in that scope.
Just some quick thoughts.
Related
The asp-items Razor "TagHelper" will add an <option> to a <select> for each value in the SelectList. I want to modify each of those children.
Specifically I want to disable some of them (i.e. add disabled="disabled").
Even more specifically I want to dynamically disable some of them; I'm using angular so I could ng-disabled="{dynamic_boolean_which_determines_disabled}". This means the option could be disabled at first, but after user makes a change, the option could be disabled (without page reload). Angular should take care of this; I think Angular and TagHelpers should work together in theory...
I expected:
I could somehow access an IEnumerable of the children <option> tags that would be created (i.e. one for each item in the SelectList), iterate the children tags, and SetAttribute("disabled") or SetAttribute("ng-disabled")...
I tried:
Creating my own TagHelper which targets the select[asp-items], and tries to GetChildContentAsync() and/or SetContent to reach an IEnumerable <option> tags and iterate them and process each, but I think this will only let me modify the entire InnerHtml as a string; feels hacky to do a String.replace, but I could do it if that's my only option? i.e. ChildrenContent.Replace("<option", "<option disabled=\"...\"")
Creating my own TagHelper which targets the option elements that are children of the select[asp-items], so I can individually process each. This works, but not on the dynamically-added <option> created by asp-items, it only works on "literal" <option> tags that I actually put into my cshtml markup.
I think this'll work but not ideal:
As I said above, I think I can get the result of TagHelper's dynamic asp-items <option></option> <option></option>, as a string, and do a string replace, but I prefer not to work with strings directly...
I suspect (I haven't tried it) that I could just do the work of asp-items myself; i.e. custom-items. But then I'm recreating the wheel by re-doing the work which asp-items could've done for me?
So I hadn't yet read the "AutoLinkHttpTagHelper" in the example which uses string replacement (specifically RegEx replace) to replace every occurrence of a URL, with an <a> pointed at that URL. The cases are slightly different*, but...
Anyway, here's my solution once I learned to stop worrying and love the string modification:
[HtmlTargetElement("select", Attributes = "asp-items")]
public class AspItemsNgDisabledTagHelper : SelectTagHelper
{
//Need it to process *after* the SelectTagHelper
public override int Order { get; } = int.MaxValue;
//https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/authoring#ProcessAsync
public AspItemsNgDisabledTagHelper(IHtmlGenerator gen) : base(gen) {}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
//Notice I'm getting the PostContent;
//SelectTagHelper leaves its literal content (i.e. in your CSHTML, if there is any) alone ; that's Content
//it only **appends** new options specified; that's PostContent
//Makes sense, but I still wasn't expecting it
var generated_options = output.PostContent.GetContent();
//Note you do NOT need to extend SelectTagHelper as I've done here
//I only did it to take advantage of the asp-for property, to get its Name, so I could pass that to the angular function
var select_for = this.For.Name;
//The heart of the processing is a Regex.Replace, just like
//their example https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/authoring#inspecting-and-retrieving-child-content
var ng_disabled_generated_options = Regex.Replace(
generated_options,
"<option value=\"(\\w+)\">",
$"<option value=\"$1\" ng-disabled=\"is_disabled('{select_for}', '$1')\">");
//Finally, you Set your modified Content
output.PostContent.SetHtmlContent(ng_disabled_generated_options);
}
}
Few learning opportunities:
Was thinking I'd find AspForTagHelper and AspItemsTagHelper, (angular background suggested that the corresponding attributes; asp-for and asp-items, would be separate "directives" aka TagHelper).
In fact, TagHelper "matching" focuses on the element name (unlike angular which can match element name... attribute... class... CSS selector)
Therefore I found what I was looking for in SelectTagHelper, which has For and Items as properties. Makes sense.
As I said above, I extend SelectTagHelper, but that's not necessary to answer my original question. It's only necessary if you want access to the this.For.Name as I've done, but there may even be a way around that (i.e. re-bind its own For property here?)
I got on a distraction thinking I would need to override the SelectTagHelper's behavior to achieve my goals; i.e. Object-Oriented Thinking. In fact, even if I did extend SelectTagHelper, that doesn't stop a separate instance of the base SelectTagHelper from matching and processing the element. In other words, element processing happens in a pipeline.
This explains why extending and calling base.Process(), will result in Select doing its job twice; once when your instance matches, and again when the base instance matched.
(I suppose could've prevented SelectTagHelper from matching by creating a new element name like <asp-items-select>? But, not necessary... I just avoid calling base.Process(). So unless that's a bad practice...)
*Different in this way:
They want to create a tag where none exists, whereas I want to add an attribute a tag which is already there; i.e. the <option>
Though the <option> "tag" is generated by the SelectTagHelper in its PostContent (was expecting to find it in Content), and I don't think tags-generated-in-strings-by-content-mods can be matched with their corresponding TagHelper -- so maybe we really are the same in that we're just dealing with plain old strings
Their "data" aka "model" is implied in the text itself; they find a URL and that URL string becomes a unit of meaning they use. In my case, there is an explicit class for Modeling; the SelectList (<select>) which consists of some SelectListItem (<option>) -- but that class doesn't help me either.
That class only gives me attributes like public bool Disabled (remember, this isn't sufficient for me because the value of disabled could change to true or false within browser; i.e. client-side only), and public SelectListGroup Group -- certainly nothing as nonstandard as ng-disabled, nor a "catch-all" property like Attributes which could let me put arbitrary attributes (ng-disabled or anything else) in there.
I have an object stored in the ViewScope: ObjectName (valueA:one, valueB:two)
I stored the values using Java:
ObjectObject location = new ObjectObject();
location.put("valueA", FBSUtility.wrap("one"));
location.put("valueB", FBSUtility.wrap("two"));
Utils.setViewScope("ObjectName", location);
How would I go about retrieving these values from the ViewScope? I've tried doing something like the following:
ObjectObject location;
location = (ObjectObject) ExtLibUtil.getViewScope().get("ObjectName");
but I'm not sure what methods to use to get the values or if this is even the correct path. Thanks in advance for any help.
It's a bit unusual to go out of your way to use the FBS classes, but that path is reasonable enough to accomplish what you want. As long as the latter code is executed after the format, that should retrieve the same object and properly cast it to ObjectObject. After that, you could use location.get("valueA"), etc. to get the values by name, and then whatever methods of FBSValue are appropriate (I'd guess stringValue()).
Incidentally, unless you have a specific need to use these internal classes (like if you're doing something fancy with SSJS functions), it may make sense to use just a normal HashMap<String, Object> instead. SSJS and EL can work with those quite well.
I have a problem with my puppet script.
I would like to get a value set in my resource file. I declare a resource like that
define checkxml(
$account = '',
$pwd = template('abc/abc.erb'),
){
if(empty($pwd)){
fail('pwd empty')
}
}
I call it via :
checkxml{"$agtaccount":
account => $agtaccount,
}
I want to get the value of $pwd. The $pwd will get is value by Template. If i try to show the value in my resource definition it's ok, I get the right value, so the Template works fine.
My problem is to get access this value after calling the ressource. I saw the getparam of stdlib but doesn't work for me.
getparam(Checkxml["$agtaccount"],"pwd")
If i try to get the account parameters instead of pwd it's ok. I think as i doesn't declare the pwd i can't get him back
How can i get him ?
Thanks for your help
Ugh, this looks dangerous. First off I'd recommend to steer clear of that function and the concept it embodies. It faces you with evaluation order dependencies, which can always lead to inconsistent manifest behavior.
As for the retrieval of the value itself - that will likely not work if the default is used. That's because on a catalog building level, there is not yet a value that is being bound to the parameter, if that makes any sense.
The resolution of final parameter values is rather involved, so there are lots of things that can go wrong with a manifest that relies on such introspective functionality.
I recommend to retrieve the desired value in a more central location (that depends on your manifest structure) and use it both when declaring the Checkxml["$agtaccount"] resource as well as its other uses (for which you are currently trying to extract it).
I have nillable decimal element defined in xsd schema like this:
<xs:element name="myDecimalValue" nillable="true" type="xs:decimal" />
This is distinguished field and I want to check if it is nill in expression shape. I could use xpath() function like this:
xpath("string(//*[local-name()='myDecimalValue']/#*[local-name()='nil'])") == "true"
But it looks a little bit complicated for simple null-checking. So I'm wondering if I'm missing something and there is better way to do it?
you can try to use a decide shape instead of the xpath
your decide shape will lok like this
just ask if myDecimalValue is null
msgName.myDecimalValue != null here
than you can continue your logic
if you using a distinguished field use it its more readable for other dev
EDIT:
can you try
varString = System.Convert.ToString(msgName.myDecimalValue);
and then ask if varString is null or not
i tried it and it compile well
hopes it help
:)
I have a suspicion that this can't be achieved.
Specifically, from http://support.microsoft.com/kb/942250
Properties that have a null value are not permitted in the message
context. Therefore, if a null value is written into the message
context, this value will be deleted.
(this is in the document section relating to Promoted and Distinguished properties, so I am assuming it is applicable to both).
So it looks like your xpath solution might be the required solution, as the distinguished property won't be in the message context anyway.
If I want to parse a text field in SSJS there are 2 main tools. The built in JavaScript code and the newly converted #Functions. Are the #Functions slower then using pure javascript? Or is there no real difference?
viewScope.put("length", tmpStr.length)
vs.
viewScope.put("length:, #Length(tmpStr))
All SSJS is parsed into an AST (abstract syntax tree) at runtime. In other words, your code just remains a String until the exact moment that it is executed, at which point a parser examines that String to syntactically identify what the code contains: which characters denote variables, which are operators, functions, etc. Once that parsing is complete, the runtime engine is able to run Java code that is a rough approximation of what the JavaScript code was designed to do.
This is why SSJS is always slower than the directly equivalent Java: if you just write your code in Java to begin with, then it's compiled into bytecode the moment you build your project, but perhaps more importantly, at runtime it doesn't have to "guess" what code to run by parsing a String... it just runs the Java code you already defined.
On the other hand, there's nothing about this process that significantly distinguishes the SSJS implementation of various #Functions from "native" JavaScript; given that #Length(tmpStr) is just a wrapper for tmpStr.length, it doesn't surprise me that Sven is seeing a difference in execution time given enough iterations. But if your goal is optimization, you'll gain far more improvement by moving all code from SSJS blocks to bean methods than you will by eschewing the convenience of #Functions in favor of native JavaScript, because even native JavaScript has to be parsed into an AST. In that sense, there is no fundamental difference between the two.
UPDATE: there's a slight caveat to the AST parsing mentioned at the beginning of this answer. By default, the XPages runtime caches up to 400 unique SSJS expressions (you can override this limit via the ibm.jscript.cachesize property in the server's xsp.properties file). So if an expression is encountered that matches exactly (including whitespace) one that is already cached, Domino doesn't have to construct a new AST for that expression; it just references the tree already in cache. This is a MRU ("most recently used") cache, so the more frequently the same expression is encountered, the more likely it is to remain in the cache. Regardless of whether the AST is cached, it still has to be evaluated against the current context, and some of the JavaScript wrapper objects do have additional overhead compared to what you'd likely use instead if you were just coding directly in Java (for instance, {} becomes an ObjectObject, which is similar to a HashMap, but has additional features that support closures, which are just wasted if you're not using closures anyway). But the primary performance implication of this AST cache is that, unlike in most development contexts, duplication of code can actually be a good thing, if only in the sense that using the same exact expression over and over again allows all but the first instance of each to skip the language parsing and jump straight to invocation.
For me it seems that the #Formula is not as fast as using SSJS-code.
You can easily test by yourself with some code like this one (reload page multiple times to get serious results):
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:label id="label1">
<xp:this.value>
<![CDATA[#{javascript:
var start = java.lang.System.currentTimeMillis();
var testString = "0123456789";
var dummy;
for( var i=0; i<100000; i++ ){
dummy = #Length( testString )
}
var stop = java.lang.System.currentTimeMillis();
stop - start + " ms"}]]>
</xp:this.value>
</xp:label>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label id="label2">
<xp:this.value>
<![CDATA[#{javascript:
var start = java.lang.System.currentTimeMillis();
var testString = "0123456789";
var dummy;
for( var i=0; i<100000; i++ ){
dummy = testString.length;
}
var stop = java.lang.System.currentTimeMillis();
stop - start + " ms"}]]>
</xp:this.value>
</xp:label>
</xp:view>
I dont think #formula in SSJS is as fast as traditional #formula. One of the reasons would be that #formula is just another layer on top of SSJS functionality and therefore there is a bit more code to execute.
But that's just a wild guess.
As far as #DBLookup, I did some testing and found it was significantly slower then getting the view in SSJS, then doing a getDocumentByKey and getting fields from the found NotesDocument. At least 10 times slower when I looped four #DBLookups 100 times vs. getting the document 100 times and then getting the four fields.
Howard
tmpStr.length is browser native function (not in Javascript but code inside the browser and compiled)
#Length(tmpStr) is a javascript function, so it is interpreted by the browser