How to make only the selected row editable in Tabulator? - tabulator

I'm using Tabulator for tables in React.
What I want to implement
I want to force users to only edit one row at a time (but all columns in that row):
Select a row
Click EDIT button -> makes only that row editable (all columns)
Edit/enter values to those editable columns via inline edit
Click SAVE -> saves changes to the table
What I've tried:
Using the edit() api in CellComponent to force the cell to be editable. But that didn't open the cell for editing (nothing happens when I click on the cell after calling edit() on the cell.
const handleIfEditable = () => false; // force
const columns: [
{ title: "ID", field: "id", editor: "input", editable: handleIfEditable },
{ title: "Name", field: "name", editor: "input", editable: handleIfEditable }
];
const selectedRowComponents = table.getSelectedRows();
const row = selectedRowComponents[0];
const cells = row.getCells();
cells.map(cell => cell.edit()) // trying to make all cells in row editable, didn't work (nothing happens)

You are in luck, this is really easy you can just use the editable column definition property and the isSelected function on the Row Component
//define editable check function
var selectedCheck(cell){
return cell.getRow().isSelected();
}
...
//in column definition for an editable column
{title:"First", field:"first_name", editable:selectedCheck},

Here is what I have implemented.
Is that what you are looking for?
function formatter_EditButton(cell, formatterParams, onRendered){
return "<div class='btn badge badge-pill badge-secondary'>Edit</div>";
}
function formatter_CancelButton(cell, formatterParams, onRendered){
return "<div class='btn badge badge-pill badge-warning'>Cancel</div>";
}
function formatter_SaveButton(cell, formatterParams, onRendered){
return "<div class='btn badge badge-pill badge-success'>Save</div>";
}
function formatter_DeleteButton(cell, formatterParams, onRendered){
return "<div class='btn badge badge-pill badge-danger'>Delete</div>";
}
function cellClick_EditButton(e, cell){
currentRow = cell.getRow()
currentTable = cell.getTable()
selectedRows = currentTable.getSelectedRows()
if (selectedRows.length == 0) {
for (i = 0; i < selectedRows.length; i++) {
selectedRows[i].deselect()
selectedRows[i].reformat()
}
currentTable.deselectRow()
currentRow.select()
currentRow.reformat()
cells = currentRow.getCells()
for (i = 0; i < cells.length; i++) {
cells[i].setValue(cells[i].getValue())
}
currentTable.hideColumn("EditButton")
currentTable.showColumn("CancelButton")
currentTable.showColumn("DeleteButton")
currentTable.showColumn("SaveButton")
}
}
function cellClick_CancelButton(e, cell){
if (!cell.getRow().isSelected()){
return
}
currentRow = cell.getRow()
currentTable = cell.getTable()
if (cell.getRow().isSelected()){
//Cancel
cells = currentRow.getCells()
for (i = 0; i < cells.length; i++) {
cells[i].restoreOldValue();
}
stopEditing(cell)
}
}
function cellClick_SaveButton(e, cell){
if (!cell.getRow().isSelected()){
return
}
stopEditing(cell)
}
function cellClick_DeleteButton(e, cell){
if (!cell.getRow().isSelected()){
return
}
//Can use prompt to make them connfirm the name
if(window.confirm("Delete the user "+cell.getData().FirstName+" "+ cell.getData().LastName+"?"))
{
stopEditing(cell)
cell.getRow().delete()
}
}
function stopEditing(cell){
currentRow = cell.getRow()
currentTable = cell.getTable()
currentTable.deselectRow()
currentTable.showColumn("EditButton")
currentTable.hideColumn("CancelButton")
currentTable.hideColumn("DeleteButton")
currentTable.hideColumn("SaveButton")
currentRow.reformat()
}
function isRowSelected(cell){
return cell.getRow().isSelected()
}
function cellClick_FlipIfSelected(e, cell){
if (cell.getRow().isSelected()){
cell.setValue(!cell.getValue())
}
}
var UsersTable = new Tabulator("#UsersTable",{
index:"ID",
ajaxURL:"/api/getUsersData",
layout:"fitDataFill",
layoutColumnsOnNewData:true,
paginationSize:10,
pagination:"local",
selectable:false,
initialSort:[
{column:"FirstName", dir:"asc"},
{column:"LastName", dir:"asc"},
{column:"Active", dir:"desc"}
],
columns:[
{title:"Active", field:"Active", formatter:"tickCross", mutator: mutator_Active, cellClick:cellClick_FlipIfSelected, align:"center", resizable:false},
{title:"ID", field:"ID"},
{title:"Last", field:"LastName", editable:isRowSelected, editor:"input", resizable:false},
{title:"First", field:"FirstName", editable:isRowSelected, editor:"input", resizable:false},
{title:"Email", field:"Email", editable:isRowSelected, editor:"input", resizable:false},
{title:"Phone Number", field:"PhoneNumber", editable:isRowSelected, editor:"input", resizable:false},
{title:"Created", field:"CreatedAt", editable:isRowSelected, formatter:"datetime", resizable:false},
{title:"Updated", field:"UpdatedAt", editable:isRowSelected, formatter:"datetime", resizable:false},
{field:"EditButton",formatter:formatter_EditButton,cellClick:cellClick_EditButton, headerSort:false, align:"center", resizable:false},
{field:"CancelButton", formatter:formatter_CancelButton,cellClick:cellClick_CancelButton, headerSort:false, align:"center", resizable:false,visible:false},
{field:"SaveButton",formatter:formatter_SaveButton,cellClick:cellClick_SaveButton, headerSort:false, align:"center", resizable:false,visible:false},
{field:"DeleteButton",formatter:formatter_DeleteButton,cellClick:cellClick_DeleteButton, headerSort:false, align:"center", resizable:false,visible:false},
]
})

