Colored text in section header on Xpage - xpages

I really want to set some different colors based on different values in the header of a section. Header is computed like this:
var stat = rowData.getColumnValue("Status");
var sectionText = rowData.getColumnValue("Subject") + " " + rowData.getColumnValue("Status");
if (stat == "New") {
return sectionText + " * Obs new ticket * ".toUpperCase(); //some color here
} else if (stat == "Treated") {
return sectionText;
} else if (stat == "Staff") {
return sectionText;
} else {
return sectionText + " * Obs new answer from customer *".toUpperCase(); //and maybe some different color here
}
This code works just fine, but if I try to add some color to the different values it won't work. Any idea how to do this? So far I have tried CSS, but I can only get colors in the background or the text of the section it self, not in the header. The section is in a column of a Data Table.

You can set the headerStyle attribute of the xp:section to add CSS to header. You can even compute it so that it can be different for each section in your Data Table depending of conditions. Something like this:
<xp:section .....>
<xp:this.headerStyle><![CDATA[#{javascript:if (someVar == true) {
return "color:rgb(128,0,128)";
} else {
return "color:rgb(128,0,0)";
}}]]></xp:this.headerStyle>
.....
.....
.....
</xp:section>

Related

How to get row number in SlickGrid by pressing the Enter key-Solved

