Ag-Grid - Pasting Excel Data into Grid - Appending Rows - ag-grid

When pasting clipboard/Excel data into AG-Grid, how do I get the data to append to the current rows?
If my table currently has a single row and I'm trying to paste 10 rows into the table, Ag-Grid only overwrites the single row instead of appending the extra 9 rows. Am I missing a gridOption or is this not possible?

To get what you need, follow these steps:
1) Add a paste event listener:
mounted () {
window.addEventListener('paste', this.insertNewRowsBeforePaste);
}
2) Create the function that retrieves the data from the clipboard and creates new lines in the grid:
insertNewRowsBeforePaste(event){
var self = this;
// gets data from clipboard and converts it to an array (1 array element for each line)
var clipboardData = event.clipboardData || window.clipboardData;
var pastedData = clipboardData.getData('Text');
var dataArray = self.dataToArray(pastedData);
// First row is already in the grid and dataToArray returns an empty row at the end of array (maybe you want to validate that it is actually empty)
for (var i = 1; i < dataArray.length-1; i++) {
self.addEmptyRow(i);
}
}
3) dataToArray is a function that ag-Grid uses to paste new lines and I just needed to adjust the "delimiter" variable. I copied it from the clipboardService.js file.
// From http://stackoverflow.com/questions/1293147/javascript-code-to-parse-csv-data
// This will parse a delimited string into an array of
// arrays. The default delimiter is the comma, but this
// can be overriden in the second argument.
export var dataToArray = function(strData) {
var delimiter = self.gridOptions.api.gridOptionsWrapper.getClipboardDeliminator();;
// Create a regular expression to parse the CSV values.
var objPattern = new RegExp((
// Delimiters.
"(\\" + delimiter + "|\\r?\\n|\\r|^)" +
// Quoted fields.
"(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
// Standard fields.
"([^\"\\" + delimiter + "\\r\\n]*))"), "gi");
// Create an array to hold our data. Give the array
// a default empty first row.
var arrData = [[]];
// Create an array to hold our individual pattern
// matching groups.
var arrMatches = null;
// Keep looping over the regular expression matches
// until we can no longer find a match.
while (arrMatches = objPattern.exec(strData)) {
// Get the delimiter that was found.
var strMatchedDelimiter = arrMatches[1];
// Check to see if the given delimiter has a length
// (is not the start of string) and if it matches
// field delimiter. If id does not, then we know
// that this delimiter is a row delimiter.
if (strMatchedDelimiter.length &&
strMatchedDelimiter !== delimiter) {
// Since we have reached a new row of data,
// add an empty row to our data array.
arrData.push([]);
}
var strMatchedValue = void 0;
// Now that we have our delimiter out of the way,
// let's check to see which kind of value we
// captured (quoted or unquoted).
if (arrMatches[2]) {
// We found a quoted value. When we capture
// this value, unescape any double quotes.
strMatchedValue = arrMatches[2].replace(new RegExp("\"\"", "g"), "\"");
}
else {
// We found a non-quoted value.
strMatchedValue = arrMatches[3];
}
// Now that we have our value string, let's add
// it to the data array.
arrData[arrData.length - 1].push(strMatchedValue);
}
// Return the parsed data.
return arrData;
}
4) Finally, to add new blank lines in the grid, use the function below:
addEmptyRow(rowIndex) {
var newItem = {};
this.gridOptions.api.updateRowData({add: [newItem], addIndex: rowIndex});
}
Basically what this code does is insert blank rows at the beginning of the grid and let ag-Grid paste the data into those rows. For it to work, the line where the code is pasted must be the first line in the grid. It's using the updateRowData from ag-grid (https://www.ag-grid.com/javascript-grid-data-update/).
You may need to make some adjustments if you need something else.

I have the same question. I originally used a paste event listener to add a number of rows to the grid, based on the difference between available space and clipboard data length. But now the grid will only add the rows and not complete the paste.

Related

Copied value disappears when row that contained source value is deleted in Google spreadsheets

I wrote this script that is used as a trigger onEdit in a sheet. The idea is to pick a value from a worksheet, copy it into another worksheet based on some logic, and then delete the source row that contained the original value.
When run, often times, the copy will take place, but on delete, the copied value will disappear. One way I noticed fixes the problem is if I delete the trigger, save, and create it again...
How can I avoid this behavior?
function onEdit(e) {
var range = e.range;
var entry = range.getSheet();
var sss = entry.getParent();
if (sss.getName() != "Weight Tracker")
return;
if (entry.getName() != "Entry")
return;
Logger.log("CopyData is running...."+range.getCell(1,2).getValue());
var weight = range.getCell(1,2).getValue();
Logger.log("weight = "+weight);
var details = sss.getSheetByName('Details');
var trange = details.getRange(3, 1, 200);
var data = trange.getValues();
var today = new Date().setHours(0,0,0,0);
for(var n=0;n<data.length;n++) {
var date = new Date(data[n]).setHours(0,0,0,0);
Logger.log("date = "+date+" =? "+today);
if(date == today) {
break
};
}
Logger.log("n = "+n+" today: "+today);
// n is 0 based, sheet is 1 based + 2 headers = 3, 5 is Jim's weight
details.getRange(n+3,5).setValue(weight);
// get rid of the row so next addition arrives to the top row
Logger.log("deleting row...");
// for some reason deleting the road removes the value entered...
range.getSheet().deleteRow(1);
}

