Tabulator: Update column definition after data is loaded - tabulator

I am using autoColumns: true and data loaded through the builtin ajax call ajaxURL: "/...". After data is loaded I would like to dynamically update a few columns to allow dropdowns to be used. I know I could pre-define the column definitions prior to loading data but I am trying to do this dynamically.
In the code example below, I am adding new columns after data is loaded dynamically through a razor loop. This builds select lists dynamically and I used it as a POC to test part of what I need to do with updating column definitions.
How do I replace an existing columns definition and trigger an update without replacing the cells below data?
After the column definition is updated while keeping data how do I make sure the editor "select" is set to the cell value, my thinking this would happen automatically after the definition is updated.
Thanks!
dataLoaded: function(data) {
/////////////////////////////////////////
// Example, adding dynamic columns
/////////////////////////////////////////
#foreach(var item in Model.Report.ReportFields) {
<text>
table.addColumn({
title: "#item.FieldName",
field: "#item.FieldName",
editor: "select",
editorParams: {
values: {
#foreach(var fi in item.ReportFieldItems) {
<text> "#fi.ItemValue": "#fi.ItemName", </text>
}
}
}
});
</text>
}
///////////////////////////////////////////////////////////////
// Example updating column definition for field name "Status"
///////////////////////////////////////////////////////////////
var aColumns = table.getColumns();
for (var i = 0; i < aColumns.length; i++) {
if (aColumns[i]._column.field == 'Status') {
console.log(aColumns[i]._column.defitions);
aColumns[i]._column.definition.editor = "select";
var obj = {
key1: "value1",
key2: "value2"
};
var obj2 = {
values: obj
};
aColumns[i]._column.definition.editorParams = obj2;
}
}
}

table.setColumns is my friend. Before I could use this I needed to build the column definition from the returned column data. Seems like an opportunity for a new feature instead of a quick loop to do this directly. This answers both questions, first to update an existing definition and to not replace the existing data.
var newColumns = [];
var aColumns = table.getColumns();
for (var i = 0; i < aColumns.length; i++) {
if (aColumns[i]._column.field == 'Status') {
console.log(aColumns[i]._column.defitions);
aColumns[i]._column.definition.editor = "select";
var obj = {
key1: "value1",
key2: "value2"
};
var obj2 = {
values: obj
};
aColumns[i]._column.definition.editorParams = obj2;
}
newColumns.push(aColumns[i]._column.definition);
}
console.log(newColumns);
table.setColumns(newColumns);

Related

Suitescript 2.0 - How to show a field and update value based on dropdown?

I have a drop down that, when a particular option is selected a hidden field will be displayed and that field which have a new value. I am able to get the field to display but am not able to populate the field with the value. Script below:
function fieldChanged(context) {
var records = context.currentRecord;
if (context.fieldId == 'custbody_data') {
var note = context.currentRecord.getField({ fieldId: 'custbody_note' });
var type = records.getValue({
fieldId: 'custbody_data'
});
if (type == "2") {
note.isDisplay = true;
note.setValue = "test";
} else if (type == "1") {
note.isDisplay = false;
note.setValue = "";
}
}
}
return {
fieldChanged: fieldChanged
}
note.setValue = "";
There are two problems in what you are trying to do:
With the NetSuite API, to manipulate the value of fields in the record you need to use the N/currentRecord#Record object , not the N/currentRecord#Field. In other words you need to be calling context.currentRecord.setValue().
setValue is a method, not a property. I.e. you need to call the function setValue() with the new value, and not assign it a value (note.setValue = "new value") like you are attempting.
Putting those together, the correct syntax for updating a field value is:
context.currentRecord.setValue({
fieldId: 'custbody_note',
value: "test",
});

Prevent nested lists in text-editor (froala)

