Purpose of this property type
The 'component' property type can be used by web components to nest other 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 property, in which case they will receive data corresponding to records in that foundset.
The 'component' property value client side receives in JS all it needs to 'instantiate' a child web component and make it operational just like a normal 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 root level.
Servoy Developer's form editor handles adding/setting/removing child components through this property type behind-the-scenes. So the property itself will not appear in the properties view during development.
When component is not linked to a foundset
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.
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 property of type 'component'.
The template of our parent component directive uses the svy-component-wrapper directive that Servoy provides to easily integrate child web components 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-value="model.childComponent1"/> as that will not work.
Listening for changes in "model"
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 'performance' section. So:
Advanced usage of non-foundset-linked components
For advanced usage (most will probably never want to do this), if the parent component needs for some reason to manipulate child model/behavior, it can do that by specifying each attribute to be set on child separately - and it can intercept 'component' property content or provide there whatever it wants instead of directly the 'component' property type content. The template snippet below will produce identical results as the one above that only sets the "component-property-value" attribute:
Child components linked to a foundset
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 properties will contain the needed data to build up one set of child components for each row in the 'foundset' typed property's viewport. See the 'foundset' property type page for more info about it's usage.
When components linked to a foundset are requested by a custom web component, that component will need to deal itself with how it creates child components in the browser (how will it visually display separate rows/columns of the foundset as child components) and how it links model/behavior between them and the 'component' typed property/properties. The 'component' typed property provides all that is needed to make that work.
Above we defined 2 properties: 'childElement" for one child component linked to a foundset, and 'childElements' as an array of child components linked to the given foundset. The foundset is specified using "forFoundset" configuration value. In case of the array property - the configuration value is specified for each element of the array using "elementConfig".
Foundset linked component property value in browser
In browser js, a component property value that is linked to a foundset has the following content (example contents of a child text field in a portal parent):
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 angular components out there that generate their own templates for individual components per record (such as uiGrid).
componentDirectiveName: also present when not linked to a foundset; read-only; the directive tag name of the child web component.
- name: also present when not linked to a foundset; read-only; the name (property) of the web component.
- forFoundset.recordBasedProperties: not present when not linked to a foundset; read-only; a list of child component property names that are delivered for each record in the foundset viewport (these properties have different values for each record and will be available in modelViewport).
- model: also present when not linked to a foundset; read-write; properties of the child component that are the same for all records in the foundset's viewPort.
- modelViewport: not present when not linked to a foundset; read-write; model properties of the child component that have different values for each record in the foundset; this array's indexes correspond directly to the indexes of the linked 'foundset' type property's 'viewPort'; it's contents change when the 'foundset' property's viewPort change. See foundset property type documentation for more about how the viewPort is controlled.
handlers: also present when not linked to a foundset; read-only; whatever handlers the web component has attached (chosen by developer at design-time). See discussion about 'rowId' from servoyApi. startEdit below
- api: also present when not linked to a foundset; read-write; whatever API functions the textfield component provided; initially empty object. When component is linked to a foundset and the server side JS code calls such an API function it should get called in the end on the selected foundset record's child component that represents this component property (there is some support choosing to call on childComponents corresponding to all records, through a forFoundset.apiCallTypes entry, but that is not currently used nor documented here).
- servoyApi: also present when not linked to a foundset; read-only; Servoy specific API provided to be used with web components.
- startEdit(propertyName, rowId): provided by the 'component' property; it is used by the child component's svy-autoapply directive (if used by child component) or by custom child component directive code to signal that the editing of that cell has started; as you will probaly have one child component representing this 'component' property value for each record in the foundset's viewPort - startEdit requires receiving a 'rowId' argument to know which record entered edit mode. For example the parent component creates a wrapper startEdit(propertyName) function (closure around the rowId of that record - that is always available in modelViewport as "_svyRowId" that will call the 'component' provided startEdit using that rowId) for each record and feeds it to the corresponding angular component. (the rowId argument is not relevant when using non-foundset linked components)
- apply(propertyName, componentModel, rowId): also present when not linked to a foundset; read-only; 'componentModel' and 'rowId' are only relevant when the component is linked to a foundset. 'componentModel' is the real object that is given by parent component to the per-record child component at record 'rowId' as model - this is most likely made up of a combination between model and a modelViewport row. See discussion about 'rowId' from servoyApi. startEdit above. Apply pushes a changed dataprovider value of the child component to the record on the server. It is used by svy-autoapply directive or directly by custom child component code.
addViewportChangeListener / removeViewportChangeListener: discussed below (starting with 8.3.2).
Listening for changes in "modelViewport" (part of the model that differs based on record) - starting with Servoy 8.3.2
The property provides client-side (in browser) two methods: addViewportChangeListener / removeViewportChangeListener.
So you can add a listener that will receive updates (row insert/delete/change or full viewport update) similar to how the change listener in foundset property type works:
If you want your listener code to execute after all other properties (foundset property, other child component properties) get their updates, you can use something similar to this.
In Servoy < 8.4, you might find what $foundsetTypeUtils 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 destroyed. For example if due to a tabpanel switch of tabs your form is hidden, the component and it's angular scope will be destroyed - at which point you have to remove any listeners that you added on model properties (like the child component property), because the model properties will be reused in the future (for that form when it is shown again) and will keep any listeners in it. When that form will be shown again, it's UI will 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 memory leaks (model properties will keep listeners of obsolete components each time that component's form is hidden, which in turn will prevent those scopes and other objects that they can reference from being garbage collected) and probably weird exceptions (obsolete listeners executing on destroyed scopes of destroyed components).
Example of removing a listener:
Runtime property access
Since Servoy 8.0.3 , component type is scriptable. Type can be accessed like:
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.