Sort/Order an Undetermined Number of Columns (LINQ\Entity Framework)

Need to sort/order a list of data based on an undetermined number of columns (1 or more).
What i'm trying to do is loop through the desired columns and add an OrderBy or ThenBy based on their number to the query'd list, but i'm unsuccessful...
Done this, but it doesn't compile:
var query = GetAllItems(); //returns a IQueriable list of items
//for each selected column
for (int i = 0; i < param.Columns.Length; i++)
{
if (i == 0)
{
query = query.OrderBy(x => x.GetType().GetProperty(param.Columns[i].Name));
}
else
{
//ERROR: IQueriable does not contain a definition for "ThenBy" and no extension method "ThenBy"...
query = query.ThenBy(x => x.GetType().GetProperty(param.Columns[i].Data));
}
}
How can i resolve this issue? Or any alternative to accomplish this requirement?
SOLUTION: #Dave-Kidder's solution is well thought and resolves the compile errors i had. Just one problem, OrderBy only executes (actually sorts the results) after a ToList() cast. This is an issue because i can't convert a ToList back to an IOrderedQueryable.
So, after some research i came across a solution that resolve all my issues.
Microsoft assembly for the .Net 4.0 Dynamic language functionality: https://github.com/kahanu/System.Linq.Dynamic
using System.Linq.Dynamic; //need to install this package
Updated Code:
var query = GetAllItems(); //returns a IQueriable list of items
List<string> orderByColumnList = new List<string>(); //list of columns to sort
for (int i = 0; i < param.Columns.Length; i++)
{
string column = param.Columns[i].Name;
string direction = param.Columns[i].Dir;
//ex.: "columnA ASC"
string orderByColumn = column + " " + direction;
//add column to list
orderByColumnList.Add(orderBy);
}
//convert list to comma delimited string
string orderBy = String.Join(",", orderByColumnList.ToArray());
//sort by all columns, yay! :-D
query.OrderBy(orderBy).ToList();
The problem is that ThenBy is not defined on IQueryable, but on the IOrderedQueryable interface (which is what IQueryable.OrderBy returns). So you need to define a new variable for the IOrderedQueryable in order to do subsequent ThenBy calls. I changed the original code a bit to use System.Data.DataTable (to get a similar structure to your "param" object). The code also assumes that there is at least one column in the DataTable.
// using System.Data.DataTable to provide similar object structure as OP
DataTable param = new DataTable();
IQueryable<DataTable> query = new List<DataTable>().AsQueryable();
// OrderBy returns IOrderedQueryable<TSource>, which is the interface that defines
// "ThenBy" so we need to assign it to a different variable if we wish to make subsequent
// calls to ThenBy
var orderedQuery = query.OrderBy(x => x.GetType().GetProperty(param.Columns[0].ColumnName));
//for each other selected column
for (int i = 1; i < param.Columns.Count; i++)
{
orderedQuery = orderedQuery.ThenBy(x => x.GetType().GetProperty(param.Columns[i].ColumnName));
}
you should write ThenBy after OrderBy like this:
query = query
.OrderBy(t=> // your condition)
.ThenBy(t=> // next condition);

Parsing tab delimited file detect if first row value it empty/tab

Hi all I have a to parse some files to load into a DataSet and I ran into an issue where the first row value is sometimes blank so when I parse the data the rows added to the columns are off because there is no value for the row[RouteCode].
Example Data
Columns are in the first line(Tab delimited) DataRows are in the following rows(Tab delimited)
RouteCode City EmailAddress FirstName
NULL MyCity My-Email MyFirstName
What I am seeing is all the Columns are added fine but each row added the first tab value is not detected so it shifts the columns(hope I am making sense) so in this case the city data is sitting in the RouteCode column and the last column somehow is getting the first row value (tab).
class TextToDataSet
{
public TextToDataSet()
{ }
/// <summary>
/// Converts a given delimited file into a dataset.
/// Assumes that the first line
/// of the text file contains the column names.
/// </summary>
/// <param name="File">The name of the file to open</param>
/// <param name="TableName">The name of the
/// Table to be made within the DataSet returned</param>
/// <param name="delimiter">The string to delimit by</param>
/// <returns></returns>
public static DataSet Convert(string File,
string TableName, string delimiter)
{
//The DataSet to Return
DataSet result = new DataSet();
//Open the file in a stream reader.
using (StreamReader s = new StreamReader(File))
{
//Split the first line into the columns
string[] columns = s.ReadLine().Split(delimiter.ToCharArray());
//Add the new DataTable to the RecordSet
result.Tables.Add(TableName);
//Cycle the colums, adding those that don't exist yet
//and sequencing the one that do.
foreach (string col in columns)
{
bool added = false;
string next = "";
int i = 0;
while (!added)
{
//Build the column name and remove any unwanted characters.
string columnname = col + next;
columnname = columnname.Replace("#", "");
columnname = columnname.Replace("'", "");
columnname = columnname.Replace("&", "");
//See if the column already exists
if (!result.Tables[TableName].Columns.Contains(columnname))
{
//if it doesn't then we add it here and mark it as added
result.Tables[TableName].Columns.Add(columnname);
added = true;
}
else
{
//if it did exist then we increment the sequencer and try again.
i++;
next = "_" + i;
}
}
}
//Read the rest of the data in the file.
string AllData = s.ReadToEnd();
//Split off each row at the Carriage Return/Line Feed
//Default line ending in most windows exports.
//You may have to edit this to match your particular file.
//This will work for Excel, Access, etc. default exports.
string[] rows = AllData.Split("\n".ToCharArray());
//Now add each row to the DataSet
foreach (string r in rows)
{
//Split the row at the delimiter.
string[] items = r.Split(delimiter.ToCharArray());
//Add the item
result.Tables[TableName].Rows.Add(items);
}
}
//Return the imported data.
return result;
}
}
}
If there aren't supposed to be any missing entries anywhere in the file (i.e there should always be something between the tabs) then you could use:
string[] columns = s.ReadLine().Split(delimiter.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
and then check that columns is not an empty array. If it is then read the next line and carry on processing:
while (columns.Length == 0)
{
// Row is empty so read the next line out of the file
columns = s.ReadLine().Split(delimiter.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
}
This will ensure that your data always starts with a filled row. However, it will break down if there is ever an empty entry further down the list.
If there could be empty entries then you'll probably have to check for all columns being empty:
while (columns.All(c => string.IsNullOrEmpty(c)))
{
// Row is empty so read the next line out of the file
columns = s.ReadLine().Split(delimiter.ToCharArray());
}

how to seek position in datatable before writing to the file

I have the data table that is read from csv file. Then it is iterated through row and columns, and each value is appended before writing to the file that is also destination csv file. I want to separate the data of one column upon the special character ("/"), into two columns. For example, the column of 'Type' of data table is "women/shoes and handbags/guess". I have another column 'SubType', so I want to separate one column to two columns in data table before writing. I just want to ignore third type that is guess. Is there a way to seek position "/" and after second "/", I want to insert that value into another column of data table that is 'SubType'.
foreach (DataRow dRow in dtSor.Rows)
{
for (int i = 0; i < dtSor.Columns.Count; i++)
{
if (dRow[i].ToString().Contains(","))
{
dest_csv.Append("\"" + dRow[i].ToString() + "\"" + ",");
}
else if (dRow[i].ToString() == "")
{
dest_csv.Append("NULL" + ",");
}
else
{
dest_csv.Append(dRow[i].ToString() + ",");
//dest_csv.Append(dRow[i].ToString());
}
}
dest_csv.Remove(dest_csv.Length - 1, 1);
dest_csv.Append(Environment.NewLine);
}
File.WriteAllText(destination_file, dest_csv.ToString(), Encoding.Default);
}
First check if the special char is in the field with indexOf, then split the field on the special char in an araay and take only the parts you're intereseted in.
like so:
const char special = '\\';
string maintype ="";
string subtype ="";
string field = dRow[i].ToString();
if (field.IndexOf(special)>-1)
{
string[] splitted = field.Split(special);
maintype = splitted[0];
subtype = splitted[1];
}

Word/Office Automation - How to retrieve selected value from a Drop-down form field

I am trying to retrieve the value of all fields in a word document via office automation using c#. The code is shown below however if the field is a drop-down then the value of the range text is always empty even though I know it is populated. If it is a simple text field then I can see the range text. How do I get the selected drop down item? I feel there must be something quite simple that I'm doing wrong...
private void OpenWordDoc(string filename) {
Microsoft.Office.Interop.Word.Application app = new Microsoft.Office.Interop.Word.Application();
Document doc = app.Documents.Open(filename, ReadOnly: true, Visible: false);
foreach (Field f in doc.Fields) {
string bookmarkName = "??";
if (f.Code.Bookmarks.Count > 0) {
bookmarkName = f.Code.Bookmarks[1].Name; // have to start at 1 because it is vb style!
}
Debug.WriteLine(bookmarkName);
Debug.WriteLine(f.Result.Text); // This is empty when it is a drop down field
}
doc.Close();
app.Quit();
}
Aha - If I scan through FormFields instead of Fields then all is good...
foreach (FormField f in doc.FormFields) {
string bookmarkName = "??";
if (ff.Range.Bookmarks.Count > 0) {
bookmarkName = ff.Range.Bookmarks[1].Name; // have to start at 1 because it is vb style!
}
Debug.WriteLine(bookmarkName);
Debug.WriteLine(ff.Result); // This is empty when it is a drop down field
}
Problem solved. Phew.