Programatically updating underlying data in Slickgrid - slickgrid

I have 6 textboxes at the top of the screen that update an entire column(one textbox per column) based on any changes. I was selecting the columns based on their class (.l#). Here is the code (issues to follow):
function UpdateField() {
var ctrl = this;
var id = parseInt(ctrl.id.replace("item", ""), 10) - 1;
var bound = [".l1", ".l7", ".l8", ".l9"];
var fields = $(bound[id]);
for (var i = 0; i < fields.length; i++)
{
fields[i].innerHTML = $(ctrl).val();
}
};
which is bound to the keyup event for the text areas. Issues are:
1) initially fields.length was -1 as I didn't want to put data in the "add new
row" section at the bottom. However, when running it, I noticed the
final "real" record wasn't being populated. Also, when stepping through, I
noticed that the "new row" field was before the "last row" field.
2) when doing it this way, it is purely superficial: if I double click the field,
the real data hasn't been changed.
so in the grand scheme of things, I know that I was doing it wrong. I'm assuming it involves updating the data and then forcing a render, but I'm not certain.

Figured out how to do it. Modified the original code this way:
function UpdateField() {
var ctrl = this;
var id = parseInt(ctrl.id.replace("item", ""), 10) - 1;
var bound = ['title1', 'title2', 'title3', 'title4'];
var field = bound[id];
for (var i = 0; i < dataView.getLength(); i++)
{
var item = dataView.getItem(i);
item[field] = $(ctrl).val();
dataView.updateItem(i, item);
}
grid.invalidate();
};
I have 6 textboxes (item1-item6) that "bind" to fields in the sense that if I change data in a textbox, it updates all of the rows and any new rows added also have this data.
Parts where the two issues can be explained this way:
1) to work around that, though still it would be a presentational fix and not a real updating of the underlying data, one could force it to ignore if it had the active class attached. Extra work, and not in the "real" direction one is going for (masking the field).
2) It was pretty obvious with the original implementation (though it was all I could figure out via Chrome Dev Tools that I could modify at the time) that it was merely updating a div's content and not actually interacting with the data underneath. Would look nice, and perhaps one could just pull data from the item1-item6 boxes in place of the column if it is submitted, but if someone attempts to modify the cell, they'll be looking at the real data again.

Related

Google Sheets - QUERY, adding a cell to hide a row

I'm hoping to use a Google Form to have my students sign up for activities. I added a column to the right of the form data where I can put an "x" once I have met with them. I have a separate tab called "Ordered" where I use QUERY to sort and show only the entries without an x. Once I meet with a student, I can put an x on the original data tab, and the entry will hide from the Ordered tab (kind of a queue for my students).
I would love to not have to switch between the 2 tabs every time I have to check someone off. Is there a way to add a similar column on the Ordered tab that will hide the finished entry?
Here is the sheet I'm referring to:
https://docs.google.com/spreadsheets/d/1fQHF0EoGLk5NEI6GvyRk4InBl-uEq0jq7qqmyOmFjy8/edit#gid=379798836
I'm sharing this sheet with other teachers who aren't familiar with spreadsheets, so the solution has to be ludite friendly.
As #Aerials said, you are likely to run into circular dependency issues here. Because of this, I'd suggest using Apps Script and get rid of the QUERY formula. You want to do the following:
Every time someone submits the form, the Ordered sheet gets updated with the new submission data.
Every time an x is added to the Form Responses 1 sheet, the corresponding row in Ordered gets removed.
Every time an x is added to the Ordered sheet, the corresponding row in Ordered gets removed, and an x gets added to the corresponding row in Form Responses 1.
A possible way to go would be something along the following lines (open the script bound to your spreadsheet by clicking Tools > Script editor):
Install an onFormSubmit trigger so that Ordered gets updated with new data every time the form is submitted. The trigger can be installed manually or programmatically, copying this function to your script and running it once:
function onFormSubmit(e) {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger("copyAndFilterData")
.forSpreadsheet(ss)
.onFormSubmit()
.create();
}
Once the trigger is installed, the function copyAndFilterData will execute every time the form is submitted. This function should updated Ordered based on the data in Form Responses 1, removing all the submission that have been marked as complete x. This function could be the following:
function copyAndFilterData() {
var ss = SpreadsheetApp.getActive();
var sheet1 = ss.getSheetByName("Form Responses 1");
var sheet2 = ss.getSheetByName("Ordered");
var sourceData = sheet1.getDataRange().getValues();
var filteredData = sourceData.filter(sourceRow => sourceRow[5] !== "x")
.sort((a, b) => a - b)
.map(sourceRow => {
sourceRow.pop();
sourceRow.splice(1, 1);
return sourceRow;
});
sheet2.getRange(1, 1, sheet2.getLastRow(), 4).clear({contentsOnly: true});
var destRange = sheet2.getRange(1, 1, filteredData.length, filteredData[0].length)
destRange.setValues(filteredData);
}
At this point, another piece of functionality would be needed: update the spreadsheet based on the addition or removal of x on any of both sheets. For this, an onEdit trigger would be needed, like this:
function onEdit(e) {
var editedSheet = e.range.getSheet();
if (editedSheet.getName() === "Form Responses 1") copyAndFilterData();
else if (editedSheet.getName() === "Ordered") removeRows(e, editedSheet);
}
Once this function is copied and the project saved, every time Form Responses 1 is edited, the Ordered sheets gets updated (function copyAndFilterData), and every time Ordered is edited, the function removeRows is fired. This function should do two things: (1) remove the row which was marked as complete in Ordered (x added) and (2) add the corresponding x to Form Responses 1. The submission dates can be compared to identify the row:
function removeRows(e, editedSheet) {
var range = e.range;
var column = range.getColumn();
var row = range.getRow();
var value = range.getValue();
if (column == 5 && row > 1 && value == "x") {
var date = editedSheet.getRange(row, 1).getValue();
var formSheet = e.source.getSheetByName("Form Responses 1")
var values = formSheet.getRange(2, 1, formSheet.getLastRow() - 1).getValues();
var markedRow = values.findIndex(value => value[0].getTime() === date.getTime());
formSheet.getRange(markedRow + 2, 6).setValue("x");
editedSheet.deleteRow(row);
}
}
Reference:
Overview of Google Apps Script
Simple Triggers
Installable Triggers

