Platform : Domino v9
Background:
When an user opens a document using an Xpage, the Rich Text control (CKEditor)displays the existing content in that document.
The RichText field is bound to a backend Notes document.
When an user makes any changes to this content and clicks the Update button, validation occurs (for this example, we are checking to see if length exceeds 20 characters).
Validation is done using the ValidateExpression option.
On validation, user sees the error message but the new content entered by user is lost. The Rich Text control displays the original content.
Validation Code
<xp:this.validators>
<xp:validateExpression>
<xp:this.expression><![CDATA[#{javascript:var contentStr = value.getContentAsText();
var regex = /(<([^>]+)>)/ig ;
contentStr = contentStr.replace(regex,'');
if( contentStr.length < 20 ) {
return true;
}else{
return false;
}
}]]></xp:this.expression>
<xp:this.message><![CDATA[#{javascript:var errMsg = getErrorMessageText ('713','');
if ( errMsg != null && errMsg != "") {
//return errMsg;
return "Length cannot exceed 20 characters";
}else{
return "Length cannot exceed 20 characters";
}}]]></xp:this.message>
</xp:validateExpression>
</xp:this.validators>
Things I tried:
I tried the solution mentioned in Tommy Valand's blog but that did not solve the problem. Tommy's Blog
Question
How do I validate the RichText control without losing the new content ?
Screenshots:
When I have complex validation, I sometimes use a hidden input field for validation. Not sure if this will help in this situation, but could be worth a try..
getLocalValue should return the converted value in the validation phase. Since you're dealing with strings, getSubmittedValue might be even more appropriate/easier to use.
If you use xp:message, set the for attribute to whatever id you decide for the hidden field.
<xp:inputHidden id="dummyValidatorField" value="dummyValue">
<xp:this.validators>
<xp:validateExpression>
<xp:this.expression><![CDATA[#{javascript:var contentStr = getComponent( 'idOfRtField' ).getLocalValue(); // maybe you need to append .getContentAsText();
var regex = /(<([^>]+)>)/ig ;
contentStr = contentStr.replace(regex,'');
if( contentStr.length < 20 ) {
return true;
}else{
return false;
}
}]]></xp:this.expression>
<xp:this.message><![CDATA[#{javascript:var errMsg = getErrorMessageText ('713','');
if ( errMsg != null && errMsg != "") {
//return errMsg;
return "Length cannot exceed 20 characters";
}else{
return "Length cannot exceed 20 characters";
}}]]></xp:this.message>
</xp:validateExpression>
</xp:this.validators>
Strange behavior. Maybe you can validate your RTF control via CSJS (and not SSJS/Java) with this recently added snippet: http://openntf.org/XSnippets.nsf/snippet.xsp?id=validate-rich-text-field
Related
I have set a data validation as a list from a range but when I enter a text that does not match the list, I am getting a red warning in the cell.
My question is: How to use both methods, but avoiding the red warning? How to make datavalidation accept both which is available in range list, and that which is not available in range list, but also keep the dropdown?
Using onEdit(e), I have a script that will compare the value input to the data validation rule and if its not in the list, the error may be displayed momentarily but the value will be replaced with "" which is a valid input.
My data validation is Sheet1!C5:D5 merged cell for a range, Sheet!C7:D7 for a list.
DataValidation.getCriteriaValues() returns an array of objects the first element of that array is an array of the values displayed in the dropdown for a list or a range object.
function onEdit(e) {
try {
if( e.range.getSheet().getName() === "Sheet1") {
if( ( e.range.getA1Notation() === "C5" ) || ( e.range.getA1Notation() === "C7" ) ) {
let valid = e.range.getDataValidation();
if( valid != null ) {
let criteria = valid.getCriteriaValues();
let values = criteria[0];
if( valid.getCriteriaType() === SpreadsheetApp.DataValidationCriteria.VALUE_IN_RANGE ) {
values = values.getValues().flat();
}
if( values.indexOf(e.value) < 0 ) e.range.setValue("");
}
}
}
}
catch(err) {
SpreadsheetApp.getUi().alert(err);
}
}
Reference
Range.getDataValidation()
DataValidation.getCriteriaValues()
The form has three fields
1. First Name
2. Last Name
3. Phone
I want max 20 characters length validation in name fields.
There are different ways of verifying validations of fields.
If user try to enter more than max limit in field
It prevents user to enter text and turns the color of field to red
It allows more text to field and shows validation message when try to do form submission or move to next field.
Solution:
Using sendKeys() method send more than 20 characters to field,
Case 1:
1. Get text of the same field using getText() method, then perform String.length and perform Assert to make sure character count is 20
Then verify CSS property of field and make sure color turns to red using Assert.
Case 2: Perform case 1 first point,
then get locator of validation message and get text from it and verify with expected validation message.
Enter more than 20 characters using selenium web driver in textbox and then get the text of the same textbox using selenium and calculate the length of the string it should be 20 and not more than that.
To check the maximum and minimum length of any field, you can use the HTML's maxlength and minlength attribute and then find the length using Selenium's getAttribute("maxlength") and getAttribute("minlength") function.
Sample code is given below: Say we're talking about the username field.
//Getting the minimum and maximum value of the username field
int minLengthDefined = Integer.parseInt(mcp.userName().getAttribute("minlength"));
int maxLengthDefined = Integer.parseInt(mcp.userName().getAttribute("maxlength"));
//Get the typed value
String typedValue = mcp.userName().getAttribute("value");
//Get the length of the typed value
int size = typedValue.length();
if( size < minLengthDefined ){
Assert.assertEquals(size, minLengthDefined, "Username field has incorrect value.");
Sysout.out.println("It contains "+size+" characters.");
Sysout.out.println("Username should lie between "+minLengthDefined+ " and "+maxLengthDefined+ " characters." );
}
else if( size > maxLengthDefined) {
Assert.assertEquals(size, maxLengthDefined, "Username field has incorrect value.");
Sysout.out.println("It contains "+size+" characters.");
Sysout.out.println("Username should lie between "+minLengthDefined+ " and "+maxLengthDefined+ " characters." );
}
else
Sysout.out.println("unhandled issue occurred!");
}
There are multiple ways to validate minimum and maximum character length of some text field.
Here is a code snippet to get the email field minimum and maximum character length and to validate it, hope i answered your question :)
First get the HTML attribute minlength and maxlength for your required field.
public class LoginPage{
#FindBy(name = "email")
WebElement txtEmail;
public int[] getEmailMinMaxCharLength() {
int[] charLength = new int[2];
charLength[0] = Integer.parseInt(txtEmail.getAttribute("minlength"));
charLength[1] = Integer.parseInt(txtEmail.getAttribute("maxlength"));
return charLength;
}
}
public class Test{
LoginPage loginPage = new LoginPage();
#Test
public void verifyMinMaxCharLength() {
int[] charlength = loginPage.getEmailMinMaxLength();
int minCharLength = charlength[0];
int maxCharLength = charlength[1];
softAssert.assertEquals(minCharLength, 5);
softAssert.assertEquals(maxCharLength, 50);
softAssert.assertAll();
}
}
I´m trying to save a value in spreadsheet's header for later use as a new column value.
This is the reduced version with value (XYZ) in header:
The value in header must be used for new column CODE:
This is my design:
tFilterRow_1 is used to reject rows without values in A, B, C columns.
There is a conditional in tJavaRow_1 to set a global variable:
if(String.valueOf(row1.col_a).equals("CODE:")){
globalMap.putIfAbsent("code", row1.col_b);
}
The Var expression in tMap_1 to get the global variable is:
(String)globalMap.get("code")
The Var "code" is mapped to column "code" but I'm getting this output:
a1|b1|c1|
a2|b2|c2|
a3|b3|c3|
What is missed or there is a better approach to accomplish this escenario ?
Thanks in advance.
Short answer:
I tJavaRow use the input_row or the actual rowN in this case row4.
Longer answer, how I'd do it.
I'd do is let the excel flow in AS-IS. By using some Java tricks we can simply skip the first few rows then let the rest of the flow go through.
So the filter + tjavarow combo can be replaced with a tJavaFlex.
tJavaFlex I'd do:
begin:
boolean contentFound = false;
main
if(input_row.col1 != null && input_row.col1.equalsIgnoreCase("Code:") ) {
globalMap.put("code",input_row.col2);
}
if(input_row.col1 != null && input_row.col1.equalsIgnoreCase("Column A:") ) {
contentFound = true;
} else {
if(false == contentFound) continue;
}
This way you'll simply skip the first few records (i.e header) and only care about the actual data.
How do I remove or hide the data value not required from table in birt tool?
I tried with the values it works in some places but now in groups which has multiple values.
I need to filter some of the values which should not be displayed in the data tab of the table.
I have a column which does not have any value that I need to filter out (But its not an empty value because when I check I got to know that it has some blank spaces). It should display only the columns with non-blank value.
How can I remove those columns from the data set.
You can of course try scripting the data source query but you can also run a script on the table when it is created to hide the empty column.
Try this script in the table's onCreate event:
var mycolumnCount = this.getRowData().getColumnCount();
var DisNull = false;
for(i=1;i<mycolumnCount;i++) {
var temp = this.getRowData().getColumnValue(i)
if(this.getRowData().getColumnValue(i) == "") {
DisNull = true;
}else{
DisNull = false;
i = mycolumnCount+1;
}
}
if(DisNull == true) {
this.getStyle().display = "none"
}
I read on many forums about how to implement a solution for view pagionation, but I didn't solve it.
I created $$ViewTemplateDefault containing some personalized hotspotbuttons for Next, Previous and a text field $$ViewBody. ( or, alternatively, an embedded view ).
Any tips and help will be really appreciated.
I will explain in a couple words, just to be clear:
So, initially: the first 30 lines will appear => in a right corner: Page 1.
If Next is clicked => the next 30 lines => Page 2. and so on.
Here is a working solution for categorized views too. It calculates the current page number based on the previous page number and uses cookies.
Add to your form a Path-Thru HTML text <span id="pageNumber"></span > for the page number:
and add following code to form's onLoad event as Web/JavaScript:
function getURLParameter(parameter) {
return decodeURIComponent((new RegExp('[?|&]' + parameter + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [, ""])[1].replace(/\+/g, '%20')) || null;
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name)==0) return c.substring(name.length,c.length);
}
return "";
}
function compareStart(start1, start2) {
var list1 = start1.split(".");
var list2 = start2.split(".");
for (i=0; i <100; i++) {
var value1 = list1[i];
var value2 = list2[i];
if (value1 == null) {
return value2 == null ? 0 : -1;
} else if (value2 == null) {
return 1;
}
value1 = Math.round(value1);
value2 = Math.round(value2);
if (value1 !== value2) {
return value1 < value2 ? -1 : 1;
}
}
}
var start = getURLParameter("Start");
var page = "1";
if (start == null || start === "1") {
window.name = Math.floor((Math.random()*10000)+1);
start = "1";
} else {
page = getCookie("page" + window.name);
var oldStart = getCookie("start" + window.name);
page = Math.round(page) + compareStart(start, oldStart);
}
document.getElementById('pageNumber').innerHTML = page;
document.cookie = "page" + window.name + "=" + page;
document.cookie = "start" + window.name + "=" + start;
How does it work?
The commands #DbCommand("Domino"; "ViewNextPage") and #DbCommand("Domino"; "ViewPreviousPage") return an URL with parameter "&Start=". This is the row number in view where the current page starts. For categorized views they return a hierarchical number like "&Start=1.2.4.2". That means that the view starts at the first main topic, subtopic 2, subsubtopic 4, document 2.
This parameter "&Start=" gives us the possibility to recognize if user pressed "prev" or "next": we just compare the URL "&Start=" parameter of current and former page.
For that, we have to remember the URL "&Start=" parameter and put it into a cookie "start".
We also need to save the current page number. We put it into a cookie "page".
At onload event we calculate the current page number based on previous page number:
if "&Start=" parameter is larger now then we add 1
if "&Start=" parameter is smaller now then we subtract 1
if "&Start=" parameter didn't change then we keep the former value
If "&Start=" parameter is empty we know we are on page 1.
Here is one other thing we have to deal with: cookies are saved per user session not per browser tab. That means, if we have two views open in browser same cookies "start" and "page" would be used. To avoid that, we have to add to cookie name something tab specific. I use for that a random four digit number and save it in window.name which is tab specific.
I understand your question that you have a working form $$ViewTemplateDefault and now looking for a possibility to show the current page number "Page nn" in that form.
I assume that you use #DbCommand("Domino"; "ViewNextPage") for getting next page and #DbCommand("Domino"; "ViewPreviousPage") for getting previous page.
Those next and prev functions working the way that always one document will "overlap". If you have 30 lines per page and click next, then last document will be first in next page and next 29 show up in addition. You can watch that in used URL parameter "&Start=": 1 ... 30 ... 59 ... 88 ...
Knowing this you can count the current page number this way:
_start := #ToNumber(#Replace(#UrlQueryString("start"); ""; "1"));
_count := #ToNumber(#Replace(#UrlQueryString("count"); ""; "30")) - 1;
#Integer((#ToNumber(_start) / _count) + 1)
Be aware that this will work for non-categorized and non-collapsible views only.
A more sophisticated solution you can find here. It has additional features like GoTo page and Documents per page.
If you have the chance for your project then use XPages instead. You can do pagination much easier as it is available "out of the box".
Update:
You won't find a reasonable solution for categorized views. If you don't want to use Domino Data/Access Services REST API you have to live with the Domino view URL parameters (look here for "OpenView"). You aren't able to tell from "&Start=" or any other parameter on which page you are currently on.
The easiest way to get a good working pagination is using XPages. Hope you are allowed to use it in your project...