Foundset property type
Purpose of this property type
This page is written mostly for NG web component creators, not for Servoy developers that just want to use web components. You might want to view general Servoy Foundset documentation instead.
The 'foundset' property type can be used by web components to access/change a foundset's data/state directly from the browser.
The foundset typed property in the browser will work based on a 'viewport' of the server's foundset. The viewport is controlled directly by the component's code. Server will adjust foundset viewport bounds/contents only when needed due to data changes, deletes, inserts...
The foundset property also gives the possibility of knowing/changing the selection of the foundset.
For advanced uses, the foundset property can be linked to/interact with other property types (dataprovider, tagstring, component, ...), so that those other properties will provide a viewport as well - representing the same rows/records as in the foundset's viewport. The properties that support foundset view of data will allow the web component to specify a "forFoundset: "[foundsetPropertyName]" in their own property's description in the .spec file.
For foundset property types Servoy Developer allows (in properties view) one of the following:
a (parent) form's foundset
a related foundset
a separate foundset (of any table; similar to JSDatabaseManager.getFoundset()). When this option is chosen the user can also choose whether or not the separate foundset should load all records initially. (if not checked, contents can be loaded at any time from scripting)
"- none -" which means that you are going to set that foundset at runtime through scripting.
Foundset property value in browser scripting
In browser js, a foundset property value has the following content:
Browser side provided property content in model
export interface IFoundsetFieldsOnly {
/**
* An identifier that allows you to use this foundset via the 'foundsetRef' and
* 'record' types.
*
* 'record' and 'foundsetRef' .spec types use it to be able to send RowValue
* and FoundsetValue instances as record/foundset references on server (so
* if an argument or property is typed as one of those in .spec file).
*
* In reverse, if a 'foundsetRef' type sends a foundset from server to client
* (for example as a return value of callServerSideApi) it will translate to
* this identifier on client (so you can use it to find the actual foundset
* property in the model, if server side script put it in the model as well).
*/
foundsetId: number;
/**
* the size of the foundset on server (so not necessarily the total record count
* in case of large DB tables)
*/
serverSize: number;
/**
* this is the data you need to have loaded on client (just request what you need via provided
* loadRecordsAsync or loadExtraRecordsAsync)
*/
viewPort: ViewPort;
/**
* array of selected records in foundset; indexes can be out of current
* viewPort as well
*/
selectedRowIndexes: number[];
/**
* sort string of the foundset, the same as the one used in scripting for
* foundset.sort and foundset.getCurrentSort. Example: 'orderid asc'.
*/
sortColumns: string;
/**
* the multiselect mode of the server's foundset; if this is false,
* selectedRowIndexes can only have one item in it
*/
multiSelect: boolean;
/**
* the findMode state of the server's foundset
*/
findMode: boolean;
/**
* if the foundset is large and on server-side only part of it is loaded (so
* there are records in the foundset beyond 'serverSize') this is set to true;
* in this way you know you can load records even after 'serverSize' (requesting
* viewport to load records at index serverSize-1 or greater will load more
* records in the foundset)
*/
hasMoreRows: boolean;
/**
* columnFormats is only present if you specify
* "provideColumnFormats": true inside the .spec file for this foundset property;
* it gives the default column formatting that Servoy would normally use for
* each column of the viewport - which you can then also use in the
* browser yourself; keys are the dataprovider names and values are objects that contain
* the format contents
*/
columnFormats?: Record<string, any>;
}
/**
* Interface for client side values of 'foundset' typed properties in .spec files.
*/
export interface IFoundset extends IFoundsetFieldsOnly {
/**
* Request a change of viewport bounds from the server; the requested data will be loaded
* asynchronously in 'viewPort'.
*
* @param startIndex the index that you request the first record in "viewPort.rows" to have in
* the real foundset (so the beginning of the viewPort).
* @param size the number of records to load in viewPort.
*
* @return a promise that will get resolved when the requested records arrived browser-
* side. As with any promise you can register success, error callbacks, finally, ...
* See JSDoc of RequestInfoPromise.requestInfo and FoundsetChangeEvent.requestInfos
* for more information about determining if a listener event was caused by this call.
*/
loadRecordsAsync(startIndex: number, size: number): RequestInfoPromise<any>;
/**
* Request more records for your viewPort; if the argument is positive more records will be
* loaded at the end of the 'viewPort', when negative more records will be loaded at the beginning
* of the 'viewPort' - asynchronously.
*
* @param negativeOrPositiveCount the number of records to extend the viewPort.rows with before or
* after the currently loaded records.
* @param dontNotifyYet if you set this to true, then the load request will not be sent to server
* right away. So you can queue multiple loadLess/loadExtra before sending them
* to server. If false/undefined it will send this (and any previously queued
* request) to server. See also notifyChanged(). See also notifyChanged().
*
* @return a promise that will get resolved when the requested records arrived browser-
* side. As with any promise you can register success, error callbacks, finally, ...
* That allows custom component to make sure that loadExtra/loadLess calls from
* client do not stack on not yet updated viewports to result in wrong bounds.
* See JSDoc of RequestInfoPromise.requestInfo and FoundsetChangeEvent.requestInfos
* for more information about determining if a listener event was caused by this call.
*/
loadExtraRecordsAsync(negativeOrPositiveCount: number, dontNotifyYet?: boolean): RequestInfoPromise<any>;
/**
* Request a shrink of the viewport; if the argument is positive the beginning of the viewport will
* shrink, when it is negative then the end of the viewport will shrink - asynchronously.
*
* @param negativeOrPositiveCount the number of records to shrink the viewPort.rows by before or
* after the currently loaded records.
* @param dontNotifyYet if you set this to true, then the load request will not be sent to server
* right away. So you can queue multiple loadLess/loadExtra before sending them
* to server. If false/undefined it will send this (and any previously queued
* request) to server. See also notifyChanged(). See also notifyChanged().
*
* @return a promise that will get resolved when the requested records arrived browser
* -side. As with any promise you can register success, error callbacks, finally, ...
* That allows custom component to make sure that loadExtra/loadLess calls from
* client do not stack on not yet updated viewports to result in wrong bounds.
* See JSDoc of RequestInfoPromise.requestInfo and FoundsetChangeEvent.requestInfos
* for more information about determining if a listener event was caused by this call.
*/
loadLessRecordsAsync(negativeOrPositiveCount: number, dontNotifyYet?: boolean): RequestInfoPromise<any>;
/**
* If you queue multiple loadExtraRecordsAsync and loadLessRecordsAsync by using dontNotifyYet = true
* then you can - in the end - send all these requests to server (if any are queued) by calling
* this method. If no requests are queued, calling this method will have no effect.
*/
notifyChanged(): void;
/**
* Sort the foundset by the dataproviders/columns identified by sortColumns.
*
* The name property of each sortColumn can be filled with the dataprovider name the foundset provides
* or specifies. If the foundset is used with a component type (like in table-view) then the name is
* the name of the component on who's first dataprovider property the sort should happen. If the
* foundset is used with another foundset-linked property type (dataprovider/tagstring linked to
* foundsets) then the name you should give in the sortColumn is that property's 'idForFoundset' value
* (for example a record 'dataprovider' property linked to the foundset will be an array of values
* representing the viewport, but it will also have a 'idForFoundset' prop. that can be used for
* sorting in this call; this 'idForFoundset' was added in version 8.0.3).
*
* @param sortColumns an array of JSONObjects { name : dataprovider_id,
* direction : sortDirection }, where the sortDirection can be "asc" or "desc".
* @return a promise that will get resolved when the new sort
* will arrive browser-side. As with any promise you can register success, error
* and finally callbacks.
* See JSDoc of RequestInfoPromise.requestInfo and FoundsetChangeEvent.requestInfos
* for more information about determining if a listener event was caused by this call.
*/
sort(sortColumns: Array<{ name: string; direction: ('asc' | 'desc') }>): RequestInfoPromise<any>;
/**
* Request a selection change of the selected row indexes. Returns a promise that is resolved
* when the client receives the updated selection from the server. If successful, the array
* selectedRowIndexes will also be updated. If the server does not allow the selection change,
* the reject function will get called with the 'old' selection as parameter.
*
* If requestSelectionUpdate is called a second time, before the first call is resolved, the
* first call will be rejected and the caller will receive the string 'canceled' as the value
* for the parameter serverRows.
* E.g.: foundset.requestSelectionUpdate([2,3,4]).then(function(serverRows){},function(serverRows){});
*
* @return a promise that will get resolved when the requested selection update arrived back browser-
* side. As with any promise you can register success, error callbacks, finally, ...
* See JSDoc of RequestInfoPromise.requestInfo and FoundsetChangeEvent.requestInfos
* for more information about determining if a listener event was caused by this call.
*/
requestSelectionUpdate(selectedRowIdxs: number[]): RequestInfoPromise<any>;
/**
* It will send a data update for a cell (a column in a row) in the foundset to the server.
* Please make sure to adjust the viewport value as well not just call this method.
*
* This method is useful if you do not want to use push-to-server SHALLOW in .spec file but do it manually instead, or if
* you have nested JSON object/arrays as cell values and you need to tell the foundset that something nested has changed (DEEP watches are not available in NG2) or
* if you just need a promise to know when the change was done or failed on server.
*
* The calculated pushToServer for the foundset property should be set to 'allow' if you use this method. Then the server will accept
* data changes from this property, but there will be no automatic proxies to detect the changes-by-reference to cell values, so the component uses this call
* to send cell changes instead.
*
* @param rowID the _svyRowId column of the client side row
* @param columnName the name of the column to be updated on server (in that row).
* @param newValue the new data in that cell
* @param oldValue the old data that used to be in that cell
* @return (first versions of this method didn't return anything; more recent ones return this) a promise that will get resolved when the new cell value
* update is done server-side (resolved if ok, rejected if it failed). As with any promise you can register success, error
* and finally callbacks.
*/
columnDataChangedByRowId(rowID: string, columnName: string, newValue: any, oldValue: any): Promise<any>;
/**
* Convenience method that does exactly what #columnDataChangedByRowId does, but based on a row index from the viewport not on that row's ID.
*/
columnDataChanged(rowIndex: number, columnName: string, newValue: any, oldValue: any): Promise<any>;
/**
* Please use columnDataChangedByRowId(...) instead.
*
* @deprecated please use columnDataChangedByRowId(...) instead.
*/
updateViewportRecord(rowID: string, columnID: string, newValue: any, oldValue: any): void;
/**
* Receives a client side rowID (taken from myFoundsetProp.viewPort.rows[idx]._svyRowId)
* and gives a Record reference, an object
* which can be resolved server side to the exact Record via the 'record' property type;
* for example if you call a handler or a servoyapi.callServerSideApi(...) and want
* to give it a Record as parameter and you have the rowID and foundset in your code,
* you can use this method. E.g: servoyapi.callServerSideApi("doSomethingWithRecord",
* [this.myFoundsetProperty.getRecordRefByRowID(clickedRowId)]);
*
* NOTE: if in your component you know the whole row (so myFoundsetProp.viewPort.rows[idx])
* already - not just the rowID - that you want to send you can just give that directly to the
* handler/serverSideApi; you do not need to use this method in that case. E.g:
* // if you have the index inside the viewport
* servoyapi.callServerSideApi("doSomethingWithRecord",
* [this.myFoundsetProperty.viewPort.rows[clickedRowIdx]]);
* // or if you have the row directly
* servoyapi.callServerSideApi("doSomethingWithRecord", [clickedRow]);
*
* This method has been added in Servoy 8.3.
*/
// eslint-disable-next-line @typescript-eslint/ban-types
getRecordRefByRowID(rowId: string): object;
/**
* Sets the preferred viewPort options hint on the server for this foundset, so that the next
* (initial or new) load will automatically return that many rows, even without any of the loadXYZ
* methods above being called.
*
* You can use this when the component size is not known initially and the number of records the
* component wants to load depends on that. As soon as the component knows how many it wants
* initially it can call this method.
*
* These can also be specified initially using the .spec options "initialPreferredViewPortSize" and
* "sendSelectionViewportInitially". But these can be altered at runtime via this method as well
* because they are used/useful in other scenarios as well, not just initially: for example when a
* related foundset changes parent record, when a search/find is performed and so on.
*
* See also the description of "foundsetInitialPageSize" property type for a way to set this
* at design-time (via properties view) or before the form is shown for components that 'page' data.
*
* @param preferredSize the preferred number or rows that the viewport should get automatically
* from the server.
* @param sendViewportWithSelection if this is true, the auto-sent viewport will contain
* the selected row (if any).
* @param centerViewportOnSelected if this is true, the selected row will be in the middle
* of auto-sent viewport if possible. If it is false, then
* the foundset property type will assume a 'paging'
* strategy and will send the page that contains the
* selected row (here the page size is assumed to be
* preferredSize).
*/
setPreferredViewportSize(preferredSize: number, sendViewportWithSelection?: boolean, centerViewportOnSelected?: boolean)