Sorting Data in a grid using TFDTable - delphi

I have a TFDTable the is connected to a TGrid using LiveBindings and the data displays sorted by the indexes in the table itself. I want to be able to change the sort order by clicking on the header of the grid. Here is what I have done so far:
void __fastcall TmainFrm::Grid7HeaderClick(TColumn *Column)
{
if(IBS_EntityTable->IndexName == Column->Header)
return; // if it is being sorted by the same column, don't do anything
try
{
TFDIndex* pIndex;
IBS_EntityTable->Indexes->BeginUpdate();
IBS_EntityTable->Indexes->Clear();
pIndex = IBS_EntityTable->Indexes->Add();
pIndex->Name = Column->Header;
pIndex->Fields = Column->Header;
pIndex->Active = true;
IBS_EntityTable->IndexName = pIndex->Name;
}
__finally
{
IBS_EntityTable->Indexes->EndUpdate();
IBS_EntityTable->Refresh();
}
}
but the sort order does not change. In fact the only data that does change is the selected row. What am I doing wrong?
Thank youSam

Related

extJS 6 - filtering store for combobox

I have a store filled on application init.
It is used in multiselect combobox in a view where I select records that I want and add their id's to a variable.
In grid I have a combobox with the same store, and I want to filter out store so it only contains the id's I have selected.
setViewData : function(dataStore, record, readOnly) {
var store = Ext.getStore('ScaleStore');
store.clearFilter();
store.filterBy(function (scaleRecord) {
Ext.each(record.data.scaleList, function (scale) {
if(scale.id == scaleRecord.data.schl1NrId) {
return true;
}
});
});
}
The store contains 5 records.
record.data.scaleList - here I have lets say 3 records out of 5 I have selected in the multiselect combobox.
My goal is to have only the ones I have selected(3 out of 5) displayed in the grid combobox.
With this code I get all of the records, or wrong ones at random.
Any pointers to what I am doing wrong here?
Thank you all :)
It seems you are using Ext.each incorrectly. The documentation on Ext.each states the following:
The iteration can be stopped by returning false from the callback
function. Returning undefined (i.e return;) will only exit the
callback function and proceed with the next iteration of the loop.
Which means you are not returning the values that you want to filter. To do so, and assuming that you still want to use Ext.each, you would have to do the following:
store.filterBy(function (scaleRecord) { // This function is executed
// for each record
var filter = false;
Ext.each(record.data.scaleList, function (scale) {
if(scale.id == scaleRecord.data.schl1NrId) {
filter = true;
return false; // return false if you want to stop the iteration
}
});
return filter; // return the boolean to apply the
// filter to the current record
});

TClientDataSet OnNewRecord vs. AfterInsert

