Archive

Posts Tagged ‘Parameter’

XPages: JQuery Gantt Module

16/09/2012 9 comments

In the past weeks I was trying something new, at least new for me…

I realised that using jQuery in Xpages is a wonderful idea. Mark Roden has a nice blog with great things you can do with jQuery in XPages. You can find it here: Xomino Also check out his demo site: Xomino JQinX Demo

In one project I wanted to use a gantt chart. I want to write this article to show you how it’s done and warn you about the problems I had.

Let’s start.

To use jQuery you have to download it and integrate it in your database. Build your own version of jQuery here: JQuery Download It is the download page of JQuery UI, a set of many nice plugins and modules for jQuery. A version of the base jQuery is included. You can also configure a custom theme for your jQuery modules. It is on the right menu when you follow the link “design a custom theme

For the gantt chart I am using this module: Taitems JQuery Gantt Chart Module

So, now you have to integrate these files into your database. You can do this either via the resource category in the database, or via the PackageExplorer (it is an Eclipse View) I recommend using the package explorer, because you can manage your files more easily. Simply drag and drop your files and create some folders to organize them.

Now you have to load them into your application. You can do this easily via a theme with the following statement:

<resource>
<content-type>application/x-javascript</content-type>
<href>js/jquery-1.7.min.js</href>
</resource>

Or you load them directly into your code via the resource properties of the XPage/ Custom Control

<xp:script src="js/jquery-ui-1.8.23.custom.min.js" clientSide="true"/>

You have to do this for the jQuery base module, the UI module and the gantt module, also you have to load the CSS files.

First problem I had to face: The gantt module doesn’t work with every jQuery version. I was using jQuery 1.8.0. But when using this module, I had an error message which complained about some undefined variables (TypeError: m is undefined) Using the 1.7 version was a solution to this.

Now we can get started. On the page of the gantt module is an easy manual how to use this module. It is quite self explaining.

To implement a gantt chart, you only have to put in these few lines of code (client side of course)

<xp:scriptBlock id="scriptBlock1">
<xp:this.value>
// + "/ganttJSONBuilder?OpenAgent";
// + "/js/data.js";
jQuery( function() {
var dataPath = location.href.substring(0,
location.href.lastIndexOf('/') + 1)
+ "/ganttJSONBuilder?OpenAgent;

$(".gantt").gantt( {
source : dataPath
});
});]]>
<<!--xp:this.value>
<<!--xp:scriptBlock>

As data source the module uses JSON data. Use this site to validate your JSON data easily. The structure of your data is explained in the manual site of the module.

Be aware, the module expects a URL as data source. That means you can’t use server side Javascript in that scriptblock to generate the data source. So, we have two options on how to load the data into the module, either via a file which you can put into the WebContent folder in your database, or you call an agent. The agent is probably the better option because you sure want to load data dynamically.

Using an agent to achieve this is basically easy, you only have to write your JSON string into a variable and then you do this:

PrintWriter pw = getAgentOutput();
pw.println(output);

The difficulty of generating the JSON string itself depends on your data. In my case it was a simple project management, so some tasks, projects and editors. I used a view. I looped the entries and parsed the information into a JSON String.

There are some nice little things you should know when doing so.

The first line of your JSON string has to be the content type of the output, for JSON it is: Content-type: application/json; charset=utf-8\n
Also, it is very important that you use line breaks in the string, simply by using \n. If you aren’t doing that, your JSON string will not be properly interpreted and the module shows nothing but a nice grey bar.
Easily to overlook, but also important, in JSON you have to use a comma to separate your entries. Be sure you have no comma behind your last entry or parameter. It will result in a misinterpretion of the data.
The next important thing is, in the JSON data, you have to provide all entries defined in the manual. That means, if you forgot to write one of the parameters into the string, it is valid, but not recognized by the gantt module and isn’t doing anything. All of them need a value. providing nothing will have the same result: Nothing =)
I wanted to have my projects and tasks separated. In one line the project and then in the other lines the related tasks. This is possible by writing an extra entry to your JSON string, in which you only write the information about the project. When writing the tasks of the project, you probably don’t want to write the project in front of each task. If you think you can define an empty string for the name parameter of the JSON entry, you are wrong. The module needs a value of at least one character. Using an empty string results in not displaying the chart and some nice error messages. Using a space (” “) is the solution.
Also, you have to provide a start and end date for each entry. I wanted to have no duration for the project, so I didn’t provide the “from” and “to” parameter, it rewarded me with some other javascript errors. So I used the whole duration of the project for the project entry, it’s a good feature anyway.