Related

Read Data from Excel sheet Column wise in flutter

I am Using excel: ^2.0.1 for reading data from my file picked with File Picker. I am able to Read each cell by
void getExcelFile() async {
FilePickerResult pickedFile = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['xlsx'],
allowMultiple: false,
);
if (pickedFile != null) {
var file = pickedFile.paths.single;
var bytes = await File(file).readAsBytes();
Excel excel = await compute(parseExcelFile, bytes);
for (var table in excel.tables.keys) {
print(table);
print(excel.tables[table].maxCols);
print(excel.tables[table].maxRows);
Sheet sheetObject = excel[table];
for (int row = 0; row < sheetObject.maxRows; row++) {
sheetObject.row(row).forEach((cell) {
var val = cell.value; // Value stored in the particular cell
print("cell value is: " + val.toString());
});
}
}
}
}
but data is row wise, I want to read Data Column Wise
here I want to add each name with its corresponding email into my class, user can add his own excel file and there could be several headings in that file but i just need name and email
class ExcelSheetData {
var name;
var email;
ExcelSheetData({this.name, this.email});
}
I was able to find round about for column index of name and email then I was able to get name and email from each row from that particular index, below is the code if it help someone other:
Future<List<ExcelSheetData>> getExcelFile(BuildContext context, String name, String email) async {
FilePickerResult pickedFile = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['xlsx'],
allowMultiple: false,
);
List<ExcelSheetData> excelList = [];
int nameIndex;
int emailIndex;
if (pickedFile != null) {
setAddMembersLoadingTrue();
var file = pickedFile.paths.single;
var bytes = await File(file).readAsBytes();
Excel excel = await compute(parseExcelFile, bytes);
for (var table in excel.tables.keys) {
for (var row in excel.tables[table].rows) {
// name variable is for Name of Column Heading for Name
if (row?.any((element) => element?.value?.toString() == name) ?? false) {
Data data = row?.firstWhere((element) => element?.value?.toString()?.toLowerCase() == name);
nameIndex = data.colIndex;
}
// email variable is for Name of Column Heading for Email
if (row?.any((element) => element?.value?.toString() == email) ?? false) {
Data data = row?.firstWhere((element) => element?.value?.toString()?.toLowerCase() == email);
emailIndex = data.colIndex;
}
if (nameIndex != null && emailIndex != null) {
if (row[nameIndex]?.value.toString().toLowerCase() != name.toLowerCase() && row[emailIndex]?.value.toString().toLowerCase() != email.toLowerCase())
excelList.add(
ExcelSheetData(
name: row[nameIndex]?.value.toString(),
email: row[emailIndex]?.value.toString(),
),
);
}
}
}
setAddMembersLoadingFalse();
return excelList;
}
return null;
}
If u want to read column wise then:
then modify it from second loop
for (int column = 0; column < sheetObject.maxColumns; column++) {
sheetObject.column(column).forEach((cell) {
var val = cell.value; // Value stored in the particular cell
print("cell value is: " + val.toString());
});

Update cell update without scroll to top

I have an issue where on updating a cell with cellEdited, The page jumps back to the top of the page when the mutator or formatter is called.
I've tried setting table.redraw(false) and then the formatter/mutator doesn't seem to do anything.
When a user updates the proficiency level the formatter or mutator should add a button if it's above 3. It works perfectly. However it jumps to the top of the screen.
Any help appreciated.
var evidenceMutator = function(value, data, type, params, component){
skillsTable.redraw(false);
var claimedProficiency = data.Proficiency;
var skillClaimed = data.SkillID;
var Evidence = data.Evidence;
var memberID = $("#user").data("memberid");
if (claimedProficiency >= 3 ) {
// has provided evidence
if (Evidence == 1) {
return '<a class="btn btn-outline-success btn-xs btn-slim evidence" href="#" id="'+ skillClaimed + '" onclick="openEvidenceModal(this, this.id, '+ memberID +')"> <i class="fas fa-check-circle cssover"></i> Edit Evidence </a>';
} else { // needs to provide
return '<a class="btn btn-outline-danger btn-xs btn-slim evidence" href="#" id="'+ skillClaimed + '" onclick="openEvidenceModal(this, this.id, '+ memberID +')"> <i class="fas fa-times-circle cssover"></i> Add Evidence </a>';
}
} else {
// cell.getElement().style.color = "#CCD1D1";
//cell.getElement().style.fontStyle ="italic";
// cell.getElement().style.fontSize = "0.75em";
return ' No Evidence Needed';
}
}
function complianceTick(cell) {
var isCompliant = cell.getData().Compliance;
var Evidence = cell.getData().Evidence;
var claimedProficiency = cell.getData().Proficiency;
// style all with sucesss and only change if we need to
cell.getElement().style.color = "#33CC66";
cell.getElement().style.fontSize = "0.85em";
cell.getElement().style.fontStyle = "italic";
if (claimedProficiency >= 3 ) {
if (Evidence == '1') {
return '<i class="fas fa-check-circle"></i>';
} else {
cell.getElement().style.color = "#FF0000";
cell.getElement().style.fontSize = "0.85em";
cell.getElement().style.fontWeight = "bold";
return '<i class="fas fa-times-circle"></i>';
}
} else {
return '';
}
}
var skillsTable = new Tabulator("#SkillsTable", {
index: "SkillID",
height:"100%",
headerVisible:false,
autoResize:true,
layout:"fitColumns", //fit columns to width of table
groupBy:"SkillCatID",
groupHeader: groupMaker,
initialSort:[ //set the initial sort order of the data
{column:"RowOrder", dir:"asc"}
],
columns:[ //define the table columns
{title:"", field:"SkillID", visible:false},
{title:"Skill", field:"SkillName", visible:true, headerSort:false, formatter: skillName},
{title:"", field:"SkillCatID", visible:false},
{title:"", field:"SkillCatName", visible:false},
{title:"My Level", field:"Proficiency", visible:true, editor:inputEditor, formatter:prof, width:"7%", headerSort:false, align:"center",
cellEdited:function(cell, row, success){ // EDITING FUNCTION
var $value = cell.getValue();
var $field = cell.getField();
var $row = cell.getRow();
var $id = $row.getData().SkillID;
var integer = parseInt($value, 10);
if ($value != "" && integer < 5 && integer >= 0) {
// update record
updateRecord($id, $field, $value, $row);
//$row.update({"Proficiency" : $value});
//$row.update({"Evidencer" : $value});
skillsTable.updateRow($id, {id:$id,"Proficiency":$value});
skillsTable.updateRow($id, {id:$id,"Evidencer":$value});
} else {
cell.restoreOldValue();
if ($value != "") {
alert ("Values should be between 0 and 4, the cell has been restored to its previous value.")
}
}
},
},
{title:"Target", field:"MinLevel", visible:false, headerSort:false},
{title:"", field:"Proficiency", visible:true, formatter: skillDec, headerSort:false},
{title:"Evidence", field:"Evidencer", visible:true, hozAlign:"center", formatter: evidenceCell, mutator: evidenceMutator, width:"10.5%", headerSort:false},
{title:"", field:"RowOrder", visible:false, sorter:"number"},
{title:"disc", field:"DisciplineID", visible:false, headerFilter:true}
],
});
UPDATE
I think I've narrowed down the issue to the formatter:
```
function evidenceCell(cell, formatterParms, onRendered) {
var claimedProficiency = cell.getData().Proficiency;
var skillClaimed = cell.getData().SkillID;
var Evidence = cell.getData().Evidence;
var memberID = $("#user").data("memberid");
if (claimedProficiency >= 3 ) {
// has provided evidence
if (Evidence == 1) {
return '<a class="btn btn-outline-success btn-xs btn-slim evidence" href="#" id="'+ skillClaimed + '" onclick="openEvidenceModal(this, this.id, '+ memberID +')"> <i class="fas fa-check-circle cssover"></i> Edit Evidence </a>';
} else { // needs to provide
return '<a class="btn btn-outline-danger btn-xs btn-slim evidence" href="#" id="'+ skillClaimed + '" onclick="openEvidenceModal(this, this.id, '+ memberID +')"> <i class="fas fa-times-circle cssover"></i> Add Evidence </a>';
}
} else {
cell.getElement().style.color = "#CCD1D1";
cell.getElement().style.fontStyle ="italic";
cell.getElement().style.fontSize = "0.75em";
return 'No Evidence Needed';
}
}
```
It looks like the issue is with the cell.getData(). It seems to trigger the scroll to the top of page. I think it may be a bug with the newer version. Testing 4.2 and it seems to not be an issue. However, I have issues with modals going to the top of the page with the older version.
The issue seems to be with the virtual DOM. In my case I can luckily turn it off by using the following:
virtualDom:false,
I'm not sure this would be a good fix for people with hundreds of rows.

unable to hide an entire column using a fa icon in tabulator

I need a help with a issue in tabulator.
I have placed an fa icon to hide and particular column. The icon is placed in an header in table and on click of the icon the entire column should be hidden but i am unable to do so.
Using headerClick function i an able to hide the column but i need to have the function on the icon click not on header click in tabulator.
this is the code which is working but we need to enable click on the Fa icon not on header click.
var showHideButtonFormatter = function(cell, formatterParams, onRendered){
var btn = document.createElement("i");
btn.classList.add("pull-right","fa","fa-eye-slash","btn");
btn.setAttribute('type', 'button');
var title = document.createElement("span");
title.appendChild(btn);
var textnode = document.createTextNode(formatterParams["titleName"]);
title.appendChild(textnode);
title.style.css ="text-align:center"
$(btn).on('click', function (e) {
});
return title;
}
//PriceBreakup Table --START
var priceBreakupTablejsondata = [[${rfxForm.itemSupplierDetails}]];
if(priceBreakupTablejsondata == "{}" || priceBreakupTablejsondata == null){
priceBreakupTablejsondata = priceBreakupTablejsondata;
}else{
priceBreakupTablejsondata = JSON.parse(priceBreakupTablejsondata);
}
var columnsObjects =[]
function showAllColumns(){
columnsObjects.forEach(function(value, index, array) {
value.show();
});
}
var priceBreakupTable = new Tabulator("#priceBreakupTable", {
height:310,
data:[],
layout:"fitColumns",
placeholder:"",
columnVertAlign:"bottom",
columns:[
//{title:"#", field:"rownum ", widthGrow:1, formatter:"rownum"},
{title:"Item", field:"item", widthGrow:2, headerSort:false},
{title:"Quantity", field:"quantity", widthGrow:1, headerSort:false},
{title:"UOM", field:"uom", align:"center", widthGrow:1, headerSort:false},
],
});
//column definition in the columns array
if(priceBreakupTablejsondata!=null){
for(i=0; i<Object.keys(priceBreakupTablejsondata).length; i++){
var itemsArray = priceBreakupTablejsondata[i];
var supplierArray = itemsArray.supplierList;
for (var s = 0; s < supplierArray.length; s++) {
if (i==0) {
priceBreakupTable.addColumn(
{ title:supplierArray[s].supplierName,align:"center",
headerClick:function(e, column){
//e - the click event object
//column - column component
columnsObjects.push(column);
column.hide();
},
titleFormatter:showHideButtonFormatter, titleFormatterParams:{"titleName":supplierArray[s].supplierName},
columns:[
{title:"Unit Rate", field:"unitRate_"+s, headerSort:false, align:"center", widthGrow:2},
{title:"Tax Amount", field:"taxAmount_"+s, headerSort:false, align:"center", widthGrow:2},
{title:"Total Amount", field:"totalAmount_"+s, headerSort:false, align:"center", widthGrow:2.2},
],
}
,false);
}
}
}
}
function reformatData(itemJSON){
var output = [];
var totalSuppliers;
var headersRow = {
item: "",
quantity: "",
uom: "",
}
var itemsLength = Object.keys(itemJSON).length;
for(i=0; i < itemsLength; i++){
var row = {
item:itemJSON[i].item,
quantity:itemJSON[i].quantity,
uom:itemJSON[i].uom,
}
var supplierArray = itemJSON[i].supplierList;
for(s=0; s < itemJSON[i].supplierList.length; s++){
totalSuppliers = itemJSON[i].supplierList.length
var supplierName = supplierArray[s].supplierName;
row["unitRate_"+s] = supplierArray[s].unitRate;
row["taxAmount_"+s] = supplierArray[s].taxAmount ;
row["totalAmount_"+s] = supplierArray[s].totalAmount ;
}
output.push(row);
}
for(hid=0; hid < totalSuppliers; hid++){
headersRow["unitRate_"+hid] = "Unit Rate";
headersRow["taxAmount_"+hid] = "Tax Amt.";
headersRow["totalAmount_"+hid] = "Total Amt.";
}
//alert(JSON.stringify(headersRow));
output.unshift(headersRow)
return output;
}
if(priceBreakupTablejsondata!=null){
priceBreakupTable.setData(reformatData(priceBreakupTablejsondata));
}
//PriceBreakup Table --END
The click event listener for your btn in your header formatter should be should be:
btn.addEventListener("click", function(e){
cell.getColumn().hide();
});

Drop down List from Viewbag not following selected property

I have a dropdownList that will be populated by a viewbag carrying a List values containing "Text" and "Value" property that is set by a loop.
List<SelectListItem> year = new List<SelectListItem>();
for (var i = 1990; i <= DateTime.Now.Year; i++)
{
year.Add(new SelectListItem
{
Text = i.ToString(),
Value = i.ToString()
});
}
ViewBag.Year = year;
And in my view:
#Html.DropDownListFor(model => model.Year, new SelectList(ViewBag.Year, "Value", "Text", Model.Birthday.Year), htmlAttributes: new { #class = "form-control", style = "width: 80px" })
Nothing happens.
Try
#Html.DropDownListFor(model => model.Year, new SelectList(#ViewBag.Year, "Value", "Text", Model.Birthday.Year), new { #class = "form-control", style = "width: 80px" })
It will work as expected.
I used script instead
$('#Year option')
.removeAttr('selected')
.filter('[value=#Model.Birthday.Year]')
.attr('selected', true)

How do you write SharePoint 2010 conditional formatting for this?

I am using SPD 2010 and SharePoint Sever 2010.
Using conditional formatting I'm trying to format a list so that if today's date is greater than 30 days before the start date column a cell will turn red.
I've tried several XPath expressions from other Stackoverflow entries but nothing has worked. I'm wondering if I am even adding the XPath expression in the right place: In SPD I am selecting a date from the desired column, choosing "Conditional Formatting", selecting "Format Column", selecting "Advanced" and putting my expressions in the XPath expression textbox.
Appreciate your help!
I'm going to suggest a non-Xpath approach, but if you're looking to stick with Xpath you should take a look at the answers on this similar question.
After struggling with this problem myself, I've taken to using JavaScript for conditional formatting, since it can work with dates (including the current date), numbers, and strings, and it can manipulate HTML pretty easily.
A bit of client side code can automatically:
Detect whether a list of conditional formatting rules exists
Create the list if necessary
Provide an interface for creating new rules (which are stored as list items) that should apply to the current page
Loop through all the current page's rules and apply them to any list view web parts detected on the same page
The nicest part of this approach is that I can add the web part to a page and just let the business users configure the formatting to their liking; no need for me to be further involved whenever they want to change something.
I put the combined HTML/CSS/JavaScript in a text file that can be saved to a document library somewhere in my site collection. To then add the conditional formatting to page, I just add a content editor web part to the page and set its "content link" property to be the path to the text file.
The content editor web part then displays a gray "Conditional Formatting" button that the user can click to view and modify the formatting rules, as seen in the screen shot below.
Here's the conditional formatting code for SharePoint 2010 that I use in my text file:
<div id="_conditional_formatting_link" style="display:none; ">
<div unselectable="on" style="display:inline-block; user-select:none; cursor:pointer; padding: 3px; background-color:#9b9b9b; color:white; border:1px solid #888;" onclick="javascript:var rules = document.getElementById('_conditional_formatting'); if(rules.style.display == 'none'){rules.style.display = 'inline-block'}else{rules.style.display = 'none'}">
Conditional Formatting
</div>
</div>
<div id="_conditional_formatting" style="display:none;background-color:#dfdfdf; border: 1px solid black; width:95%; max-width:1100px;">
<a style="border:0px;padding:5px;float:right;" title="Reapply Formatting" href="javascript:getConditions(false);">
<img style="border:0px;" src="/_layouts/images/staticrefresh.gif"/>
</a>
<ol id="_conditional_formatting_rules"></ol>
<div style="text-align:right; ">
<div id="_add_conditional_formatting_rule" unselectable="on" onclick="javascript: Add_Conditional_Formatting();" style="user-select:none; cursor:pointer; padding: 3px; margin: 3px; display:inline-block; background-color:#9b9b9b; border:1px solid #888; color:white;">Add Rule</div>
</div>
</div>
<script>
this.cfl = this.cfl ? this.cfl : new Object(null);
var conditionalFormattingList = "Conditional Formatting";
function getConditions(reloadRules) {
/* if reloadRules, query the conditions list and get all the rules. Otherwise just reapply the ones in memory to the current document. */
if (typeof reloadRules == "undefined") { reloadRules = true; }
if (reloadRules) {
var conditionalFormattingRules = document.getElementById("_conditional_formatting_rules");
while (conditionalFormattingRules.children.length > 0) { /* Clear out the currently displayed list of rules. */
conditionalFormattingRules.removeChild(conditionalFormattingRules.children[0]);
}
this.cfl.clientContext = new SP.ClientContext();
this.cfl.user = cfl.clientContext.get_web().get_currentUser();
var list = cfl.clientContext.get_web().get_lists().getByTitle(conditionalFormattingList);
var camlQuery = new SP.CamlQuery();
var folder = list.get_rootFolder();
camlQuery.set_viewXml('<View><Query><Where><Eq><FieldRef Name=\'URL\' /><Value Type=\'Text\'>' + document.location.pathname + '</Value></Eq></Where><OrderBy><FieldRef Name=\'Priority\' /><FieldRef Name=\'Name\' /></OrderBy></Query></View>');
this.cfl.items = list.getItems(camlQuery);
cfl.clientContext.load(cfl.user);
cfl.clientContext.load(list, 'EffectiveBasePermissions');
cfl.clientContext.load(cfl.items);
cfl.clientContext.load(folder);
}
this.cfl.clientContext.executeQueryAsync(
Function.createDelegate(this,
function () {
var Me = this.cfl.user.get_title();
if (reloadRules) {
var baseFormUrl = folder.get_serverRelativeUrl() + "/EditForm.aspx?ID=";
/* Figure out if the current user has access to create or edit items on the Conditional Formatting list */
var perms = list.get_effectiveBasePermissions();
this.cfl.hasEdit = perms.has(SP.PermissionKind.editListItems);
this.cfl.hasCreate = perms.has(SP.PermissionKind.addListItems);
/* Fill an array with our formatting rules */
this.cfl.targets = [];
var itemEnumerator = this.cfl.items.getEnumerator();
while (itemEnumerator.moveNext()) {
var item = itemEnumerator.get_current();
var targ = new Object(null);
targ.column = item.get_item("Column");
targ.comparison = item.get_item("Comparison");
targ.style = item.get_item("Style");
targ.scope = item.get_item("Scope");
targ.type = item.get_item("Type");
targ.value = item.get_item("Value"); if (targ.value == null) { targ.value = ""; }
targ.id = item.get_item("ID");
targ.offset = item.get_item("Offset");
cfl.targets.push(targ);
}
}
if (!this.cfl.hasCreate) { document.getElementById("_add_conditional_formatting_rule").style.display = "none"; }
for (var targetIterator = 0, len = cfl.targets.length; targetIterator < len; targetIterator++) {
var currentTarget = cfl.targets[targetIterator];
if (reloadRules) {
var rulelist = document.getElementById("_conditional_formatting_rules");
var ruletoadd = document.createElement("li");
var comparisondisplay = currentTarget.type.indexOf("Field") != -1 ? "value of the <b>" + currentTarget.value + "</b> column" : "<b>'" + currentTarget.value + "'</b>";
if (currentTarget.type == "Special" || currentTarget.type == "Number") {
if (currentTarget.value.toString().toLowerCase() == "[me]") { comparisondisplay = "<b>[Me](" + Me + ")</b>"; }
else { comparisondisplay = "<b>" + currentTarget.value + "</b>"; }
}
if (currentTarget.value == "") { comparisondisplay = "<b>(blank)</b>"; }
if (currentTarget.offset != null) {
comparisondisplay += "<b>" + (currentTarget.offset < 0 ? " " : " +") + currentTarget.offset + "</b>"
}
var editLink = this.cfl.hasEdit ? "<div style='display:inline-block;cursor:pointer;' onclick='SP.UI.ModalDialog.commonModalDialogOpen(" + '"' + baseFormUrl + currentTarget.id + '&Source=' + document.location.pathname + '"' + ",{},refreshPageConditions); '>" + "<img src='/_layouts/images/EDITITEM.GIF' style='vertical-align:middle;' title='Customize' alt='Customize'/>" + " </div>" : "";
ruletoadd.innerHTML = editLink + "When <b>" + currentTarget.column + "</b> "
+ currentTarget.comparison + " " + comparisondisplay
+ ", apply {" + (currentTarget.style == null ? "remove all formatting" : "<span style='" + currentTarget.style + "'>" + currentTarget.style + "</span>") + "} to the <b>" + currentTarget.scope + "</b>" + ((currentTarget.scope != "Cell" && currentTarget.scope != "Row") ? " column" : "");
rulelist.appendChild(ruletoadd);
}
var tables = document.querySelectorAll("table.ms-listviewtable"); /* Should get all the list view web parts on the page. */
var t_i = 0;
while (t_i < tables.length) {
var columnIndex = null; /* Index of the column to compare against */
var valueIndex = null; /* Index of a second column from which to pull the value to compare */
var styleTargetIndex = null; /* Index of a column to apply formatting to */
var thisTable = tables[t_i];
var headings = thisTable.rows[0].cells;
var h_i = 0;
while (h_i < headings.length) { /* Check all the column headings... */
var thisHeading = headings[h_i].querySelector("div:first-child");
if (thisHeading != null) {
/* In Internet Explorer, the headings have a DisplayName attribute you can grab. If that's not there, just grab the innerText or textContent */
var dispname = thisHeading.DisplayName ? thisHeading.DisplayName : (thisHeading.innerText ? thisHeading.innerText : thisHeading.textContent);
dispname = dispname.replace(/^\s+|\s+$/g, '');/* removes leading and trailing whitespace */
if (currentTarget.scope != "Cell" && currentTarget.scope != "Row") {
/*If the scope isn't Cell or Row, see if this is the cell to which the formatting should applied */
if (dispname == currentTarget.scope) { styleTargetIndex = h_i; }
}
if (currentTarget.type.indexOf("Field") != -1) {
/*If the value type is a Field, check to see if this is the field whose value we care about */
if (dispname == currentTarget.value.toString()) { valueIndex = h_i; }
}
if (dispname == currentTarget.column) { columnIndex = h_i; }
}
h_i += 1;
}
if (columnIndex != null) { /* If we found a matching heading, let's try to apply the rules... */
var rows = thisTable.rows;
for (var i = (rows.length > 0 ? 1 : 0) ; i < rows.length; i++) {
var cells = rows[i].children;
if (cells.length <= columnIndex) { continue }
var innerLink = cells[columnIndex].querySelector("a"); /* I want to specifically target links so that we can change their text color if necessary */
/* Populate valueToEval with the text value of the current cell, or its inner link if it has one */
var valueToEval = cells[columnIndex].innerText ? (innerLink != null ? innerLink.innerText : cells[columnIndex].innerText) : (innerLink != null ? innerLink.textContent : cells[columnIndex].textContent);
if (typeof (valueToEval) == "undefined") { valueToEval = "" } /* Treat empties as blanks */
var listValueToCompareAgainst = null;
if (valueIndex != null) { /* valueIndex only has a value if we need to grab the comparison value from another column on the list */
valueLink = cells[valueIndex].querySelector("a");
listValueToCompareAgainst = cells[valueIndex].innerText ? (valueLink != null ? valueLink.innerText : cells[valueIndex].innerText) : (valueLink != null ? valueLink.textContent : cells[valueIndex].textContent);
}
var needsStyling = false;
switch (currentTarget.type) {
case "Number":
if (!isNaN(Number(valueToEval))) {
valueToEval = +(valueToEval);
}
if (!isNaN(Number(currentTarget.value))) {
currentTarget.value = +(currentTarget.value);
}
break;
case "Date":
valueToEval = new Date(valueToEval);
currentTarget.value = new Date(currentTarget.value);
if (currentTarget.offset != null) {
currentTarget.value.setDate(currentTarget.value.getDate() + +(currentTarget.offset));
}
break;
case "Text": /* Already covered, bro */ break;
case "Date Field":
valueToEval = new Date(valueToEval);
currentTarget.value = new Date(listValueToCompareAgainst);
if (currentTarget.offset != null) {
currentTarget.value.setDate(currentTarget.value.getDate() + +(currentTarget.offset));
}
break;
case "Text Field": currentTarget.value = listValueToCompareAgainst; break;
case "Number Field":
if (!isNaN(Number(listValueToCompareAgainst))) {
currentTarget.value = listValueToCompareAgainst;
if (currentTarget.offset != null) {
currentTarget.value += Number(currentTarget.offset);
}
}
if (!isNaN(Number(valueToEval))) {
valueToEval = Number(valueToEval);
}
break;
case "Special":
if (currentTarget.value.toLowerCase) {
if (currentTarget.value.toLowerCase() == "[me]") { currentTarget.value = Me }
else if (currentTarget.value.toLowerCase().indexOf("[today]") != -1) {
var dateDifference = Number(currentTarget.value.toLowerCase().replace("[today]", "").replace(" ", "").replace("+", ""));
currentTarget.value = new Date();
if (!isNaN(dateDifference)) { currentTarget.value.setDate(currentTarget.value.getDate() + dateDifference); }
if (currentTarget.offset != null) {
currentTarget.value.setDate(currentTarget.value.getDate() + Number(currentTarget.offset));
}
valueToEval = new Date(valueToEval);
}
} else { valueToEval = new Date(valueToEval); }
break;
}
switch (currentTarget.comparison) {
case "Greater Than or Equal To": needsStyling = (valueToEval >= currentTarget.value); break;
case "Greater Than": needsStyling = (valueToEval > currentTarget.value); break;
case "Less Than or Equal To": needsStyling = (valueToEval <= currentTarget.value); break;
case "Less Than": needsStyling = (valueToEval < currentTarget.value); break;
case "Equal To": needsStyling = (valueToEval == currentTarget.value); break;
case "Not Equal To": needsStyling = (valueToEval != currentTarget.value); break;
case "Contains": needsStyling = (valueToEval.indexOf(currentTarget.value) != -1); break;
case "Does Not Contain": needsStyling = (valueToEval.indexOf(currentTarget.value) == -1); break;
}
if (needsStyling) {
var links;
if (currentTarget.scope != "Row") {
var targetIndex = (styleTargetIndex != null) ? styleTargetIndex : columnIndex;
cells[targetIndex].setAttribute("style", currentTarget.style);
links = cells[targetIndex].querySelectorAll("a");
} else {
rows[i].setAttribute("style", currentTarget.style);
for (var j = 0; j < cells.length; j++) {
cells[j].setAttribute("style", currentTarget.style);
}
links = rows[i].querySelectorAll("a");
}
for (var j = 0; j < links.length; j++) {
if (links[j].title != "Open Menu") {
links[j].setAttribute("style", currentTarget.style);
links[j].style.backgroundColor = "";
}
links[j].style.border = "0px";
}
}
}
}
t_i += 1;
}
}
document.getElementById("_conditional_formatting_link").style.display = "inline-block";
}
),
Function.createDelegate(this,
function (sender, args) { /* There was an error accessing the list. Time to create it! */
var lci = new SP.ListCreationInformation();
lci.set_title(conditionalFormattingList);
lci.set_templateType(SP.ListTemplateType.genericList);
var condition_list = clientContext.get_web().get_lists().add(lci);
clientContext.load(condition_list);
var colTitle = condition_list.get_fields().getByTitle("Title");
colTitle.set_required(false); colTitle.set_hidden(true); colTitle.update();
condition_list.update();
var colColumn = condition_list.get_fields().addFieldAsXml('<Field Description=\'The column to compare (must be visible on the page)\' DisplayName=\'Column\' Type=\'Text\'/>', true, SP.AddFieldOptions.defaultValue);
var colComparison = condition_list.get_fields().addFieldAsXml('<Field Description=\'\' Type=\'Choice\' DisplayName=\'Comparison\' Format=\'Dropdown\' FillInChoice=\'FALSE\'><Default>Equal To</Default><CHOICES><CHOICE>Greater Than</CHOICE><CHOICE>Greater Than or Equal To</CHOICE><CHOICE>Equal To</CHOICE><CHOICE>Less Than or Equal To</CHOICE><CHOICE>Less Than</CHOICE><CHOICE>Not Equal To</CHOICE><CHOICE>Contains</CHOICE><CHOICE>Does Not Contain</CHOICE></CHOICES></Field>', true, SP.AddFieldOptions.defaultValue);
var colValue = condition_list.get_fields().addFieldAsXml('<Field Description=\'The value or the name of a column to compare against\' DisplayName=\'Value\' Type=\'Text\'/>', true, SP.AddFieldOptions.defaultValue);
var colType = condition_list.get_fields().addFieldAsXml('<Field Description=\'Indicate the type of value you are comparing against. Choose Special if using the [Me] or [Today] placeholders.\' Type=\'Choice\' DisplayName=\'Type\' Format=\'Dropdown\' FillInChoice=\'FALSE\'><Default>Text</Default><CHOICES><CHOICE>Date</CHOICE><CHOICE>Number</CHOICE><CHOICE>Text</CHOICE><CHOICE>Date Field</CHOICE><CHOICE>Number Field</CHOICE><CHOICE>Text Field</CHOICE><CHOICE>Special</CHOICE></CHOICES></Field>');
var colOffset = condition_list.get_fields().addFieldAsXml('<Field Description=\'Optionally specify a number to offset the value by when comparing against a number or date.\' DisplayName=\'Offset\' Type=\'Number\' />', true, SP.AddFieldOptions.defaultValue);
var colStyle = condition_list.get_fields().addFieldAsXml('<Field NumLines=\'4\' Description=\'The CSS to apply to when the condition is met. Leave blank to remove formatting. Example syntax: background-color:darkred; color:white; font-weight:bold;\' DisplayName=\'Style\' Type=\'Note\' />', true, SP.AddFieldOptions.defaultValue);
var colScope = condition_list.get_fields().addFieldAsXml('<Field Description=\'The scope to which the style should be applied. Choose Row, Cell, or specify a column name.\' Type=\'Choice\' DisplayName=\'Scope\' Format=\'Dropdown\' FillInChoice=\'TRUE\'><Default>Cell</Default><CHOICES><CHOICE>Cell</CHOICE><CHOICE>Row</CHOICE></CHOICES></Field>', true, SP.AddFieldOptions.defaultValue);
var colPriority = condition_list.get_fields().addFieldAsXml('<Field Description=\'Priority determines which styles are applied in case of overlapping conditions. Higher numbers are applied later.\' DisplayName=\'Priority\' Type=\'Number\' />', true, SP.AddFieldOptions.defaultValue);
var colUrl = condition_list.get_fields().addFieldAsXml('<Field Description=\'Page where this rule should be applied\' DisplayName=\'URL\' Type=\'Text\'/>', true, SP.AddFieldOptions.defaultValue);
clientContext.executeQueryAsync(
Function.createDelegate(this, function () { getConditions(); }),
Function.createDelegate(this, function (sender, args) { document.getElementById("_conditional_formatting").innerHTML = ("An error occcurred while trying to apply conditional formatting to the list for you. Error details: " + args.get_message() + " " + args.get_stackTrace()); document.getElementById("_conditional_formatting_link").style.display = "inline-block"; }));
}
));
}
/* This method is called when the Add Rule button is clicked. */
function Add_Conditional_Formatting() {
/* Create a new item with only the URL and Priority fields filled out */
var currUrl = document.location.pathname;
var clientContext = new SP.ClientContext();
var itemCreateInfo = new SP.ListItemCreationInformation();
var newItem = clientContext.get_web().get_lists().getByTitle(conditionalFormattingList).addItem(itemCreateInfo);
newItem.set_item('URL', currUrl);
/* Give the new item a priority that will put it at the end of the list. This is kind of a hack since the highest priority is not necessarily the rulecount. */
newItem.set_item('Priority', document.getElementById("_conditional_formatting_rules").children.length + 1);
newItem.update();
clientContext.load(newItem);
clientContext.executeQueryAsync(Function.createDelegate(this, function () {
getConditions(); /* Reload to refresh the rules list after adding the item */
}), Function.createDelegate(this, function (sender, args) { alert(args.get_message()); }));
}
/* This method is called when the Edit Item dialog box is closed. It refreshes the page it the item was saved. */
function refreshPageConditions(result) { if (result != SP.UI.DialogResult.cancel) { window.location.replace(document.location.href) } }
ExecuteOrDelayUntilScriptLoaded(function () {
getConditions();
/* If there are any collapsible sections on the page, keep checking to see whether formatting needs to be reapplied */
this.cfl.TableRowCount = 0;
if (document.querySelector("img[alt='expand']") != null) {
setInterval(function () {
var tempTableRowCount = document.querySelectorAll("tr").length;
if (tempTableRowCount != this.cfl.TableRowCount) {
/* Only reapply formatting if the count of table rows is different than it was previously */
this.cfl.TableRowCount = tempTableRowCount;
getConditions(false) /* Passing false reapplies loaded rules without re-querying the SharePoint list */
}
}, 1000)
}
}, "SP.JS");
</script>

Resources