My question is about inserting records into a table on a firebird database. The table is very simple - it has only 2 columns:
CREATE TABLE myTable
(
COL_ID CHAR(36) NOT NULL CONSTRAINT PK_COL_ID PRIMARY KEY USING INDEX IX_COL_ID,
COL_ACRONYM VARCHAR(255)
);
In my application (c++ Builder XE10) I have the following constellation:
A TDataSource connected to
A TClientDataSet connected to
A TDataSetProvider connected to
A TFDTable connected to
A TFDConnection connected to
A Firebird Database
The application does the following:
Insert a new row using TClientDataSet.Append();
Edit the newly inserted record.
Save this record using TClientDataSet.ApplyUpdates(-1);
Everything is working as expected as long as I do the 2nd step manually or by editing the data within the AfterInsert event:
__fastcall TFormMain::TFormMain(TComponent* Owner)
: TForm(Owner)
{
ClientDataSet1->Active = true;
}
UnicodeString TFormMain::GenerateGuid( void )
{
// ...some fancy code creating and returning a GUID...
}
void __fastcall TFormMain::ButtonAppendClick(TObject *Sender)
{
ClientDataSet1->Append();
}
void __fastcall TFormMain::ButtonSaveClick(TObject *Sender)
{
ClientDataSet1->ApplyUpdates(-1);
}
void __fastcall TFormMain::ClientDataSet1AfterInsert(TDataSet *DataSet)
{
DataSet->FieldByName( "COL_ID" )->AsString = GenerateGuid();
DataSet->FieldByName( "COL_ACRONYM" )->AsString = "Whatever: this works!";
}
This works good... so far...
Due to some other changes I decided to move the the auto-creation of data into the OnNewRecord event of the TClientDataSet:
void __fastcall TFormMain::ClientDataSet1NewRecord(TDataSet *DataSet)
{
DataSet->FieldByName( "COL_ID" )->AsString = GenerateGuid();
DataSet->FieldByName( "COL_ACRONYM" )->AsString = "Not too good...";
}
For the first moment it looked good, because the DB-controls on the GUI have been filled with the correct data. But as soon as I hit the Save Button the data disappeard and the new record has not been stored to the database - as if I cancelled the process.
Secondly I noticed, that if I change one of the columns MANUALLY before executing ApplyUpdates(),... then the record is stored.
So I simply added the following line for automatic posting:
void __fastcall TFormMain::ClientDataSet1NewRecord(TDataSet *DataSet)
{
DataSet->FieldByName( "COL_ID" )->AsString = GenerateGuid();
DataSet->FieldByName( "COL_ACRONYM" )->AsString = "Not too good...";
DataSet->Post();
}
This minor change did its job.
My question now is: WHY?
Does AfterInsert automatically post the new record?
Are records that were added by Append() automatically cancelled, when they are left unchanged after the OnNewRecord event?
regards and thanx
Herwig
As far as I know, the difference between TDataSet's OnNewRecord and AfterInsert event handlers is:
Editing fields values from the OnNewRecord will NOT flag the record as modified
Editing fields values from the AfterInsert will flag the record as modified
I guess that this is the cause of the problem

Vaadin Grid Row Index

In a vaadin table if we do
table.setRowHeaderMode(RowHeaderMode.INDEX);
we get a column with the row index.
Is it possible to to the same with a vaadin grid?
So far I haven't seen such an option, but you should be able to fake it with a generated column. Please see below a naive implementation which should get you started (improvements and suggestions are more than welcome):
// our grid with a bean item container
Grid grid = new Grid();
BeanItemContainer<Person> container = new BeanItemContainer<>(Person.class);
// wrap the bean item container so we can generated a fake header column
GeneratedPropertyContainer wrappingContainer = new GeneratedPropertyContainer(container);
wrappingContainer.addGeneratedProperty("rowHeader", new PropertyValueGenerator<Long>() {
private long index = 0;
#Override
public Long getValue(Item item, Object itemId, Object propertyId) {
return index++;
}
#Override
public Class<Long> getType() {
return Long.class;
}
});
// assign the data source to the grid and set desired column order
grid.setContainerDataSource(wrappingContainer);
grid.setColumnOrder("rowHeader", "name", "surname");
// tweak it a bit - definitely needs more tweaking
grid.getColumn("rowHeader").setHeaderCaption("").setHidable(false).setEditable(false).setResizable(false).setWidth(30);
// freeze the fake header column to prevent it from scrolling horizontally
grid.setFrozenColumnCount(1);
// add dummy data
layout.addComponent(grid);
for (int i = 0; i < 20 ; i++) {
container.addBean(new Person("person " + i, "surname " + i));
}
This will generate something similar to the image below:
There is a Grid Renderer that can be used to do this now. It is in the grid renderers add-on https://vaadin.com/directory/component/grid-renderers-collection-for-vaadin7. It is compatible with Vaadin 8 as well.
Here is how it could be used (there are a few different options for how to render the index).
grid.addColumn(value -> "", new RowIndexRenderer()).setCaption("Row index");
Worth to mention that I use the following with Vaadin 18 flow and works perfectly.
grid.addColumn(TemplateRenderer.of("[[index]]")).setHeader("#");
Ok, it took me more than a while to figure this out. I don't know why you need this, but if your purpose is to find which grid row was clicked, then you can get the index from the datasource of your control via the itemClick event of your listener.
In my case, my datasource is an SQLContainer, and I already had it available (see ds var) so I did it this way:
grid.addListener(new ItemClickEvent.ItemClickListener() {
#Override
public void itemClick(ItemClickEvent event) {
Object itemId = event.getItemId();
int indexOfRow = ds.indexOfId(itemId);
}
});
You usually add a datasource to your control when you initialize it, via constructor or by setting the property. If you got you Grid from somewhere with an already-attached datasource, you can always get it with something like this:
SQLContainer ds = (SQLContainer)gred.getContainerDataSource();
I use this trick:
int i = 0;
grid.addComponentColumn(object -> {
i++;
return new Label("" + i);
}).setCaption("");