Seems like a simple task but I have been unsuccessful so far using Slickgrid to get the row number of the highlighted row in a grid when I press a key, specifically the Enter key. I need no data, just the row number so I can use it to reference an array element.
I have managed to do this with the mouse using the onDblClick event handler but not with the simple onKeyDown handler.
Here is the function I use to fill the grid with data which I call when specifically needed:
var grid;
function ttesting(){
var data=[];
load_text_resource(descsource);
grid = new Slick.Grid("#myGrid", data, columns, options);
grid.invalidate();
//load data from multidimensional array:
for (var i = 0; i < maxdesc-2; ++i) {
data[i] = {
aName: descarray[i][2] + " " + descarray[i][3] + descarray[i][4] + descarray[i][5] + descarray[i][6],
aTitle: descarray[i][8],
aDesc:descarray[i][9]
};
}
grid.setOptions(options);
// the following mouse handler works:
grid.onDblClick.subscribe(function(event) {
var cell = grid.getCellFromEvent(event),
row = cell.row;
alert(descarray[row][10]);
});
//This keyDown handler does not work:
grid.onKeyDown.subscribe(function(event) {
var cell = grid.getCellFromEvent(event),
row = cell.row;
if (event.keyCode == 13) {
alert(descarray[row][10]);
}
}
grid.setSelectionModel(new Slick.RowSelectionModel());
grid.render();
}
All I need to know is the row number of the highlighted row when I press the Enter key. I have also tried using instead in the above with no success:
if (event.keyCode == 13) {
selectRow = grid.getSelectedRows();
alert(selectRow);
}
Suggestions welcome.
The traditional way to do this is to create a custom formatter for the ID column, and embed the value (ie. the row id) into the button or link displayed. For example, here's an edit link that I use to open an external page:
function EditLinkFormatter(rowIndex, cell, value, columnDef, grid, dataProvider) {
if (value == null || value === "" || !columnDef.hyperlink) { return "-"; }
if (typeof value == 'string') { value = urlEncode(value); }
return 'edit';
}
This uses value, which is the value of the ID column for the row, but rowIndex is what you are after. It's there in the formatter parameters too.
Found the solution. I needed to include the args variable in the event function:
grid.onKeyDown.subscribe(function(event,args) {
if (event.keyCode == 13) {
var cell = args.cell,
row = args.row;
alert(descarray[row][10]);
}
});

Select All Checkboxes in a Dropdown

I am new to node.js / protractor and want to select select all checkboxes in a dropdown. My code works but there is a problem with 2 of the items that have the same text. When selected, both are checked. In my code I want to skip these 2 items but my text comparison isn't working.
Since selecting one of these duplicate items checks both, selecting the second one un-selects both. For simplicity I'd prefer to merely skip these when they are found in the forEach loop.
element.all(by.xpath('//*[#id="work-bench"]/div[1]/div[1]/div/div[5]/div/div[3]/ul')).all(by.className('checkbox')).then(function(totalDCs) {
console.log('DCs in Dropdown List ' + (totalDCs.length));
DCCount = totalDCs.length;
});
element.all(by.className('multiselect__element')).then(function(options) {
var i = 0;
var j = 1;
options.forEach(function(option) {
option.getText().then(function(text) {
console.log(text + ' was selected');
i++;
if(text != 'FULFILLMENT') {
option.click();
if(DCCount-j == i) {
return DCCount;
}
}
else {
j++;
console.log('j equals ' + j);
}
});
});
});
The line if(text != 'FULFILLMENT') doesn't recognize the match and thus performs the selection (twice).
Try to use
if(text !== 'FULFILLMENT')
or
if(!text.localeCompare('FULFILLMENT'))
and tell us if that works for you

How to return from link formatter url callback without making a hyperlink

I am using the formatter:"link" url callback to generate a hyperlink for a certain column in my Tabulator table
tabulatorTable.addColumn({
title: "Value",
field: "JSONDoc.Path.To.Property",
formatter: "link",
formatterParams: {
url: getHyperLink,
target: "_blank"
}
});
Then in my callback function:
function getHyperLink(cellComp) {
var cellData = cellComp.getData();
var propValFromJSONSource = cellData.SomeProperty;
if( propValFromJSONSource != 0) {
return "http://hostname/report/showLog.php?prop=" + propValFromJSONSource;
}
else {
// If here, I can't generate a valid link, so I want to not have a hyperlink and just show the data
return ???;
}
}
Is there anything that I can do in the else statement to return from the formatter to instruct Tabulator to not create a hyperlink?
I've tried:
return ""; // This makes the hyperlink go to "<currentURL>"
return null; // This generates a relative hyperlink to "<currentURL>/null"
return undefined; // This generates a relative hyperlink to "<currentURL>/undefined"
I suspect I might not be able to use the link formatter and will need to switch to a custom formatter callback that returns "
I also have the problem of not being able to left-click on the hyperlink (even though it appears correctly down in the status bar); I can only center click or right-click and choose "Open in new tab". I'm not sure if this a bug within Tabulator, or Chrome not understanding it for some reason, but that shall be another SO question...
For now, I'm recommending the use of a custom formatter:
tabulatorTable.addColumn({
title: "Value",
field: "JSONDoc.Path.To.Property",
formatter: getHyperLink,
});
Then have the callback return the html code for a hyperlink or just text:
function getHyperLink(cellComp, formatterParams, onRendered) {
var cellData = cellComp.getData();
var cellValue = cellComp.getValue();
var propValFromJSONSource = cellData.SomeProperty;
if( propValFromJSONSource != 0) {
var hrefString = "http://hostname/report/showLog.php?prop=" + propValFromJSONSource;
return "<a href='" + hrefString + "' target='_blank'>" + cellValue + "</a>";
}
else {
return cellValue;
}
}

Get Visible Text from RichTextBlock

In a UWP app, I am using a RichTextBlock that gets populated with some content. It has word wrapping enabled and has a max lines set so that regardless of the length of its content, it will only show a certain number of lines of rich text.
I'd like to know if there is a way to figure out what is the visible text?
So if I have:
<RichTextBlock TextWrapping="Wrap" MaxLines="2">
<RichTextBlock.Blocks>
<Paragraph>
<Paragraph.Inlines>
A bunch of runs go in here with text that are several lines
</Paragraph.Inlines>
</Paragraph>
</RichTextBlock.Blocks>
</RichTextBlock>
I'd like to know how much of the text is actually visible.
I'm trying to detect cases where the text is longer than a set number of lines and append a "... Read More" at the end of the last line (replacing the last 13 chars with "... Read More")
So I wrote some code to get the behavior that I want, but unfortunately this is rather slow and inefficient. So if you're using it in an app that is primarily to show a lot of text that needs to be truncated (like a ListView with a lot of text items) then this would slow down your app perf. I still would like to know if there is a better way to do this.
Here's my code (which only handles Run and Hyperlink inlines so you'll have to modify to handle other types that you need):
private static void TrimText_Slow(RichTextBlock rtb)
{
var paragraph = rtb?.Blocks?.FirstOrDefault() as Paragraph;
if (paragraph == null) { return; }
// Ensure RichTextBlock has passed a measure step so that its HasOverflowContent is updated.
rtb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
if (rtb.HasOverflowContent == false) { return; }
// Start from end and remove all inlines that are not visible
Inline lastInline = null;
var idx = paragraph.Inlines.Count - 1;
while (idx >= 0 && rtb.HasOverflowContent)
{
lastInline = paragraph.Inlines[idx];
paragraph.Inlines.Remove(lastInline);
idx--;
// Ensure RichTextBlock has passed a measure step now with an inline removed, so that its HasOverflowContent is updated.
rtb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
}
// The last inline could be partially visible. The easiest thing to do here is to always
// add back the last inline and then remove characters from it until everything is in view.
if (lastInline != null)
{
paragraph.Inlines.Add(lastInline);
}
// Make room to insert "... Read More"
DeleteCharactersFromEnd(paragraph.Inlines, 13);
// Insert "... Continue Reading"
paragraph.Inlines.Add(new Run { Text = "... " });
paragraph.Inlines.Add(new Run { Text = "Read More", Foreground = new SolidColorBrush(Colors.Blue) });
// Ensure RichTextBlock has passed a measure step now with the new inlines added, so that its HasOverflowContent is updated.
rtb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
// Keep deleting chars until "... Continue Reading" comes into view
idx = paragraph.Inlines.Count - 3; // skip the last 2 inlines since they are "..." and "Read More"
while (idx >= 0 && rtb.HasOverflowContent)
{
Run run;
if (paragraph.Inlines[idx] is Hyperlink)
{
run = ((Hyperlink)paragraph.Inlines[idx]).Inlines.FirstOrDefault() as Run;
}
else
{
run = paragraph.Inlines[idx] as Run;
}
if (string.IsNullOrEmpty(run?.Text))
{
paragraph.Inlines.Remove(run);
idx--;
}
else
{
run.Text = run.Text.Substring(0, run.Text.Length - 1);
}
// Ensure RichTextBlock has passed a measure step now with the new inline content updated, so that its HasOverflowContent is updated.
rtb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
}
}
private static void DeleteCharactersFromEnd(InlineCollection inlines, int numCharsToDelete)
{
if (inlines == null || inlines.Count < 1 || numCharsToDelete < 1) { return; }
var idx = inlines.Count - 1;
while (numCharsToDelete > 0)
{
Run run;
if (inlines[idx] is Hyperlink)
{
run = ((Hyperlink)inlines[idx]).Inlines.FirstOrDefault() as Run;
}
else
{
run = inlines[idx] as Run;
}
if (run == null)
{
inlines.Remove(inlines[idx]);
idx--;
}
else
{
var textLength = run.Text.Length;
if (textLength <= numCharsToDelete)
{
numCharsToDelete -= textLength;
inlines.Remove(inlines[idx]);
idx--;
}
else
{
run.Text = run.Text.Substring(0, textLength - numCharsToDelete);
numCharsToDelete = 0;
}
}
}
}

