Tabulator - Cell.setValue creating recursive loop for custom formatter, table not saving data - json

I'll try to be as brief as possible as my situation is unique and I am a fairly new programmer.
TL:DR - Cannot get the row to update, table does not remember initial values when editing, cell.setValue() creates recursive loop, cell.getRow().update() seems to do nothing, row does not resize in the event of editing working (still does not hold on to previous values)), unsure if mutator or cellEdit() callback are potential solutions.
I have JSON data populating my table from an AJAX call to a Java backend. One of the columns can either be one item, a list of multiple items, or none at all. I am using a custom formatter to populate that cell with the list of items, followed by icons to remove those items...
Some text here X
Some other text X
And some more X
When clicking the X icon, it should remove the item, and when clicking the text, it will drop down a list to choose another option.
Some text here X
Some other text X
Added another line X <----removed the previous item and selected a new
The problem I am having is that the table does not continuously hold onto previous values nor concatenates them with new selections. I have written some code to take care of the concatenation issue, but it still does not save that info within the table. I have tried to use cell.setValue() but this creates a recursive loop as all my code is within the custom formatter. I have also tried cell.getRow().update() but this seems to do nothing.
Also, clicking into the drop down and clicking away without selecting a value will still send the previous value as though it was clicked again. I was able to get it to function once, but it only changed the most recent values and did not include the original ones and it did not resize the row to be smaller.
I am unsure if a custom mutator would solve my issue, or even how to get the mutator to interact with the formatter to update the cell every time a change has been made. I also have a cellEdited: function(){} callback that is going to be designed to take the entire row of information as a JSON object to save it within a DAO and considered that I might be able to solve the issue there?
Here is my code for that column. This currently will load the page with the initial design yet does not work with editing. "cell.setValue()" and "cell.getRow().update()" are currently not included because they weren't working properly...
title: "Groups",
field: "associatedGroups",
variableHeight: true,
headerFilter: true,
headerFilterPlaceholder: "Search by Group...",
formatter: function (cell, formatterParams, onRendered) {
var data = [];
var newValue = cell.getValue();
var oldValue = cell.getOldValue();
var newValueIsArray = $.isArray(newValue);
var oldValueIsArray = $.isArray(oldValue);
if (oldValue !== null && newValue !== null) {
if (!oldValueIsArray && newValueIsArray) {
data = data.concat(newValue);
data.unshift(oldValue);
} else if (oldValueIsArray && !newValueIsArray) {
data = data.concat(oldValue);
data.push(newValue);
} else {
data.push(oldValue);
data.push(newValue);
}
} else {
data = newValue;
}
<!-------------------Value of "data" used here to determine visual layout----------------->
},
editor: "select",
responsive:
0,
editorParams:
{
values: groupNames
}
,
sorter: "string",
width:
212
},

Related

ngOnChanges only works when it's not the same value

So basically I have a modal component with an input field that tells it which modal should be opened (coz I didn't want to make a component for each modal):
#Input() type!:string
ngOnChanges(changes: SimpleChanges): void {
this.type = changes["type"].currentValue;
this.openModal();
}
that field is binded to one in the app component:
modalType = "auth";
HTML:
<app-modal [type] = modalType></app-modal>
In the beginning it's got the type "auth" (to login or register), but when I click on an icon I want to open a different modal, I do it like so:
<h1 id="options-route"
(click) ="modalType = 'settings'"
>⚙</h1>
but this only works the first time, when modalType already has the value "settings" the event doesn't trigger even though the value has technically changed
I think the problem is that it's the same value because i tried putting a button that does the exact same thing but with the value "auth" again and with that it was clear that the settings button only worked when tha last modal opened was auth and viceversa
any ideas? I want to be able to open the settings modal more than once consecutively possibly keeping onChange because ngDoCheck gets called a whole lot of times and it slows down the app
You need to include the changeDetectorRef, in order to continue in this way.
More about it https://angular.io/api/core/ChangeDetectorRef
Although, a better and a faster alternative is the use of a behavior Subject.
All you have to do is create a service that makes use of a behavior subject to cycle through each and every value exposed and then retrieve that value in as many components as you want. To do that just check for data changes in the ngOnInit of target component.
You may modify this for implementation,
private headerData = new BehaviorSubject(new HeaderData());
headerDataCurrent = this.headerData.asObservable();
changeHeaderData(headerDataNext : HeaderData) {
this.headerData.next(headerDataNext)
console.log("subscription - changeUserData - "+headerDataNext);
}
Explanation:
HeaderData is a class that includes the various values that can be shared with respective data types.
changeHeaderData({obj: value}), is used to update the subject with multiple values.
headerDataCurrent, an observable has to be subscribed to in the target component and data can be retrieved easily.
I mean i'm too l-a-z-y to use your slightly-not-so-much-tbh complicated answers so I just did this:
I added a counter that tops to 9 then gets resetted to 0 and I add it to the value
screwYouOnChangesImTheMasterAndYouShallDoMyBidding = 0;
//gets called onClick
openSettings(){
if(this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding === 9){
this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding = 0;
}
this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding = this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding + 1;
this.modalType = "settings"+this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding;
}
then in the child component I just cut that last character out:
ngOnChanges(changes: SimpleChanges): void {
let change = changes["type"].currentValue as string;
change = change.substring(0, change.length - 1);
this.type = change;
this.openModal();
}
works like a charm 😂

Control OnChangeAction default and revert to default when dynamically adding Widget to Google Add-on card

Starting a new question to extend Dynamically Add Widget on user click in Gmail Add-on using CardService Appscript (GAS)
I was able to get this to work on a fairly complex UI card I am designing.
There are two things that I can't seem to control:
The selector after OnChangeAction reverts to the default item, so the user looses the context of their selection. Is there a way to persist the selection when rebuilding the card?
There is no way to setup that the value of the default selected item to force an OnChangeAction event to push its value to the UI on the cards initial load.
Anyone able to use this method to accomplish the above?
I came up with a hack for this that seems to work.
I added a default item to the top of the item list with a label of item0
.addItem("Click here to select an existing item to edit","item0", true)
I also created a cache to cache this label. CacheService.getUserCache().put('itemSelected','item0')
When the item is selected from the dropdown I update the Cache with that items value, e.g. item(n).
I then set up two Widgets within a single function itemManagerCard(e, item) that takes the item I want to change as a parameter.
WidgetOne is the base state of the card where the parameters are undefined
if(item === undefined)
WidgetTwo responds when onModeChange(e) returns the target card with the item parameters I want to change
return itemManagerCard(e, item);
if(item != undefined)
I can then populate WidgetTwo with whatever UI data I want to collect through the function's parameters. Such as:
var editItemNumText = 'You are currently editing ${itemNum}'
function itemManagerCard(e, item) {
var selectItemBodyWidget = CardService.newSelectionInput()
.setType(CardService.SelectionInputType.DROPDOWN)
.setTitle('Which item do you want to edit?')
.setFieldName('editItem');
.setOnChangeAction(CardService.newAction().setFunctionName('onModeChange'))
.addItem("Click here to select an existing Item to edit","item0", true)
.addItem("Item 1","item1", false);
//
if (item != undefined)
let itemNum = CacheService.getUserCache().get('itemNum');
var itemText = `You are currently editing ${itemNum}`;
var itemWidget = CardService.newTextParagraph()
.setText(itemText);
}
function onModeChange(e) {
let itemNum = (e.formInput.editItem);
CacheService.getUserCache().put('itemNum', itemNum);
var item = itemArr[itemNum]
return itemManagerCard(e, item);
}
Major drawback is needing to build and maintain the two separate virtually identical Widgets, which can get messy the more complex it gets.

It's possible to export only a portion of the values of a datatable column?

I have a datatable with a button to export its data to a pdf file using pdfmake. So far so good until I received the request to add one more value to one of the columns (and this value should not appear in the exported data). The column I'm having this problem has the following composition:
<td>{{client.lot}}<br><p>{{client.lot.total_negotiations}} client(s) negotiating!</p></td>
(Ok, I know this is not the best way to split all cells of the column into two rows)
What I'm trying to do is to export the data only with the "first row" (before the <br> tag). How can I accomplish this? There's any workaround adjusting the table or the pdf?
You can use the exportOptions.format() function provided by DataTables to do this.
For example, assuming we start with the following table data (where there are 2 cells containing data which needs to be formatted):
Then the resulting PDF will look as follows:
The DataTables configuration for this is:
$(document).ready(function() {
$('#example').DataTable( {
"dom": 'B<"clear">lfrtip',
buttons: [{
extend: 'pdf',
text: 'Save as PDF',
exportOptions: {
modifier: {
page: 'current'
},
format: {
body: function ( data, rowIdx, colIdx ) {
if (colIdx == 1) {
var brIdx = data.indexOf("<br>");
if (brIdx >= 0) {
return data.substring(0, brIdx);
} else {
return data;
}
} else {
return data;
}
}
}
}
}]
} );
} );
This uses a function to inspect the contents of each cell. In my case, I ignore any data which is not in column index 1 (the 2nd column in the table).
For each cell of data in this column, I check for the existence of a <br> tag in the data. If one exists, then all data from this tag to the end of the string is discarded.
All other cells in all other columns are passed through to the PDF unchanged.
You may need to adjust this, depending on your specific needs (e.g. if you need to handle multiple columns, maybe to clean up that trailing hyphen in "Lote 14 -", etc.).
You may want move the export logic to its own separate function, as well, and then call that function from the DataTable config instead (so the logic does not clutter the DataTable configuration code).
Background information on this export function can be found here: exportData - specifically, see the format section on that page. This is the general buttons function used by the exportOptions configuration in the above example.

Kendo Asp.net MVC Grid Batch Mode Calculated Column Display does not update

Using Kendo Asp.net MVC Grid in Ajax Batch Mode.
Having three columns - Qty, Rate, Total. Need to achieve real-time calculation on change. Written following function to update data.
function grid_change(e) {
if (e.action === "itemchange") {
var item = e.items[0];
item.Total = item.Qty * item.Rate;
}
}
But the column does not reflect the calculated value until focus is moved over it. How to update / refresh the cell display as soon as the change event is completed?
Changed the calculation statement (see below) and all the related columns started reflecting changes immediately after the focus was moved out.
function grid_change(e) {
if (e.action === "itemchange") {
var item = e.items[0];
item.set("Total", item.Qty * item.Rate); // Changed to this
}
}
Note: The columns that you are going to update at real-time must be editable.

Getting Current Data from KendoUI TreeView

I'm using a kendo UI tree with a remote data source from a JSON file.
I have a button on the tree page which gets the current data of the tree,sends it through a POST to a server and the server saves the current data to the JSON file so as the next time I reload the page,the changes I made will be kept.That's what I want to happen.
So I know the current data of the tree is in:
$("#treeview").data("kendoTreeView").dataSource.data()
Which means the data changes real time in there for example when someone drag and drops a node of the tree.
My problem starts when this data doesn't seem to change when I drag and drop nodes inside the tree,and only changes when I drag and drop a node on the root level of the tree and even then it doesn't do it correcly as the node should be moved in there as well but instead the node gets copied,leaving the past node in the tree as well...
For Example I have this tree:
If I make a drag and drop change like this:
And I send the data,save it and reload the change isn't made at all!
PS:Even when I view the current data after the change before sending it,I see that there is no change on the data at all even though I did a change visualy with a drag and drop.So it doesn't have to do with the sending,saving and the server.
On the other hand,if I make a change like this:
I can see in the current data that the moved node is added in the end of the data indeed but it is not deleted from it's initial position within the data!So if i send the current data to the server,save it and then refresh I get the result:
The code for viewing and sending the data is:
function sendData() {
var req = createRequest();
var putUrl = "rest/hello/treeData";
req.open("post", putUrl, true);
req.setRequestHeader("Content-type","application/json");
var dsdata = $("#treeview").data("kendoTreeView").dataSource.data();
alert(JSON.stringify(dsdata));
req.send(JSON.stringify(dsdata));
req.onreadystatechange = function() {
if (req.readyState != 4) {
return;
}
if (req.status != 200) {
alert("Error: " + req.status);
return;
}
alert("Sent Data Status: " + req.responseText);
}
}
Is this a Bug or am I doing something wrong?Has anyone been able to see the current data changing correctly on every drag and drop?
First and most important you have to use the latest version of KendoUI (Kendo UI Beta v2012.3.1024) still in beta but is where they have solved many problems.
Then, when you create the kendoTreeView you have to say something like:
tree = $("#treeview").kendoTreeView({
dataSource :kendo.observableHierarchy(data),
dragAndDrop:true
}).data("kendoTreeView");
Here the important is not using directly data array but wrapping it with kendo.observableHierarchy.
Then you will have the data updated with the result of drag & drops.
For me in addition to OnaBai answer I had to use the sync function on the save method. I am using Type Script.
this.treeData = new kendo.data.HierarchicalDataSource({
data: kendo.observableHierarchy([]),//Thanks OnaBai
schema: {
model: {
id: "MenuItemId",
children: "MenuItemChildren",
hasChildren: (e) => {
//this removes arrow next to items that do not have children.
return e.MenuItemChildren && e.MenuItemChildren.length > 0;
}
}
}
});
public save() {
this.treeData.sync().done(() => {
console.log("sync data");
var myType = this.treeData.view();
this.$http.post("/api/TreeViewPicker", myType)
.then((response) => {
});
});
}