kendoDropDownList loading bar not showing

I am using a dropDownList to show some values in order to filter results on a grid.
I have use the 'change' event to see when the grid must be updated
$('#view-mode-selector').kendoDropDownList({
change: onMChange
});
in the method onMChange I am trying to show the progress bar with
kendo.ui.progress($("#grid), true);
and at the end of the method when all is done I have
kendo.ui.progress($("#grid), false);
But the data loads and I do not see the progress bar.
If I remove the last statement (the 'false' one) I can see the progress bar, but it never disappears.
If I debug, It appears and disappears when the data is ready.
It is not an issue of the data loading too fast, sometimes the data takes 5 seconds to be ready.
The data is already in the browser, I am just showing or hiding columns.
Kendo version v2016.2.714
Thanks
EDIT
on onMChange I have some ifs where I populate an array (columnsToShow) with the name of the columns I want to show (everything else will be hidden) then I call a function with this code:
showHideColumn: function(columnsToShow) {
var grid = $(this.gridId).data("kendoGrid");
var show = false;
for (var i = 0; i < grid.columns.length; i++) {
show = false;
if (columnsToShow.length > 0) {
for (var j = 0; j < columnsToShow.length; j++) {
if (columnsToShow[j] == grid.columns[i].field) {
grid.showColumn(i);
columnsToShow.splice(j, 1);
show = true;
break;
}
}
}
if (!show) {
grid.hideColumn(i);
}
}
}
That code seems to be very inefficient at hiding/showing columns, all activity stops for few seconds when I want to show all columns (after having hidden some), I have around 30 columns and 30 rows.
Your question is a little misleading. Is the #grid element you are having difficulty displaying the loading icon for a grid control (as per the name of the element) or a drop down control (as per the title of the question)?
If grid
In order to show the loading icon I used:
kendo.ui.progress($("#grid").data("kendoGrid").element, true);
And to hide:
kendo.ui.progress($("#grid").data("kendoGrid").element, false);
If drop down
I haven't had to force display loading icons for dropdowns however there is an example here, noting the comment specifying:
The element must have a position:relative style applied
Additionally further down, it is noted that the element cannot have child elements.

How to select a row in kendo grid by data item ID?

I need to select a specific row in kendoGrid but NOT by data-uid (as data-uid is changed when the grid dataSource is loaded again) but by the row itemID. I saw posts but they only select the row by uid which is not what I needed, I actually need to restart the HTML5 application and when grid is loaded, a specific item should be selected. This is what I've been seeing
Demo: jsfiddle.net/rusev/qvKRk/3/
e.g. the object has OrderID as ID, and every time the grid is loaded, it will be the same, unlike uid, I want to know how will I be able to select a row with OrderID, instead of uid.
You cam mix row itemID and data.uid, I guess.
var grid = $("#Grid").data("kendoGrid");
var dataItem = $("#Grid").data("kendoGrid").dataSource.get(itemID);
var row = $("#Grid").data("kendoGrid").tbody.find("tr[data-uid='" + dataItem.uid + "']");
Going along with what umais has mentioned, the better approach, since there is no built in functionality for this as of yet, would be to iterate through all the records to find the one you need. The function that I built will work even if there are pages of data. The only other way that I can think of doing this would be do do a secondary ajax call; But this works well. Note that i haven't tested it with more than 2000 records.
var dataGrid = $("#GATIPS").data("kendoGrid").dataSource;
var numOfRows = dataGrid.total();
var currentPageSize = dataGrid.pageSize();
dataGrid.pageSize(numOfRows);
var dataGridData = dataGrid.data();
for (var i = 0; i < numOfRows; i++) {
if (dataGridData[i].uid == e)
return dataGridData[i];
}
dataGrid.pageSize(currentPageSize); // reset the view
e is the UID. However this can be substituted for what ever variable you need just replace the check.
a work around that I managed to have, was to go through all rows and check which row model has that ID equal to the parameter, and then get that row data-uid and select the item through data-uid. It's working fine for me, since there were no suggestion, it's the better answer for now.
Well, accordingly to what I have done (and worked for me), and even though the work around isn't the prettiest, set one more Column, with your model id and with ClientTemplate then create any html object (div in my case) inside it give it a html id of your id, so when ever you need it, you just have to go and look with something like:
grid.dataItem($("td div#id").closest("tr"));
Because remember that the dataItem method is waiting for a selector then you get your selectedItem as regular one.
EDIT:
I forgot to say, that you should (or could) use the style property
display:none
If you don't want to display that col.

ActiveReports as a convert to pdf machine

The company I'm with is likely to obtain an ActiveReports 7 license. There's a new project requirement that several webgrids (not actually webgrids, but more like html rendered with zurb) need to be converted into pdfs. At one point in the code behind they're effectively datasets or can be created into such. Is there a way to shuttle the data from the datasets into active reports, then render it out as a PDF. I'd like to keep the report as generic as possible, and thus have one active report for all the datatables, so doing using active reports as its usually done is kind of out of the question.
The only thing I can think of at the moment is a single textbox in the group header into which I could concatenate all the headers, and a single textbox in the details into which I could throw all the data for each row. The problem here is that I'd run into many formatting issues as nothing would line up properly - as tab delimiting would solve nothing here. I could have multiple textboxes with various spacing, but then it would eventually devolve into a different report for each dataset. Is it possible to apply some sort of markup so that I could keep the spacing of columns as I feed the data in. Do active reports richtextboxes honor html markup? Or is there another solution altogether?
I'd use Itextsharp, but its not free for commercial products.
Thanks,
Sam
You can dynamically build a report that will output a simple table based on a specified DataSet, well actually a System.Data.DataTable. Basically for each column in the DataTable, add a textbox to the header to hold the name of the column and add another textbox to the Detail section to hold the value.
For the textbox in the detail section set its DataField property to the name of the column. With the binding in place, you can set the report's DataSource property to the DataTable and then run the report and export it to PDF.
The following code is a basic example:
var left = 0f;
var width = 1f;
var height = .25f;
var space = .25f;
var rpt = new SectionReport();
rpt.Sections.Add(SectionType.ReportHeader, "rh").Height = height;
rpt.Sections.Add(SectionType.Detail, "detail").Height = height;
rpt.Sections.Add(SectionType.ReportFooter, "rf").Height = height;
foreach (System.Data.DataColumn col in dataTable.Columns)
{
var txt = new TextBox { Location = new PointF(left, 0), Size = new SizeF(width, height) };
txt.Text = col.ColumnName;
rpt.Sections["rh"].Controls.Add(txt);
txt = new TextBox { Location = new PointF(left, 0), Size = new SizeF(width, height) };
txt.DataField = col.ColumnName;
rpt.Sections["detail"].Controls.Add(txt);
left += width + space;
}
rpt.DataSource = dataTable;
rpt.Run();
var pdf = new PdfExport();
pdf.Export(rpt.Document, #"c:\Users\scott\downloads\test.pdf");

Restyling dynamically styled SlickGrid cells after Sort

Ok, let me explain my scenario more clearly:
When a cell is edited, it becomes 'dirty' and I style it a certain way by adding a CSS class to the cell via javascript.
Then, if the user Sorts the grid, the styling is lost (I believe because all the rows are recreated) and I need a way to restore the styling to the appropriate cell/row after a Sort.
What I attempted to do is add an entry into data[] called 'status' and onCellChange I loop through data[] and match the args.item.Id to appropriate entry in data[].
grid.onCellChange.subscribe(function (e, args) {
var done = false;
for (var i = 0; i < data.length && !done; i++) {
if (data[i].id == args.item.id) {
data[i].status = "dirty";
done = true;
}
}
}
However, onSort I'm not sure how to match the sorted rows to the data array. (Since I have no args.item) I've attempted to do selector statements:
$(".slick-row")
to restyle the correct cells, but I have no way to associate the rows with their entry in data[].
1) There is no need to search for the item in your onCellChange handler - it is available at "args.item".
2) Sorting the "data" array will not wipe out your change to the item in #1.
3) You mention dynamically styling cells. I see no code for that. If your custom formatter is the piece of code that looks at "item.status" and renders it differently if it is dirty, then you don't have to do anything extra. Sorting the data and telling the grid to re-render will preserve the "dirty" cell styles.

Resources