Dropdown field - first item should be blank

Using sharepoint build in lookup column and it set to required field. SharePoint automatically selects the first item in the dropdown box (kinda misleading for the end users).
Is there a way to display blank or Null for the first row of this drop down box?
(I am open to any solution. I prefer javascript type solution)
For Choice fields, the default value is configured in the column settings. If the "Default value" input box is populated, delete the value in order to use no default value.
Edit
For Lookup fields, the field seems to change dramatically if it is required. Fields that are NOT required have a "(None)" value by default. However, toggling the field to required will remove the "(None)" value and the first value is selected automatically.
One thing I found, is that if you use JavaScript to add the null value to the dropdown and then try to press OK you get an error page: "An unexpected error has occurred." As a workaround, I wrote some more code to do a quick validation that the field has a value before the form is submitted. If the field has no value, then it will prompt the user and cancel the submit. (Note: this code is only attached to the OK buttons so you may get errors while editing EditForm.aspx.. just choose a value for your lookup field and you'll be able to edit like normal)
Anyways, onto the code... I think the only line you'll need to change is var fieldTitle = 'Large Lookup Field'; to update it to the name of your field.
<script type="text/javascript">
function GetDropdownByTitle(title) {
var dropdowns = document.getElementsByTagName('select');
for (var i = 0; i < dropdowns.length; i++) {
if (dropdowns[i].title === title) {
return dropdowns[i];
}
}
return null;
}
function GetOKButtons() {
var inputs = document.getElementsByTagName('input');
var len = inputs.length;
var okButtons = [];
for (var i = 0; i < len; i++) {
if (inputs[i].type && inputs[i].type.toLowerCase() === 'button' &&
inputs[i].id && inputs[i].id.indexOf('diidIOSaveItem') >= 0) {
okButtons.push(inputs[i]);
}
}
return okButtons;
}
function AddValueToDropdown(oDropdown, text, value, optionnumber){
var options = oDropdown.options;
var option = document.createElement('OPTION');
option.appendChild(document.createTextNode(text));
option.setAttribute('value',value);
if (typeof(optionnumber) == 'number' && options[optionnumber]) {
oDropdown.insertBefore(option,options[optionnumber]);
}
else {
oDropdown.appendChild(option);
}
oDropdown.options.selectedIndex = 0;
}
function WrapClickEvent(element, newFunction) {
var clickFunc = element.onclick;
element.onclick = function(event){
if (newFunction()) {
clickFunc();
}
};
}
function MyCustomExecuteFunction() {
// find the dropdown
var fieldTitle = 'Large Lookup Field';
var dropdown = GetDropdownByTitle(fieldTitle);
if (null === dropdown) {
alert('Unable to get dropdown');
return;
}
AddValueToDropdown(dropdown, '', '', 0);
// add a custom validate function to the page
var funcValidate = function() {
if (0 === dropdown.selectedIndex) {
alert("Please choose a value for " + fieldTitle + ".");
// require a selection other than the first item (our blank value)
return false;
}
return true;
};
var okButtons = GetOKButtons();
for (var b = 0; b < okButtons.length; b++) {
WrapClickEvent(okButtons[b], funcValidate);
}
}
_spBodyOnLoadFunctionNames.push("MyCustomExecuteFunction");
</script>
In response Kit Menke, I've made a few changes to the code so it will persist previous value of the dropdown. I have added the following lines of code to AddValueToDropdown()....
function AddValueToDropdown(oDropdown, text, value, optionnumber){
var selectedIndex
if (oDropdown.options.selectedIndex)
selectedIndex = oDropdown.options.selectedIndex;
else
selectedIndex = -1;
// original code goes here
// changed last line of code (added "selectedIndex+1")
oDropdown.options.selectedIndex = selectedIndex+1;
}
To improve on top of Aaronster's answer: AddValueToDropdown can be done that way:
var injectedBlankValue = false;
function AddValueToDropdown(oDropdown, text, value, optionnumber) {
for (i = 0; i < oDropdown.options.length; i++) {
option = oDropdown.options[i];
if(option.getAttribute('selected')) // If one is already explicitely selected: we skip the whole process
return;
}
var options = oDropdown.options;
var option = document.createElement('OPTION');
option.appendChild(document.createTextNode(text));
option.setAttribute('value', value);
if (typeof (optionnumber) == 'number' && options[optionnumber]) {
oDropdown.insertBefore(option, options[optionnumber]);
}
else {
oDropdown.appendChild(option);
}
// changed last line of code (added 'selectedIndex+1')
oDropdown.options.selectedIndex = 0;
injectedBlankValue = true;
}
This is needed for document libraries where "add" and "set properties" are two distinct pages.
And funcValidate starts with:
var funcValidate = function () {
if (!injectedBlankValue)
return true;
All these changes is to make the whole thing work with document libraries.

Resources