Initialize document data source by key attribute (instead of UNID) - xpages

We have documents containing "key" of some referenced document. There is reason we don't use UNIDs. I want to initialize data source by this key value. So in Document ID property I write script to open view, look up specified document and use looked up UNID to initialize data source.
I think this is not optimal solution.
Question: is there a better way to initialize document data source based on key value?
Sample code:
#DbLookup("", "view", "key", 1, '[RETURNDOCUMENTUNIQUEID]');

Looking up the document's UNID by key in a view is probably indeed the best way to do it. However, you could speed up repeated calls a bit by writing a managed bean to act as a cache. If, for example, you wrote a Java class that implements java.util.Map, stub out most of the methods, and implement a .get(...) method that takes the key as a parameter, you could reference it like (assuming you call the bean "DocKeyManager"):
<xp:dominoDocument ... documentId="${DocKeyManager[someKey]}"/>
That way, you could cache the value from the .get(...) call and not have to hit the database each time, and it'd also let you change the lookup algorithm later.

You should take a look at Tims article about converting strings to MD2.
this way I think you can convert your string id's to a unid and access them using getDocumentByUnid
http://xmage.gbs.com/blog.nsf/SearchResults?OpenNavigator&Query=md2

We do this a lot, but just remember to do the lookup once (on page load via the $ tag) instead of dynamic (# tag).
If you do it dynamic it will end up doing multiple lookups . . .
The suggestion from Jesse Gallagher to cache lookup results is also a good idea.

Related

Can i get all components of an xsp document in xpages?

I have a simple document with 3 fields and 1 rich text field. I also have an xpage with 3 simple edit box controls and 1 rich text. The name of my NotesXSPDocument is document1.
Question 1:
Can i get a vector with all the controls of the xsp document? for example, instead of using getComponent("fld1"), getComponent("fld2") ... etc, can i use something like getAllComponents() or document1.getControls()? These methods do not exist of course so i am asking if there is a way to do it. I know i can get all items of a document (not XSP) by calling document1.getDocument().getItems(). IS there anything similar for xsp?
Question2:
Lets say we can get a vector as i described above. Then if i iterate through this vector to get each control's value, is there a method to check if it is rich text or simple text field?
Technically, yes, but not readily and this is one of those situations where there's likely a better way to approach whatever underlying problem it is you want to solve.
Nonetheless, if you're looking to get a list of inputs on the page, XspQuery is your friend: http://avatar.red-pill.mobi/tim/blog.nsf/d6plinks/TTRY-96R5ZT . With that, you could use "locateInputs" to get a List of all the inputs on the page, and then check their value method bindings to see if the string version is referencing your variable name. Error-prone and not pretty, but it'd work. Since they're property bindings, I don't think the startsWith filter in there would do what you want.
Alternatively, you could bind the components to something in a Java class from the start. I've been doing just such a thing recently (for a different end) and initially described it here: https://frostillic.us/f.nsf/posts/my-black-magic-for-the-day . The upshot is that, with the right cleverness for how you do your binding="" property, you could get a list of all the components that reference a property of a given object.
As for the second part of the question, if you DO get a handle on the components one way or another, you can check to see if it's a rich text control by doing "component instanceof com.ibm.xsp.UIInputRichText".
A bit complex but yes. facesContext.getViewRoot() is an UIViewRoot object so it has List<UIComponent> getChildren() method which returns its children.
However, since it's a tree-structure, some of its children will have additional children components. You have to traverse the entire tree to build a list of components you want to see.
For types, you can decide what type a component is by its class. For instance, UIInput is a text box, etc.

How to import two cc both contain compositeData?

I do not know that the question is right? Please do not take it your mind if it is crazy. Actually I am working on xpages application. There I need to do two things, that I want to add the picklist functionality and binding the dynamic data like field_1,field_2,field_3, ... upto n depands on customer choice.I am using the composite data for both custom controls. I can remove the picklist control's composite data and also I can do it by passing the scope variables. But that takes more time than the composite data.
I did not get any error. But the binded documents is not saving.
Is it possible to import the CCs that are having composite Data?
Code for first CC:-
<xc:viewpicklist datasrc="view1" dialogID="dialog1" dialogWidth="700px" dialogTitle="Pick this field value!!!">
<xc:this.viewColumn>
<xp:value>0</xp:value>
<xp:value>1</xp:value>
<xp:value>2</xp:value>
</xc:this.viewColumn>
</xc:viewpicklist>
Code for Second CC:-
<xc:BOM_Partinfo BOM_Partinfo="#{document1}"
TNUM="field#{index+1}" Desc="Desc#{index+1}" quan="Ea#{index+1}"
exp="exp#{index+1}" cap="cap#{index+1}" total="price#{index+1}"
RD="RD#{index+1}" m="manufact#{index+1}"
m_n="manufactnum#{index+1}">
</xc:BOM_Partinfo>
You can read information that is set in the properties of a custom control if it was static in the calling page:
var x = getComponent("yourcomponentid");
x.getPropertyMap().get("parametername");
but you want to propagate a data source from the outer control to the inner control...
You need to plan carefully. If you hand over the data source, then your custom control is dependent on a fixed set of fields in the data source (that would be a parameter of type com.ibm.xsp.model.DocumentDataSource). This would violate the encapsulation principles. So I would recommend you actually hand over data bindings - the advantage: you are very flexible what to bind to (not only data sources, but also beans and scope variables would work then). The trick is you provide the binding name as you would statically type it in (e.g. "document1.subject" or "requestScope.bla" ). In your control you then do
${"#{compositeData.field1}"}
${"#{compositeData.field2}"}
You need one for each field.
You cannot send a document data source to a custom control using composite data parameters.
You can try and use this script instead
http://openntf.org/XSnippets.nsf/snippet.xsp?id=access-datasources-of-custom-controls
Define data source in XP/CC where you want those CCs. Define parameter "dataSourceName" for both CCs. Inside each of them use EL "requestScope[compositeData.dataSourceName].fieldName" everywhere you want to bind to datasource.

session.evaluate and notesxspdocument

I have question regarding session.evaluate in SSJS. In a keyword document I have some #formula stored which does some conversion of data. Lets say this is would be:
#left(fieldname;2)
If the fieldname contained 'hello' this would result in 'he'. Nothing to fancy here. Now I would like to use this in an xpage.
I wrote a function called executeFormula(doc). I call this function from an action on a xpage. This xpage contains 1 notes document datasource. The function call is
executeFormula(datasource.getDocument(true))
Now for some reason the #formula is never calculated correctly. Do I need to save the document first before I can use session.evaluate(kwFormula,doc) or is the #formula wrong in some way?
p.s. I forgot to mention that this code is working inside a customvalidator
Without seeing the code for the executeFormula(doc) function it is very difficult to know exactly how session.evaluate is being called.
I would suggest taking the function out of the equasion for the moment and create a simple test page with the document source and a simple computed field with the session.evaluate in it so that you can see the result. Given your examples above the computed field would be something along the lines of
session.evaluate("#Left(fieldname;2)",xspDoc.getDocument(true));
Once you get acceptable results back then you can move it into your function and verify that it is working there also.
Don't forget that session.evaluate returns a vector so you may beed to do a .getFirstElement() on the returned value if it is not null.
If you're using it in a custom validator, the values posted from the browser/client haven't updated the data model (in your case, the document) yet. This happens after validation is successful.
I imagine it might work for some fields (e.g. fields that are updated after a successful refresh, or stored fields in an existing document).
Actually no need of mentioning the document, eg:- session.evaluate("#username") is enough.
For yours session.evaluate("#left('hello';2)") will work.,

NSArrayController that is sorted and unique (no duplicates) for use in a pop-up in a core-data app

I have core data app with an entity OBSERVATION that has as one of its attributes DEALNAME.
I want to reference through Interface Builder or by making custom modifications to an NSArrayController a list of unique sorted dealnames so that I can use them in a pop-up.
I have attempted to use #distinctUnionOfSets (and #distinctUnionOfArrays) but am unable to locate the proper key sequence.
I can sort the ArrayController by providing a sort descriptor, but do not know how to eliminate duplicates.
Are the #distinct... keys the right methodology? It would seem to provide the easiest way to optimize the use of IB.
Is there a predicate form for removing duplicates?
Or do I need to use my custom controller to extract an NSSet of the specific dealnames, put them back in an array and sort it and reference the custom array from IB?
Any help would be appreciated. I am astounded that other have not tried to create a sorted-unique pop-up in tableviews.
You need to take a look at -[NSFetchRequest returnsDistinctResults]. That is the level you need to be handling the uniquing of data.
Although I do not have a definitive answer for you, I think there are two ways you can go about it.
The way you already started. You need to bind the contents array of the PopUp button, not just against the arrayController.arrangedObjects, but continue on the path and somehow filter only objects with distinct "DealName"s. This means - the arrayController presents ALL the entities (and may sort them for you) but the PopUp button will have its contents filter via some sophisticated binding to the array controller.
Make your filtering at the ArrayController level (as suggested in another answer here). Here it depends how you set up the array controller. If It is set up to use an "Entity" (vs. "Class") which means the array controller will fetch CoreData entities directly - you can modify its "Fetch" to only bring a subset of the "OBSERVATION" entities with distinct values of "DEALNAME". I don't know how to control WHICH entities are filtered out in this case. Otherwise, you can setup the arrayController to work with "Class" objects, and then you can fetch the entities yourself (in code) and populate the arrayController programmatically, with just the entities you like.
In the second option, the Popup button should be bound normally to the arrayController's arrangedObjects.

non-database field on ClearQuest form

Is there a way to use form fields that does not correspond to database field for temporary processings?
I.e. I want to add:
temp fields item1, item2
database field sum
button with record hook that sets sum = item1 + item2
As far as I know it's simply not possible with ClearQuest.
I've tried to do something similar and was told by our IBM consultant that the only way is to create a DB field for all variables.
You can't attach data to form fields really - those are representations of the underlying data, not something scripts interact with directly.
Adding temporary data to the underlying record (entity) itself sounds unlikely as well. Perhaps it's possible to abuse the perl API and dynamically attach data to entity objects but I personally wouldn't try it, you're liable to lose your data at the whim of CQ then ;-)
That does not however mean it's impossible to have temporary data.
The best way seems to me to be using the session object, which is explicitly intended for that purpose.
From the helpfile:
IBM Rational ClearQuest supports the
use of sessionwide variables for
storing information. After you create
sessionwide variables, you can access
them through the current Session
object using functions or subroutines,
including hooks, that have access to
the Session object. When the current
session ends, all of the variables
associated with that Session object
are deleted. The session ends when the
user logs out or the final reference
to the Session object ceases to exist.
There's some helpful documentation on this subject in file:///C:/Program%20Files/Rational/ClearQuest/doc/help/cq_api/c_session_vars.htm (Presuming a default installation on a windows machine, of course.)
Translating the code example in there into what you seem to be wanting, first you store the data you have calculated inside the session object:
$session->SetNameValue("item1", $value1);
$session->SetNameValue("item2", $value2);
Then in your calculation hook you retrieve the stored values and set the value of that totals field like this:
my $item1 = GetNameValue("item1");
my $item2 = GetNameValue("item2");
my $sum = $item1 + $item2;
$entity->SetFieldValue("some_totals_record", $sum);
Adjust to taste of course ;-)
ClearQuest schema designers often include 'temporary' fields in their record types. They do this so they perform operations on hooks to generate another value.
For example, for the Notes fields, there is a 'temporary' Notes_entry field that the user types the most recent note into, and when the record is saved, the value is added to the Notes_Log field. The next time the record is edited the Notes_entry field is cleared so the user can type a new Notes_entry.

Resources