http://tabulator.info/examples/4.1
The Editable Data example above shows the use of a custom editor for the date field (example in the link is DOB). Similar examples exist in earlier tabulator versions as well as here and Github. The javascript date picker that results works perfectly for most users but not all (even if also on Chrome). So the alternate approach often attempted by the users is to try and enter the date directly into the cell. But unfortunately this is problematic --in the same way it is with the linked example. Changing the month and day isn't too bad -- but directly changing the year is very difficult. Does anyone have a potential solution? I've explored everything from blur/focus/different formats/"flatpicker"/etc - but I'm coming up empty.
The best approach to get full cross browser support would be to create a custom formatter that used a 3rd party datepicker library, for example the jQuery UI datepicker. The correct choice of date picker would depend on your needs and your existing frontend framework.
in the case of the jQuery datepicker the custom formatter could look something like this (this example uses the standard input editor, you will notice in the onRendered function it turns the standard input into the jQuery datepicker):
var dateEditor = function(cell, onRendered, success, cancel, editorParams){
var cellValue = cell.getValue(),
input = document.createElement("input");
input.setAttribute("type", "text");
input.style.padding = "4px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
input.value = typeof cellValue !== "undefined" ? cellValue : "";
onRendered(function(){
input.style.height = "100%";
$(input).datepicker(); //turn input into datepicker
input.focus();
});
function onChange(e){
if(((cellValue === null || typeof cellValue === "undefined") && input.value !== "") || input.value != cellValue){
success(input.value);
}else{
cancel();
}
}
//submit new value on blur or change
input.addEventListener("change", onChange);
input.addEventListener("blur", onChange);
//submit new value on enter
input.addEventListener("keydown", function(e){
switch(e.keyCode){
case 13:
success(input.value);
break;
case 27:
cancel();
break;
}
});
return input;
}
You can then add this to a column in the column definition:
{title:"Date", field:"date", editor:dateEditor}
I couldn't get what Oli suggested to work. Then again, I might be missing something simple as I am much more of a novice. After a lot of trial+error, this is the hack kind of approach I ended up creating -- builds upon Oli's onRender suggestion but then uses datepicker's onSelect the rest of the way.
The good: The datepicker comes up regardless where in the cell the user clicks -- so the user is less tempted to try and enter manually. If the user happens to try and enter manually, they can do so.
The less-than-ideal: If the user does manually enter, the datepicker won't go away until he/she clicks elsewhere. But not a showstopper.
//Date Editor//
var dateEditor = function(cell, onRendered, success, cancel, editorParams){
var cellValue = cell.getValue(),
input = document.createElement("input");
input.setAttribute("type", "text");
input.style.padding = "4px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
input.value = typeof cellValue !== "undefined" ? cellValue : "";
onRendered(function(){
$(input).datepicker({
onSelect: function(dateStr) {
var dateselected = $(this).datepicker('getDate');
var cleandate = (moment(dateselected, "YYYY-MM-DD").format("MM/DD/YYYY"));
$(input).datepicker( "destroy" );
cell.setValue(cleandate,true);
cancel();
},
});
input.style.height = "100%";
});
return input;
};
I use datepicker from bootstrap, this is my code
var dateEditor = function (cell, onRendered, success, cancel, editorParams) {
//create and style input
var editor = $("<input type='text'/>");
// datepicker
editor.datepicker({
language: 'ja',
format: 'yyyy-mm-dd',
autoclose: true,
}).on('changeDate', function() {
if(editorParams != 'row'){
editor.trigger('keyup');
}else{
editor.trigger('change');
}
});
editor.css({
"padding": "3px",
"width": "100%",
"height": "100%",
"box-sizing": "border-box",
});
editor.val(cell.getValue());
onRendered(function(){
editor.focus();
});
editor.on("blur", function (e) {
e.preventDefault();
if(editor.val() === '') {
success(cell.getValue());
}
else {
//submit new value on change
editor.on("change", function (e) {
success(editor.val());
});
}
});
return editor;
}
Related
I am using Tabulator 5.x. I have a table with header filtering. The column in question is the last column "Transcribed". Is there a way to have the typical down arrow on the right side of the select box that shows the end user it is a drop down list similar to if you were using option in html? Rather than having to click on it filter field to see the choices.
I looked in documentation but do not see any examples using a down arrow. I also looked in the CSS, but did not anything if indeed it was there.
var table = new Tabulator("#transcription-table", {
height:"640px",
layout:"fitDataStretch",
ajaxURL:"get_transcriptions.php",
columns:[
{title:"ID", field:"id", headerSort:false, visible:false},
{title:"Song Title", field:"songtitle", width:350, sorter:"string", headerFilter:"input"},
{title:"Artist / Group", field:"artistgroup", widthGrow:1.5 ,sorter:"string", headerFilter:"input"},
{title:"Transcribed", field:"transcribed", widthGrow:1.2, sorter:"string", headerTooltip:"Transcribed into music notation", editor:"select", editorParams:{values:{"Yes":"Yes", "No":"No"}}, headerFilter:true, headerFilterParams:{values:{"Yes":"Yes", "No":"No", "":""}}},
]
});
Thank you.
You can create your own editor by extending editor module as
Tabulator.extendModule("edit", "editors", {
selectwithdrop: function (cell, onRendered, success, cancel, editorParams) {
var cellValue = cell.getValue().toUpperCase(),
input = document.createElement("select");
Object.keys(editorParams.values).forEach((key) => {
let option = document.createElement("option");
option.text = editorParams.values[key];
option.value = key;
input.add(option);
});
input.style.padding = "10px";
input.style.width = "100%";
input.style.boxSizing = "border-box";
input.style.border = "1px solid #4b4b4b";
input.style.borderRadius = "5px";
input.style.outline = "none";
input.value = cellValue;
// onRendered(function () {
// input.focus();
// input.style.height = "100%";
// });
function onChange(e) {
success(input.value);
}
//submit new value on blur or change
input.addEventListener("change", onChange);
// input.addEventListener("blur", onChange);
//submit new value on enter
return input;
},
});
Working Demo CodeSandBox
I would like to do something like Grouping with a bottomRowCalc but having the visibility of the rows of data being optional. Currently I'm using getData('active') to add up the values and row.update() on the "sum" row with the values. But if I filter out the data rows, they wont appear in the getData('active'), so I'd have do a getData().forEach(row => {}) and manually check for another field flag I guess to see if it should be included in the sum.
It sure would be nice if I could continue to use getData('active') but set row.visibility(false) on rows I dont want to show, but still be in active ?
Is answering your own question a sin ?
Anyway, for posterity, in case anyone else wants to do this :
rowFormatter:function(row) {
let display = 'inline-block';
if (some_condition) display= 'none';
row.getElement().style.display = display;
return true;
}
rowFormatter: function(row){
var row_data= row.getData()
console.log('row_data is...',row_data);
if (row_data === 'yes'){
const children = row.getElement().childNodes;
children.forEach((child) => {
child.style.backgroundColor = 'red';
})
}
else (row_data === 'Upcomming')
{ let display = 'inline-block';
display= 'none';
row.getElement().style.display = display;
return true;
}
},
In this case you will miss s.r. with the row
I want that whenever a user visits a certain page with Project Center webpart in it, she should have her View already set (forced) e.g. "Summary", "Earned Value" etc.
I know that the view is bound to the user's last session, so if during her last visit the user changed the View into "Earned Value", the next one will be "Earned value".
How can I force that everytime a user opens the page with Project Center webpart, she will always open the "Summary" view?
Thanks.
This is a JavaScript solution I wrote that uses a query string parameter "viewuid" (GUID for the view) to set the view
var projCenterExt;
var JsGridSatellite;
_spBodyOnLoadFunctionNames.push("projCenterChangeView")
function projCenterChangeView()
{
if (window.location.search.toLowerCase().indexOf("viewuid") >= 0)
{
var JsGridViewUid = window.location.search.toLowerCase().split("viewuid=")[1].split("&")[0];
if (typeof projectCenterComponent !== 'undefined')
{
if (typeof JsGridSatellite === 'undefined') JsGridSatellite = projectCenterComponent.get_GridSatellite();
JsGridSatellite.LoadNewView({uid: JsGridViewUid});
}
}
}
Thanks Papa Daniel. You got us started but this would only work in Chrome. We had to add a pause in there and then it worked in I.E. Just to be clear, you need to find the GUID of the view you want to display and use that in your hyperlink.
Here is my example
http://projectserver/PWA/SitePages/ITDDash.aspx?idViewUID=38f25d41-2391-4ed4-b84e-2befec36b80b
var projCenterExt;
var JsGridSatellite;
_spBodyOnLoadFunctionNames.push("projCenterChangeView")
//console.debug("before projCenterChangeView");
function projCenterChangeView()
{
//alert("in projCenterChangeView");
//console.debug("before 3 secs");
setTimeout(function(){
//alert("in if:"+window.location.search.toLowerCase().indexOf("viewuid") );
if (document.location.search.toLowerCase().indexOf("viewuid") >= 0)
{
var JsGridViewUid = document.location.search.toLowerCase().split("viewuid=")[1].split("&")[0];
//alert("in if:"+JsGridViewUid );
if (typeof projectCenterComponent !== 'undefined')
{
if (typeof JsGridSatellite === 'undefined'){
//console.debug("JsGridSatellite kis undefined");
JsGridSatellite = projectCenterComponent.get_GridSatellite();
//alert("jjc test");
}
JsGridSatellite.LoadNewView({uid: JsGridViewUid}); //orig
}
//JsGridSatellite.LoadNewView({uid: JsGridViewUid});
}
//console.debug("after 3 secs");
}, 1000);
//alert("at end");
}
I need to validate inline editing in NetSuite.
I already have a Client Script in place that works great when editing the record normally.
I tried adding a User Event script that on the before save function that validates the record, but it appears this is ignored with inline editing.
Has anybody ran into this before?
Any insight you can provide would be helpful. Thanks!
Edits:
The relevant code from the UE script:
function beforeSubmit(type){
if (type == "create" || type == "edit" || type == "xedit") {
var status = nlapiGetContext().getSetting("SCRIPT", "...");
var amount = Number(nlapiGetContext().getSetting("SCRIPT", "..."));
var nr = nlapiGetNewRecord();
var entitystatus = nr.getFieldValue("entitystatus");
var projectedtotal = Number(nr.getFieldValue("projectedtotal"));
if (entitystatus == status && projectedtotal >= amount) {
var statusText = nr.getFieldText("entitystatus");
var message = "ERROR...";
throw nlapiCreateError("...", message, true);
}
}
}
This applies to the opportunity record.
The field being validated is Projected Total with id projectedtotal.
My mistake, I misunderstood how xedit handled nlapiGetNewRecord(). Calling nlapiGetNewRecord when in xedit only returns the edited fields, not the entire record. Thus, the if statement was never true in xedit mode, because either the amount or the status would be null (it was very unlikely the user would edit both at the same time, and validation relies on both these fields' values).
I edited the code to lookup the field value if it is not present in the new record. Now everything works as expected!
Thanks everyone for the help!
For reference, the corrected code is below.
function beforeSubmit(type){
if (type == "create" || type == "edit" || type == "xedit") {
var status = nlapiGetContext().getSetting("SCRIPT", "...");
var amount = Number(nlapiGetContext().getSetting("SCRIPT", "..."));
var nr = nlapiGetNewRecord();
//Attempt to get values normally
var entitystatus = nr.getFieldValue("entitystatus");
var projectedtotal = Number(nr.getFieldValue("projectedtotal"));
var id = nr.getId();
//If values were null, it's likely they were not edited and
//thus not present in nr. Look them up.
if(!entitystatus){
entitystatus = nlapiLookupField("opportunity", id, "entitystatus");
}
if(!projectedtotal){
projectedtotal = Number(nlapiLookupField("opportunity", id, "projectedtotal"));
}
if (entitystatus == status && projectedtotal >= amount) {
var message = "ERROR...";
throw nlapiCreateError("101", message, true);
}
}
}
In your user event are you checking the value of the type parameter. For inline editing, the value of the type is 'xedit'.
Has anyone discovered a way to extend or modify the functionality of the SharePoint Datasheet view (the view used when you edit a list in Datasheet mode, the one that looks like a basic Excel worksheet)?
I need to do several things to it, if possible, but I have yet to find a decent non-hackish way to change any functionality in it.
EDIT: An example of what I wish to do is to enable cascading filtering on lookup fields - so a choice in one field limits the available choices in another. There is a method to do this in the standard view form, but the datasheet view is completely seperate.
Regards
Moo
I don't think you can modify it in any non-hackish way, but you can create a new datasheet view from scratch. You do this by creating a new ActiveX control, and exposing it as a COM object, and modifying the web.config file to make reference to the new ActiveX control.
There's an example here:
Creating a custom datasheet control.
Actually, you can do this. Here is a code snippet I stripped out of someplace where I am doing just what you asked. I tried to remove specifics.
var gridFieldOverrideExample = (function (){
function fieldView(ctx){
var val=ctx.CurrentItem[curFieldName];
var spanId=curFieldName+"span"+ctx.CurrentItem.ID;
if (ctx.inGridMode){
handleGridField(ctx, spanId);
}
return "<span id='"+spanId+"'>"+val+"</span>";
}
function handleGridField(ctx, spanID){
window.SP.SOD.executeOrDelayUntilScriptLoaded(function(){
window.SP.GanttControl.WaitForGanttCreation(function (ganttChart){
var gridColumn = null;
var editID = "EDIT_"+curFieldName+"_GRID_FIELD";
var columns = ganttChart.get_Columns();
for(var i=0;i<columns.length;i++){
if(columns[i].columnKey == curFieldName){
gridColumn = columns[i];
break;
}
}
if (gridColumn){
gridColumn.fnGetEditControlName = function(record, fieldKey){
return editID;
};
window.SP.JsGrid.PropertyType.Utils.RegisterEditControl(editID, function (ctx) {
editorInstance = new SP.JsGrid.EditControl.EditBoxEditControl(ctx, null);
editorInstance.NewValue = "";
editorInstance.SetValue = function (value) {
_cellContext = editorInstance.GetCellContext();
_cellContext.SetCurrentValue({ localized: value });
};
editorInstance.Unbind = function () {
//This happens when the grid cell loses focus - hide controls here, do cleanup, etc.
}
//Below I grabbed a reference to the original 'BindToCell' function so I can prepend to it by overwriting the event.
var origbtc = editorInstance.BindToCell;
editorInstance.BindToCell = function(cellContext){
if ((cellContext.record) &&
(cellContext.record.properties) &&
(cellContext.record.properties.ID) &&
(cellContext.record.properties.ID.dataValue)){
editorInstance.ItemID = cellContext.record.properties.ID.dataValue;
}
origbtc(cellContext);
};
//Below I grabbed a reference to the original 'OnBeginEdit' function so I can prepend to it by overwriting the event.
var origbte = editorInstance.OnBeginEdit;
editorInstance.TargetID;
editorInstance.OnBeginEdit = function (cellContext){
this.TargetID = cellContext.target.ID;
/*
. . .
Here is where you would include any custom rendering
. . .
*/
origbte(cellContext);
};
return editorInstance;
}, []);
}
});
},"spgantt.js");
}
return{
fieldView : fieldView
}
})();
(function () {
function OverrideFields(){
var overrideContext = {};
overrideContext.Templates = overrideContext.Templates || {};
overrideContext.Templates.Fields = {
'FieldToOverride' : {
'View': gridFieldOverrideExample.fieldView
}
};
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideContext);
}
ExecuteOrDelayUntilScriptLoaded(OverrideFields, 'clienttemplates.js');
})();
Also, there are a couple of other examples out there. Sorry, I don't have the links anymore: