Date: Fri, 29 Mar 2024 08:34:20 +0000 (UTC) Message-ID: <637017558.10981.1711701260356@911f0a1bad02> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_10980_1050157490.1711701260355" ------=_Part_10980_1050157490.1711701260355 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
The 'component' property type can be used by web components to nest othe= r web components inside them.
The nested components can be regular components - based of the parent's = environment (foundset, ...) or linked to a .spec defined 'foundset' typed p= roperty, in which case they will receive data corresponding to records in t= hat foundset.
The 'component' property value client side receives in JS all it needs t= o 'instantiate' a child web component and make it operational just like a n= ormal form level web component.
Currently such child components will be available Servoy server side JS = scripting in form.elements - as any other web components in the form at roo= t level.
Servoy Developer's form editor handles adding/setting/removing child com= ponents through this property type behind-the-scenes. So the property itsel= f will not appear in the properties view during development.
A child component that is not linked to another foundset, so it is only = nested within another web component is easy to implement.
Let's say you want to create a component that has 2 child web components= .
"model": { (...) "childComponent1": { "type": "component" }, "childComponent2": { "type": "component" }, =09=09(...) },=20
So our component's spec file defines two properties of type "component" = for the two child components it wants to have. Note: some containers might = want a variable number of child components - that can be done using a prope= rty of type 'component[]'.
<div (...)&= gt; (...) <svy-component-wrapper component-property-value=3D"model.childCompon= ent1"></svy-component-wrapper> <svy-component-wrapper component-property-value=3D"model.childCompon= ent2"></svy-component-wrapper> (...) </div>=20
The template of our parent component directive uses the svy-component-wr= apper directive that Servoy provides to easily integrate child web componen= ts in the right place.
That's all you have to do; no special scripting is required.
NOTE: do not try directly <svy-component-wrapper component-property-v= alue=3D"model.childComponent1"/> as that will not work.
You can listen for changes in the model of this component by defining in= it a model change notifier similar to what is describled here in the 'performan= ce' section. So:
Object.definePr= operty($scope.myComponentProperty.model, $sabloConstants.modelChangeNotifie= r, { configurable: true, value: function(property,val= ue) { switch(property) { case "borderType": (...) break; case "background": case "transparent": (...) break; (...)=20
For advanced usage (most will probably never want to do this), if the pa= rent component needs for some reason to manipulate child model/behavior, it= can do that by specifying each attribute to be set on child separately - a= nd it can intercept 'component' property content or provide there whatever = it wants instead of directly the 'component' property type content. The tem= plate snippet below will produce identical results as the one above that on= ly sets the "component-property-value" attribute:
<svy-compon= ent-wrapper tagname=3D"model.childComponent1.componentDirectiveName" name=3D"model.childComponent1.name" svy-model=3D"model.childComponent1.model" svy-api=3D"model.childComponent1.api" svy-handlers=3D"model.childComponent1.handlers" svy-servoyApi=3D"model.childComponent1.servoyApi"> </svy-component-wrapper>=20
Portals, table view, list view use this. They have a set of 'component' = properties (an array of them) which is linked to a foundset. The components= represent a "row" logically. In this case the browser JS value for the pro= perties will contain the needed data to build up one set of child component= s for each row in the 'fou= ndset' typed property's viewport. See the 'foundset' property type page for more info about i= t's usage.
When components linked to a foundset are requested by a custom web compo= nent, that component will need to deal itself with how it creates child com= ponents in the browser (how will it visually display separate rows/columns = of the foundset as child components) and how it links model/behavior betwee= n them and the 'component' typed property/properties. The 'component' typed= property provides all that is needed to make that work.
"model": { (...) "myFoundset" : "foundset", "childElement" : { "type" : "component", "forFoundset": "myFoundset= " }, // or "childElements" : { "type" : "component[]", "elementConfig" : {"for= Foundset": "myFoundset"} },=20 =09=09(...) },=20
Above we defined 2 properties: 'childElement" for one child component li= nked to a foundset, and 'childElements' as an array of child components lin= ked to the given foundset. The foundset is specified using "forFoundset" co= nfiguration value. In case of the array property - the configuration value is specified for each ele= ment of the array using "elementConfig".
In browser js, a component property value that is linked to a foundset h= as the following content (example contents of a child text field in a porta= l parent):
childEleme= nt: { "componentDirectiveName": "servoydefault-textfield", "name": "shipname", "foundsetConfig": { "recordBasedProperties": ["dataProviderID"] }, "model": { "enabled": true, "text": "Ship Name", "visible": true, "tabSeq": 0, (...) }, "modelViewport": [{ "_svyRowId": ".null;5.10643;_0", "dataProviderID": = "Alfreds Futterkiste" }, { "_svyRowId": ".null;5.10692;_1", "dataProviderID": = "Alfred's Futterkiste 2" }, (...)] "handlers": { "onActionMethodID": function(args, rowId), (...) }, "api": { "getSelectedText": function(), (...) }, "servoyApi": { "startEdit": function(propertyName, rowId), "apply": function(propertyName, componentModel, rowId) }, "addViewportChangeListener": function(listener), "removeViewportChangeListener": function(listener) }=20
These contents can be used to generate what's needed and provide it to a= "svy-component-wrapper" (see usage above) or can be used with other angula= r components out there that generate their own templates for individual com= ponents per record (such as uiGrid).
componentDirectiveName: also present when not linke= d to a foundset; read-only; the directive tag name of the child web compone= nt.
addViewportChangeListener / removeViewportChangeListener: discussed below (starting with 8.3.2).
The property provides client-side (in browser) two methods: addV= iewportChangeListener / removeViewportChangeListener.
So you can add a listener that will receive updates (row insert/delete/c= hange or full viewport update) similar to how the change listener= in foundset property type works:
var l =3D funct= ion(viewportChanges) { // check to see what actually changed in the component and update what = is needed in browser }; $scope.model.myComponentThatIsFoundsetLinked.addViewportChangeListener(l);<= /pre>=20
If you want your listener code to execute after all other properties (fo= undset property, other child component properties) get their updates, you c= an use something similar to this= .
The "viewportChanges" parameter above is a javascript Object containing = one or more keys, depending on what changes took place. The keys specify th= e type of change that happened. The value gives any extra information neede= d for that type of change:
{ $foundsetTypeConstants.NOTIFY_VIEW_PORT_ROWS_COMPLETELY_CHANGED: { old= Value : ..., newValue : ... }, =20 // if we received add/remove/change operations on a set of properties f= rom modelViewport // corresponding to a record this key will be set; as seen below, it co= ntains "updates" which // is an array that holds a sequence of granular update operations to t= he viewport; the array // will hold one or more granular add or remove or change operations; // // BEFORE Servoy 8.4: all the "startIndex" and "endIndex" values below = are relative to the viewport's // state after all previous updates in the array were already processed= (so they are NOT relative to // the initial or final state of the viewport data!). Updates can come = in a random order so there is // NO guarantee related to each change/insert/delete indexes pointing t= o the correct new data in the // final current viewport state // // STARTING WITH Servoy 8.4: all the "startIndex" and "endIndex" values= below are relative to the // viewport's state after all previous updates in the array were alread= y processed. But due to some // pre-processing that happens server-side (it merges and sorts these o= ps), the indexes of update // operations THAT POINT TO DATA (so ROWS_INSERTED and ROWS_CHANGED ope= rations) are relative also to // the viewport's final/current state, so after ALL updates in the arra= y were already processed // (so these indexes are correct both related to the intermediate state= of the viewport data=20 // and to the final state of viewport data). // This means that it is now easier to apply UI changes to the componen= t as these granular updates // GUARANTEE that if you apply them in sequence (one by one) to the com= ponent's UI (delete, insert and // change included) you can safely use the indexes in there to get new = data from the present state // of the viewport. // $foundsetTypeConstants.NOTIFY_VIEW_PORT_ROW_UPDATES_RECEIVED: { =20 // DEPRECATED in Servoy 8.4: granular updates are much easier to ap= ply now; see comment above // sometimes knowing the old viewport size helps calculate incommin= g granular updates easier $foundsetTypeConstants.NOTIFY_VIEW_PORT_ROW_UPDATES_OLD_VIEWPORTSIZ= E: ..., =20 $foundsetTypeConstants.NOTIFY_VIEW_PORT_ROW_UPDATES : [ { type : $foundsetTypeConstants.ROWS_CHANGED, startIndex : ..., endIndex : ... }, { // NOTE: insert signifies an insert into the client vie= wport, not necessarily // an insert in the foundset itself; for example callin= g "loadExtraRecordsAsync" // on the foundset property // can result in an insert notification + bigger viewpo= rt size notification, // with removedFromVPEnd =3D 0 type : $foundsetTypeConstants.ROWS_INSERTED, startIndex : ..., endIndex : ..., =20 // DEPRECATED starting with Servoy 8.4; it would always= be 0 here // as server-side code will add a separate delete opera= tion instead - if necessary // BEFORE 8.4: when an INSERT happened but viewport siz= e remained the same, it was // possible for some of the rows that were previously a= t the end of the viewport // to slide out of it; "removedFromVPEnd" gives the num= ber of such rows that were // removed from the end of the viewport due to this ins= ert operation; removedFromVPEnd : ... }, { // NOTE: delete signifies a delete from the client view= port, not necessarily // a delete in the foundset itself; for example calling= "loadLessRecordsAsync" // on the foundset property // can result in a delete notification + smaller viewpo= rt size notification, // with appendedToVPEnd =3D 0 = =20 type : $foundsetTypeConstants.ROWS_DELETED, startIndex : ..., endIndex : ..., // DEPRECATED starting with Servoy 8.4; it would always= be 0 here // as server-side code will add a separate insert opera= tion instead - if necessary // BEFORE 8.4: when a DELETE happened inside the viewpo= rt but there were more rows // available in the foundset after current viewport, it= was possible for some of those // rows to slide into the viewport; "appendedToVPEnd " = gives the number of such rows // that were appended to the end of the viewport due to= this delete operation appendedToVPEnd : ... } ] } =20 }=20
In Servoy < 8.4, you might find what $foundsetTypeU= tils provides useful depending on how you plan on using this listener. = Starting with 8.4 granular updates are easier to use and you don't need to = process those indexes anymore before using them. See comments above.
Always make sure to remove listeners when the component = is destroyed
It is important to remove the listeners when your component's scope is d= estroyed. For example if due to a tabpanel switch of tabs your form is hidd= en, the component and it's angular scope will be destroyed - at which point= you have to remove any listeners that you added on model propertie= s (like the child component property), because the model propertie= s will be reused in the future (for that form when it is shown again) and w= ill keep any listeners in it. When that form will be shown again, it's UI w= ill get recreated - which means your (new) component will probably add the = listener again.
If you fail to remove listeners on $scope destroy this will lead to memo= ry leaks (model properties will keep listeners of obsolete components each = time that component's form is hidden, which in turn will prevent those scop= es and other objects that they can reference from being garbage collected) = and probably weird exceptions (obsolete listeners executing on destroyed sc= opes of destroyed components).
Example of removing a listener:
=09=09=09$scope= .$on("$destroy", function() { =09=09=09=09if (l && $scope.model.myComponentThatIsFoundsetLinked) = $scope.model.myComponentThatIsFoundsetLinked.removeViewportChangeListener(l= ); =09=09=09});=20
Since Servoy 8.0.3 , component type is scriptable. Type can be accessed = like:
elements.m= ycomponent.childElements.mybean.beanProperty=20
Mycomponent is the name of the component which contains a childElements = property of type component or component[] . From there, you can access the = component inside type via name or index (if type is an array). Then you can= access or assign properties of the mybean type.