I need to prevent/disable nested lists in text editor implemented in Angular. So far i wrote a hack that undos a nested list when created by the user. But if the user creates a normal list and presses the tab-key the list is shown as nested for a few milliseconds until my hack sets in back to a normal list. I need something like event.preventDefault() or stopPropagation() on tab-event keydown but unfortunately that event is not tracked for some reason. Also the froala settings with tabSpaces: falseis not showing any difference when it comes to nested list...in summary i want is: if the user creates a list and presses the tab-key that nothing happens, not even for a millisecond. Has anyone an idea about that?
Froala’s support told us, there’s no built-in way to suppress nested list creation. They result from TAB key getting hit with the caret on a list item. However we found a way to get around this using MutationObserver
Basically we move the now nested list item to his former sibling and remove the newly created list. Finally we take care of the caret position.
var observer = new MutationObserver(mutationObserverCallback);
observer.observe(editorNode, {
childList: true,
subtree: true
});
var mutationObserverCallback = function (mutationList) {
var setCaret = function (ele) {
if (ele.nextSibling) {
ele = ele.nextSibling;
}
var range = document.createRange();
var sel = window.getSelection();
range.setStart(ele, 0);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
};
var handleAddedListNode = function (listNode) {
if (! listNode.parentNode) {
return;
}
var parentListItem = listNode.parentNode.closest('li');
if (!parentListItem) {
return;
}
var idx = listNode.children.length - 1;
while (idx >= 0) {
var childNode = listNode.children[idx];
if (parentListItem.nextSibling) {
parentListItem.parentNode.insertBefore(childNode, parentListItem.nextSibling);
} else {
parentListItem.parentNode.appendChild(childNode);
}
--idx;
}
setCaret(parentListItem);
listNode.parentNode.removeChild(listNode);
};
mutationList.forEach(function (mutation) {
var addedNodes = mutation.addedNodes;
if (!addedNodes.length) {
return;
}
for (var i = 0; i < addedNodes.length; i++) {
var currentNode = addedNodes[i];
switch (currentNode.nodeName.toLowerCase()) {
case 'ol':
case 'ul':
handleAddedListNode(currentNode);
break;
// more optimizations
}
}
})
};

Deeply nested data objects in multidimensional object

I have a multidimensional object and using Vue, I am trying to make the inner object reactive.
My object looks like this:
data() {
return {
myObject: {}
}
}
And the filled data looks like this:
myObject: {
1: { // (client)
0: "X", // (index) : (value)
1: "Y"
},
2: {
0: "A",
2: "B"
}
}
If I try using:
let value = "X";
let client = 1;
let index = 1;
let obj = {};
obj[client][index] = value;
this.myObject = Object.assign({}, this.myObject, obj);
It throws an error:
TypeError: Cannot set property '0' of undefined
And if I try below, it overwrites the initial values as it is initially setting the object to {}
let obj = {};
obj[index] = value;
let parentObj = {};
parentObj[client] = obj;
this.myObject = Object.assign({}, this.myObject, parentObj);
What is the proper way of adding the values to the multidimensional object?
In javascript, dim2Thing[1][1] = ... expressions require dim2Thing[1] to exist. This is why you get the error you mentioned. So you can do two expressions, which should work fine:
dim2Thing[1] = dim2Thing[1] || {}
dim2Thing[1][1] = otherThing
For the last block, you mention that it "overwrites the initial values"
I think what's actually happening here is just that Object.assign is not recursive. It only merges top-level keys. So if parentObj has a key that over-laps with this.myObj, then sub-keys will be lost.
Object.assign({ a: { b: 2} }, { a: { c: 3 } }) // returns { a: { c: 3 } }
This is what I interpret your code as trying to do - though I am unfamiliar with vue.js at this time, so I cannot assure it will have the desired result to your webpage:
let value = "X";
let client = 1;
let index = 1;
const newObj = Object.assign({}, this.myObject);
// if you have lodash _.set is handy
newObj[client] = newObj[client] || {}; // whatever was there, or a new object
newObj[client][index] = value
this.myObject = newObj
Just use an array, thats reactive by design.
If you need to get elements from the array in your template or anywhere just add a find method
// temp
late
<div v-for="(value, idx) in myArray">{{find(obj => obj.id === idx)}}</div>
methods: {
find (searchFunction) {
return this.myArray.find(searchFunction)
}
}

Leaflet, geojson: filter out entire features/objects with a null value in them

I have a geojson file which I'm getting from this website which somehow contains corrupt data, with a coordinate value = null.
http://measuringamsterdam.nl/datalist/kijk/
And I'm using it in my code like this:
//Retrieve all data and add to map
$.each(datalistObject['idlist'], function(key, value) {
$.getJSON('http://measuringamsterdam.nl/datalist/kijk/' + value['id'], function(data) {
textbox = value['name'];
var dataid = L.geoJson([data], {
style: function (feature) {
return feature.properties && feature.properties.style;
},
onEachFeature: onEachFeature,
pointToLayer: function (feature, latlng) {
return L.marker(latlng, {
icon: value['icon']
});
}
}).addTo(jsonGroup);
console.log(jsonGroup);
},function(xhr) { console.error(xhr); });
});
Now somehow I need to filter out the features/objects where the coordinates have a null value.
I really need to filter the data that point in my code since I need the + value['id'] part in the getJSON code.
Ane ideas?
Using the following code you will generate a new array. Which will include only the filtered data.
var newArray = data.filter(function (el) {
return el.value != 'null';
});
You can also apply multiple filters, for example:
return el.value_a != 'null' && el.value_b > 100;
Hopefully this will work!

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