A nice feature: You can use HTML in your JSON parameters, so, if you use
tags for example in your desc parameter and some formatting tags, you can make the mouseOver of the entries look more beautiful and put some more information into it.

If you think the styleClasses available for your entries in the chart (ganttRed, ganttGreen, ganttOrange) are not that beautiful, or you want to use some more, you can easily define some. There is a css file in the module. Looks like this:

.fn-gantt .ganttOrange {
background-color: #FCD29A;
}
.fn-gantt .ganttOrange .fn-label {
color: #714715 !important;
}

Copy those entries and do your own styles, the class at the end of the definition is recognized by the module, so you can use it directly in the “customClass” parameter of your JSON.

But there is one big problem when using this module. Above the chart, I have some buttons. I wanted to load different charts with those buttons. Easily done I thought, just firing a partial execute on the chart container to set another data source or something. But, nothing happens, in fact really nothing, not even an event which I could see in my FireBug. Some other buttons with only clientside code are working fine. All other buttons on the whole page are working fine, only those I have directly above my chart didn’t wanted to work. I haven’t figured out what’s the problem, if I find it, I will let you know.

Another problem is that the gantt chart is not working in the Notes Client. If I open the Xpage in my Notes client, the chart isn’t loaded, only a nice grey bar. Other jQuery modules are working fine, for example the accordion or flip is working, but not the module. If you want to use it in the NotesClient you should think about another solution… or wait until I find a solution for this problem =)

Another thing you should consider are loading times. The loading time is increasing dramatically proportional on how many entries you display (rowsPerPage parameter in the gantt function) and how many years they cover. If you want to display some tasks from a period of 10 years or more, you have to wait a bit. I tried a period of 30 years… not a good idea, my browser crashed… after about 5 minutes of loading.

So, I think that’s all I know about the module and how to use it. Despite all the small and big problems I have a nice application with some really nice gantt charts. If you are interested in such an application, you should visit this page (Cobalt Software GmbH – ToDo Manager), the new version should be available soon. Ok, sorry for the advertisement…

If you have any questions or solutions for my unsolved problems, the comment section is all yours =)

Hope it was useful for some of you.

Advertisements

XPages: Notes Objekte in Java Singleton Patterns

28/03/2012 3 comments

Ich hatte ja vor einer Weile in diesem Post am Ende von einem Problem berichtet, welches ich im XPages Forum gepostet habe.

Gestern habe ich mit Philippe Riand, dem Chef-Entwickler der XPages von IBM, über das Problem gesprochen. Hier ist die Erklärung:

Ich habe ja das NoteDatabase Objekt in der Klasseninstanz als Attribut gespeichert. D.h. dass bei jedem Request das gleiche Objekt verwendet wird, wie es ja auch bei einem Singleton Pattern sein soll.
Aber: Nach jedem Request wird ein NotesDatabase Objekt recycled, und zwar schon seit 8.5.1. Das hat einfach den Hintergrund, dass in dem NotesDatabase Objekt die Credentials des Users, der den Request abgeschickt hat, mitgespeichert werden. So würde, wenn User A die Funktion aufruft und die Klasse initialisiert, und dann User B die gleiche Funktion aufruft, die gleiche Instanz verwenden, in dem das NotesDatabase von User gespeichert ist. Würde somit die Funktion mit den Berechtigungen von User A aufrufen. Das darf natürlich nicht passieren. Daher wird, schon seit 8.5.1, ein NotesDatabase Objekt nach jedem Request recycled. Da das ganze in dem Singleton Pattern abläuft ist natürlich das Objekt noch da und nicht Null, hat aber keine Referenz mehr zur eigentlichen Datenbank und wirft somit den Fehler dass das Objekt removed oder recycled wurde.

Macht von dem Gesichtspunkt her natürlich auf jeden Fall Sinn, man muss es halt nur wissen.

Natürlich ist die Frage warum es dann bei mir unter 8.5.1 funktioniert hat. Die einzige Erklärung die wir finden konnten war, dass dies ein Bug unter 8.5.1 sein muss. Das sollte man denke ich auf jeden Fall mal bei sich in den Anwendungen überprüfen, da dies theoretisch ein ziemliches Sicherheitsrisiko sein kann.

Stattdessen sollte man, z.B. beim Aufruf der getInstance Methode die aktuelle Datenbank übergeben oder sich in der Java Klasse das Objekt direkt holen und jedes Mal neu setzen.

XPages: Useability – Parameter mit Aliaswerten in Custom Controls

Heute mal ein kleiner Tipp, welcher die Useability von Paremetern in selbst gebauten Custom Controls erhöhen kann.

Man kann für Custom Controls ja Parameter definieren, dies tut man in den Eigenschaften des Elements xp:view (root-Element des Controls) unter Property Definition.
Dort hat man die Möglichkeit für einen Parameter den Editor (die Art wie man bei der Einbindung den Parameter angibt) auf ComboBox zu setzen und dort mehrere Einträge mitzugeben, welche man dann bei der Einbindung in eine XPage oder ein anderes Custom Control auswählen könnte.

Natürlich möchte man es sich und anderen möglicht bequem machen und dort sprechende Vorgabewerte eintragen, damit man weiß was diese jeweils bedeuten. Dies ist auf der anderen Seite natürlich schlecht im Programmcode handlebar. Daher kann man einfach Aliaswerte benutzen.

Hier ein Beispiel:

Parameter 1 wird im Menü über eine ComboBox ausgewählt. Folgende Vorgabewerte werden angeboten:

0|Wert 1
1|Wert 2

Hier muss man mit dem Alias leider anders herum arbeiten als man es von Notes her gewohnt ist. Der Wert vor der Pipe (“|”) ist der Wert der tatsächlich übergeben wird, den man dann auch mittels compositeData azurückgegeben bekommt. Der zweite Wert dahinter wird dem User im Menü angezeigt.
Achtung: Dies ist lediglich eine optische Geschichte, d.h. der Aliaswert wird nur im Menü verwendet. D.h., wenn man den Parameter mit Aliaswerten berechnen möchte, so muss man wieder den richtigen Wert, welcher vor der Pipe angegeben wurde benutzen und zurückliefern.

Lediglich eine kleine optische Spielerei, erhöht aber den Komfort beim Benutzen von eigenen Custom Controls.

XPages: Collection of advices

Hi folks,

here is a small collection of some hints which were written by my colleagues and myself throughout time.

Naming of attributes in Internet Explorer

It seems, in the Internet Explorer, Dojo has some problems finding the correct element, while usingattribute selectors, if there is a string reserved by the system in the attribute. (e.g. name or id). Because Dojo uses the selector engine “Sizzle” like JQuery, the same phenomenon can appear using JQuery.

An example for that is an input-field with the name “username”. If you select dojo.query(‘input[id$=”WFApprovalVFL”]’) the field “username” will be selected, too, although it doesn’t end with WFApprovalVFL.

The same happens with keynames in JSON-objects.

Not rendered datasources will always be computed

If you use a datasource in a custom control, the code of the datasource will always be computed, no matter whether you set rendered or loaded to false. The code will always be computed. This could lead to errors hard to track down.

Furthermore, if you use several datasources in different custom controls, which point to the same document, all these datasources will be saved and, eventually, the document is saved more than once with saveing conflicts. To avoid this, you can reference the datasource in your custom controls directyl via the name, or you can pass on the datasource via a parameter of the custom control (compositeData). Therefore you have to select the class of the datasource, which doesn’t appear in the list of available types. The name of the class of a datasource is com.ibm.xsp.model.ModelDatasource.

Check whether a viewPanel is categorized

If we have a view control in our XPage which name is viewPanel1 the following code:

var model:com.ibm.xsp.model.domino.DominoViewDataModel = getComponent("viewPanel1").getDataModel();
return model.getCategoryIndentLevel();

returns the value 0 if view is categorized, it returns -1 if not.

Use case: If you sort a view, the content of a categorized column gets lost. With that you can check whether the view was sorted and reset the view.