Get selected columns for a columntoggle table

I have a columntoggle table with some information in it.
I would like to get what are the selected columns, to stock those values in a database and when the user logs back in, using those values to make the table load with the columns he has previously selected.
Is there any way to do this?
Thanks by advance
There is nothing built-in to do this. Here is one way to do it:
jQM creates a column toggle popup with a checkbox for each column assigned a data-priority. The popup takes the id of the table plus '-popup', so you can access the checkboxes with this selector:
$("#myTableid-popup .ui-checkbox label");
Checked items have the class .ui-checkbox-on, while unchecked items have .ui-checkbox-off. Therefore you could get the indexes (indices) of all visible columns:
var selIndex = [];
function SaveSelectedColumns(){
selIndex = [];
$("#myTable-popup .ui-checkbox label").each(function(idx){
if ($(this).hasClass("ui-checkbox-on")){
selIndex.push(idx);
}
});
}
Then to restore visible columns:
function LoadSavedColumns(){
$("#myTable-popup .ui-checkbox label").each(function(idx){
var vis = IsColVisible(idx);
if ($(this).hasClass("ui-checkbox-on") && !vis){
$(this).click();
}
if ($(this).hasClass("ui-checkbox-off") && vis){
$(this).click();
}
});
}
function IsColVisible(idx){
for (var i=0; i<selIndex.length; i++){
if (selIndex[i] == idx) return true;
}
return false;
}
Working DEMO

Saving nested objects with linq to entities

I have a UI that presents a number of checkboxes to the user, and for each one that is checked, I need to create an entry in a mapping table.
LookupTable <- MapTable -> DataTable
I have a custom binder for DataTable, but can't figure out how to get it to create the MapTable objects.
Is this possible? Using asp.net MVC 1.0 and LINQ to Entities.
You need to take care of two steps. (1) Add the newly selected values and (2) remove the unselected values. You'll need a method like this in your LookupTable class.
public void SynchronizeDataTables(IEnumerable<DataTable> dataTables)
{
// get the current data tables. call ToList() to force and enumeration.
// without the ToList(), you'll get a "Sequence Changed during Enumeration"
// error
var currentDataTables = MapTable.Select(m => m.DataTable).ToList();
// if the table is selected, but not in the data store add it.
foreach (var dataTable in dataTables)
{
if (!currentDataTables.Contains(dataTable))
{
MapTables.Add(new MapTable { DataTable = dataTable });
}
}
// if the table is in the data store, but not selected, then remove it.
foreach (var dataTable in currentDataTables)
{
if (!dataTables.Contains(dataTable))
{
MapTables.Remove(dataTable);
}
}
}
Edit: When I did this, I was using LINQ-to-SQL, and I cycled through just the selected ID's, instead of the entire object. This is more complicated because LINQ-to-Entities creates slightly different objects than LINQ-to-SQL, because it doesn't expose the FK identity. Slight modification follows:
public void SynchronizeDataTables(IEnumerable<int> dataTableIds)
{
// get the current data tables. call ToList() to force and enumeration.
// without the ToList(), you'll get a "Sequence Changed during Enumeration"
// error
var currentDataTableIds = MapTable.Select(m => m.DataTable.Id).ToList();
// if the table is selected, but not in the data store add it.
foreach (var dataTableId in dataTableIds)
{
if (!currentDataTableIds.Contains(dataTableId))
{
var dataTable = ???; // some method to fetch data table with ID = dataTableId
MapTables.Add(new MapTable { DataTable = dataTable });
}
}
// if the table is in the data store, but not selected, then remove it.
foreach (var dataTable in currentDataTableIds )
{
if (!dataTableIds.Contains(dataTableId ))
{
var dataTable = ???; // some method to fetch data table with ID = dataTableId
MapTables.Remove(dataTable);
}
}
}

Resources