Avoid the onChange bug in Internet Explorer when clicking on a CheckBoxGroup

If you use RadioButtonGroups or CheckBoxGroups in an XPage or a custom control, you could face the problem in the Internet Explorer, that after a partial refresh the submitted values of the group are the values from a previous request. This occurs if you click the label of instead of the box, directly. The reason is that the IE submits the partial refresh before setting the new value from the UI.

rendered="#{javascript:context.getUserAgent().isIE()}"


Example
:

There is a CheckboxGroup with the options A, B, C and D. In the onChange event, a partial refresh will be fired on another panel, which hides or shows some elements in dependence of the chosen value. If you click the label for Option A, there is no submitted value. If you click the label of option B, the value A is submitted. If you click after that on the label of option C, Ab and B will be submitted.


Workaround
:

You can fire the partial refresh a second time in the onComplete event of the onClick event.

 <xp:this.onComplete>
<![CDATA[XSP.partialRefreshPost("#{id:refreshPanelID}");]]>
</xp:this.onComplete>

The workaround above doesn’t work if there are additional actions or server-side code. The code will be executed only on the first refresh. To get this event-handler working again, you have to hang out the click-event from the standard-action and execute it by yourself. You can achieve that with a scriptblock (The name of the control is Output-Script, you can find it if you open the list of other controls in the sidebar of the Domino Designer) Because the checkbox itself is inside the label, you have to seperate it from the own execution.

 <xp:scriptBlock id="scriptBlock1"
        rendered="#{javascript:context.getUserAgent().isIE()}">
        <xp:this.value><![CDATA[dojo.addOnLoad(function(){
    dojo.query("label",dojo.byId("#{id:checkBoxGroupID}")).forEach(function(el){        
        dojo.connect(el,"onclick",el,function(e){
            if (e.target.type != "checkbox") {
                e.preventDefault();
                dojo.query("input",this).forEach(function(el){
                    dojo.attr(el,"checked",!(dojo.attr(el,"checked")));
                });
            }    
        });
    });        
});]]></xp:this.value>
</xp:scriptBlock>

Another advantage of this method, compared to workaround 1, is that you don’t need a second request.

Validation of two dependen input-fields

While validating two fields dependent of each other, you have to read the value of the field which is validated. If you compare the value of the second field, validation will not be fired after the first fail and the validation error persists.

Example:

 <xp:inputText id="recipientNotesID"
   value="#{test.recipientNotesID}"
   disableClientSideValidation="true" required="true">
   <xp:this.validators>
      <xp:validateRequired message="Required value."></xp:validateRequired>
   </xp:this.validators>
</xp:inputText>
<xp:inputText id="recipientNotesPW"
   value="#{test.recipientNotesPW}"
   disableClientSideValidation="true" required="true">
   <xp:this.validators>
      <xp:validateExpression>
         <xp:this.expression><![CDATA[#{javascript:((getComponent("recipientNotesPW").getSubmittedValue()||"")!=(getComponent("recipientNotesID").getValue()||""))}]]>
         </xp:this.expression>
         <xp:this.message><![CDATA[#{javascript:"Values must not be equal."}]]></xp:this.message>
      </xp:validateExpression>
      <xp:validateRequired message="Required value."></xp:validateRequired>
   </xp:this.validators>
</xp:inputText>

XPages: Eventparameters

Here is another advice for the horror we call XPages.

In XPages you have, theoreticaly, the possibility to pass a parameter along with an event. Sounds quite useful, doesn’t it? Unfortunately, they don’t work properly. It wasn’t possible for me to pass on some eventparameters. They simply didn’t arrive at the target.

Here is a workaround:

You can pass the parameter clientside and use them serverside after the request:

var docId = new CGIVariables().getURLParam("documentId");

Here is a link to a quite useful library which I use from time to time in my XPages:10.lotus.com

I added the following function:

this.prototype.getURLParam = function (name){
   name = name.replace(/[[]/,"\[").replace(/[]]/,"\]");
   var regexS = "[\?&]"+name+"=([^&#]*)";
   var regex = new RegExp( regexS );
   var results = regex.exec( "?" + this.request.getQueryString() );
   if( results == null ) {
      return "";
   } else  {
      return results[1];
   }
}
%d bloggers like this: