The purpose of the app.ducx object model language is to define the persistent object model for a software component.
Using the app.ducx object model language, you can easily define the basic elements that make up the object model:
An object model block consists of import declarations and object model elements. The objmodel keyword denotes an object model block. It must be followed by the reference of your software component and curly braces.
Object model blocks can only be contained in files with a .ducx-om extension.
Syntax |
objmodel softwarecomponent // Object model elements (object classes, types, fields) |
An object class defines the abstract characteristics of an object, which include properties and the use cases that can be executed on the instances of the object class.
Syntax |
class<metaclass> reference : baseclass { |
Each object class is an instance of a meta class that defines the characteristics of the object class itself.
The meta class is specified in angle brackets following the class keyword. The definition of the meta class is optional. COOSYSTEM@1.1:ObjectClass is the default meta class.
Usually, you will use one of the following meta classes for defining a new object class:
Each object class must have a base class which must either directly or indirectly be derived from object class COOSYSTEM@1.1:Object. This object class is the base class of all other object classes.
The base class is specified after a colon following the reference of the newly defined object class.
If you do not find a more specific base class that is appropriate for your new class, you will usually derive it from one of the following most common base classes:
Example |
objmodel APPDUCXSAMPLE@200.200 // New object class for storing resources that is derived from // New object class for specification documents that is derived from // New object class for projects that is derived from |
Besides choosing the meta class and the base class, you can also define several other aspects of the object class, such as whether it should be abstract, programmatic, common, derivable, or compound.
Setting an Object Class to Abstract
The abstract keyword allows you to define an abstract object class.
An abstract object class can only be used as a base class from which child object classes may be derived, and which cannot be instantiated itself.
Example |
objmodel APPDUCXSAMPLE@200.200 class Vehicle : BasicObject { class Car : Vehicle { |
Setting an Object Class to Programmatic
Using the programmatic keyword, an object class can be marked as not creatable by a user via the user interface. Thus, for example, the object class is not available in the “Object” > “New” dialog but objects of the object class can be created programmatically.
Example |
objmodel APPDUCXSAMPLE@200.200 class Order : BasicObject { |
Marking an Object Class as Compound
Using the compound keyword, an object class can be marked as compound. Only the instances of a compound object class are displayed in the tree view on the left-hand side of the Fabasoft Web Client.
Example |
objmodel APPDUCXSAMPLE@200.200 class Product : CompoundObject { |
Marking an Object Class as a Common Class
Using the common keyword, an object class can be marked as a common class. This means that this class is creatable or usable in common locations like in a folder (i.e. in the COOSYSTEM@1.1:objchildren property). Using the allowcommonclasses keyword with a property can be marked as a property for containing common classes.
Example |
objmodel APPDUCXSAMPLE@200.200 class Product : CompoundObject { |
Marking an Object Class as Derivable
Using the derivable keyword, an object class can be marked as derivable. This means that classes from other software components can use this class as base class.
Example |
objmodel APPDUCXSAMPLE@200.200 class Product : CompoundObject { |
Each object class can have several properties to store data of various data types.
Existing properties can be reused and referenced within an object class or within a compound type.
Example |
objmodel APPDUCXSAMPLE@200.200 class FileCategory : BasicObject { |
When reusing properties, keep in mind that changes to the original property might affect your solution. For instance, the definition of the selectable object classes in the FSCFOLIO@1.1001:dcstate property in the example might be changed in a future version. As a result, reusing properties might result in unpredictable side-effects in some cases.
Syntax |
datatype reference modifiers; |
New properties can be defined within an object class, a compound type, or the fields block. For each property defined using Fabasoft app.ducx, a corresponding property component object is created as part of the project’s software component.
A property must be defined based on a valid data type. Each property can either store a scalar value or a list of values of the specified data type. Add square brackets after the data type to specify a list.
There are two ways of defining a new property: the simple shorthand notation and the extended notation for including triggers and constraints in a property definition.
A shorthand property definition consists of
Additional features concerning property definitions are covered in chapter “Extended Property Definitions”.
Example |
objmodel APPDUCXSAMPLE@200.200 class Project : CompoundObject { |
Every property must have a data type. Simple data types, enumeration types, and compound types are supported.
Simple data types are provided by software component COOSYSTEM@1.1. The following table shows all supported simple data types.
Simple data type | Keyword | Maximum Size |
COOSYSTEM@1.1:STRING | string | 4000 characters |
COOSYSTEM@1.1:BOOLEAN | boolean | |
COOSYSTEM@1.1:INTEGER | integer, time, timespan | 64 Bit |
COOSYSTEM@1.1:FLOAT | float | 16 digits |
COOSYSTEM@1.1:DATETIME | date, datetime | |
COOSYSTEM@1.1:Currency | currency | |
COOSYSTEM@1.1:CONTENT | content | |
COOSYSTEM@1.1:DICTIONARY | dictionary | |
COOSYSTEM@1.1:OBJECT | object |
For most simple data types, a corresponding list data type is provided for storing lists of items of this type. The supported list data types are depicted in the next table.
Simple data type | Keyword |
COOSYSTEM@1.1:STRINGLIST | string[] |
COOSYSTEM@1.1:BOOLEANLIST | boolean[] |
COOSYSTEM@1.1:INTEGERLIST | integer[], time[], timespan[] |
COOSYSTEM@1.1:FLOATLIST | float[] |
COOSYSTEM@1.1:DATETIMELIST | date[], datetime[] |
COOSYSTEM@1.1:CONTENTLIST | content[] |
COOSYSTEM@1.1:DICTIONARYLIST | dictionary[] |
COOSYSTEM@1.1:OBJECTLIST | object[] |
Enumeration Types
For information concerning the definition and extension of enumeration types, please consult chapter “Defining Enumeration Types”.
Compound Types
For information concerning the definition and extension of compound types, please consult chapter “Defining Compound ”.
The string keyword denotes a data type that stores a string of characters and can be used for defining string properties.
The length of a string can be specified enclosed in parentheses. If the length is not specified, the default length of 254 characters is assumed.
Note: String lists can be used for multiline input fields.
Example |
objmodel APPDUCXSAMPLE@200.200 class Task : BasicObject { // Sized string property holding a maximum of 2 characters // String list property that is displayed as a multiline input field |
The boolean keyword is used to define properties to store the Boolean values (true and false).
Note: A Boolean property can actually have three states as it can also be undefined.
Example |
objmodel APPDUCXSAMPLE@200.200 class Application : Case { |
The integer keyword denotes a simple data type and can be used to define integer number properties that store values consisting of up to 10 digits.
Integers may be signed or unsigned. Unsigned integers are capable of representing only non-negative values whereas signed integers are capable of representing negative values as well.
For defining unsigned integers use the unsigned integer keywords.
For an integer, its display size in digits can be specified enclosed in parentheses. If no size value is specified, the default size of 10 digits is assumed.
Example |
objmodel APPDUCXSAMPLE@200.200 class TaxReport : BasicObject { // Sized integer number property with 3 digits // Unsigned integer number property with 10 digits // Sized unsigned integer number property with 4 digits |
The float keyword denotes a floating-point number data type that represents a real number and can be used to define floating-point number properties to store values consisting of up to 16 digits in total with a maximum of 9 digits of precision.
The float keyword can be preceded by the keyword unsigned to denote an unsigned float which can only represent non-negative values.
For a floating-point number property, the size in digits before and the number of digits of precision can be specified enclosed in parentheses. If no size values are specified, the default sizes of 10 digits before with 2 digits of precision are assumed.
Example |
objmodel APPDUCXSAMPLE@200.200 class AssetPerformance : BasicObject { // Sized floating-point number property of a 3 digit number with // Sized unsigned floating-point number property of an 8 digit number |
The date keyword is used for defining a property to store a date value.
The datetime keyword is used for defining a property that stores both a date and a time value.
By default, there is no conversion of the date time value to the current time zone. This behavior is also described by the optional suffix universal.
The suffix local is used for time zone dependent values, where a conversion to local time is done each time the property is accessed using a kernel interface.
Example |
objmodel APPDUCXSAMPLE@200.200 class Milestone : BasicObject { // Date and time property with local time conversion enabled // Date and time property with local time conversion disabled |
The time keyword is used for defining a property to store a time value.
Time values are stored in seconds in an integer number property of data type COOSYSTEM@1.1:INTEGER. The COOATTREDIT@1.1:CTRLTimestamp control is used for displaying time values in time format.
Example |
objmodel APPDUCXSAMPLE@200.200 struct Session { class Conference : BasicObject { |
The timespan keyword is used for defining a property to store a time span.
The time span is stored in an integer number property of data type COOSYSTEM@1.1:INTEGER. The COOATTREDIT@1.1:CTRLTimespan control is used for displaying the time span in days, hours, minutes, and seconds.
Example |
objmodel APPDUCXSAMPLE@200.200 class MailPollingDefinition : BasicObject { |
The currency keyword is used to define a currency property that represents an amount of money in a certain currency.
For a currency property, the size in digits before and the number of digits of precision can be specified enclosed in parentheses.
Example |
objmodel APPDUCXSAMPLE@200.200 class Project : CompoundObject { // Sized currency property of a 12 digit number with |
A content property that is defined using the content keyword can store any kind of binary data, such as a Microsoft Word or LibreOffice document.
A file can be imported into a content property from the file system, and conversely content that is stored in a content property can be exported to the file system.
Example |
objmodel APPDUCXSAMPLE@200.200 class Project : CompoundObject { |
A dictionary is a list of key-value pairs. Each key-value pair consists of a string which is used as a key for addressing the dictionary entry, and a value or a list of values of the same data type.
Example |
objmodel APPDUCXSAMPLE@200.200 class StatusLog : BasicObject { |
An object pointer property can store a pointer to an object or a list of pointers to objects.
No explicit keyword is required for defining an object pointer property. Instead, the object class of the objects that shall be referenced by the new object pointer property is used as data type.
Example |
objmodel APPDUCXSAMPLE@200.200 class PeerGroup : BasicObject { // Object list for storing a list of user objects |
By default, instances of all object classes that are directly or indirectly derived from the object class provided in the definition can be selected in the object pointer. A default object pointer property will also allow the creation of new object instances within the property. However, for object pointer properties, you can provide an extended definition of the object classes allowed and not allowed in the property in order to limit the selectable and creatable items.
The allow keyword can be used to define a list of object classes that should be allowed in the object pointer property, and the exclude keyword can be used for excluding object classes. Both the object classes listed in the allow block and in the exclude block must be either directly or indirectly derived from the object class provided in the object pointer property definition.
For each of the object classes listed within an allow block, the create keyword can be added after the reference of the object class to allow the creation of instances of this object class in the object pointer property. If the create keyword is omitted, no instances can be created directly within the object pointer property.
Object classes listed within an allow or exclude block must be separated by semicolons.
The allowcommonclasses keyword defines that the object pointer property can contain all common object classes (all object classes with common set to true).
Example |
objmodel APPDUCXSAMPLE@200.200 class Department : BasicObject { // This definition creates an object list for storing objects that |
In contrast to properties, which are defined directly within an object class, an enumeration type can only be defined as a separate objmodel model element.
A custom enumeration type can be defined using the enum keyword which must be followed by a reference and a block containing the permissible enumeration items separated by commas.
Each enumeration item can be assigned a specific integer value. For enumeration items that are not assigned to a specific integer value, the compiler automatically generates integer values. To do so, the compiler simply increments the integer value of the preceding enumeration item by 1, starting with value 1 for the first item if it does not have an assigned value.
Note: It is highly recommended to explicitly assign integer values to every enumeration item to avoid errors with extensions of the enumeration type later on.
Example |
objmodel APPDUCXSAMPLE@200.200 // Enumeration type consisting of four enumeration items with // Enumeration type consisting of six enumeration items with an // Enumeration type consisting of enumeration items with partially |
Note: Existing enumeration types can be extended with additional enumeration items. For details on how to extend an existing enumeration type with additional enumeration items, please consult chapter “Extending an Existing Enumeration Type”.
Using the keyword obsolete, enumeration values can be defined as disabled. Drop-down lists for enumeration values in the user interface do not contain disabled values.
Example |
objmodel APPDUCXSAMPLE@200.200 |
In rare cases it is necessary to specify some other attributes for the type. This is accomplished by using generic assignments before the block of enumeration values.
Example |
objmodel APPDUCXSAMPLE@200.200 |
As with an object pointer property, no explicit keyword is required for defining an enumeration property. Instead, you must provide the enumeration type which you want to assign to the new enumeration property.
Example |
objmodel APPDUCXSAMPLE@200.200 enum Transmission { enum Options { class Car : BasicObject { // Enumeration property that can store a list of enumeration items |
A compound type is a structure that can contain a list of properties of any valid data type. Similar to enumeration types, compound types can be defined or extended within an objmodel block.
Defining a New Compound Type
A custom compound type can be defined using the struct keyword that must be followed by a reference and a block containing the properties that should become part of the compound type separated by semicolons.
You can either reference existing properties or define new properties directly within a compound type.
Example |
objmodel APPDUCXSAMPLE@200.200 // Compound type for storing publications |
Defining the Key for a Compound Type
For a compound type, you can define one or more key properties. The key is evaluated for determining unique entries in a property using the compound type.
The properties making up the key must be referenced in a block that is assigned to COOSYSTEM@1.1:typeuniqueattrs.
In addition to setting the key properties for a compound type, you can also define that compound properties using this compound type can contain unique entries only. To enforce unique keys, COOSYSTEM@1.1:typelistunique must be set to true.
In the following example, the APPDUCXSAMPLE@200.200:isbn property is specified as the key for compound type “Publication”. Additionally, the COOSYSTEM@1.1:typelistunique is set to true so the compound type will only allow entries with a unique ISBN.
Example |
objmodel APPDUCXSAMPLE@200.200 struct Publication { |
Note: You can also extend existing compound types with new properties. For details on how to extend an existing compound type with new properties, please consult chapter “Extending an Existing Compound Type”.
Defining a Type Cast for a Compound Type
Optionally a type cast may be defined. In expressions the type cast allows you to assign values directly to properties of the compound type, based on the type of the value without need to explicitly specify the property. The type cast defines which type should be assigned to which property. The TypeCastPrototype has two parameters: source and target.
In the following example string lists are assigned to features and integers and floats are assigned to productioncosts. In an expression you can make following assignment: Feature feature = 300;. The result is the same as when writing feature.productioncosts = 300;.
Note: The direct assignment to the compound property productioncosts works because for currencies a default type cast is provided by default.
Example |
app.ducx Object Model Language objmodel APPDUCXSAMPLE@200.200 struct Feature { app.ducx Use Case Language usecases APPDUCXSAMPLE@200.200 |
Using a Primary Property for Direct Assignments
For some compound properties it is likely that only one property of the compound property is set (e.g. FSCVAPP@1.1001:applytofield). For this case a property of the compound property can be marked by setting COOSYSTEM@1.1:typeprimaryattr. Additionally, the predefined type cast COOSYSTEM@1.1:TypeCast has to be used.
In the following example title is marked as primary property thus assignments like Publication publication = “An Introduction to Fabasoft app.ducx”; are possible.
Example |
objmodel APPDUCXSAMPLE@200.200 struct Publication { |
Samples for compound types with primary properties are listed in the following table:
Compound type | Primary property |
FSCVAPP@1.1001:Branch | FSCVAPP@1.1001:identopt |
FSCVAPP@1.1001:ApplyToField | FSCVAPP@1.1001:identopt |
COOSYSTEM@1.1:LanguageStringList | COOSYSTEM@1.1:langstring |
COOSYSTEM@1.1:LanguageContentList | COOSYSTEM@1.1:langcontent |
COOSYSTEM@1.1:Content | COOSYSTEM@1.1:contcontent |
COOSYSTEM@1.1:Currency | COOSYSTEM@1.1:currvalue |
Note: Direct assignments can not only be utilized in expressions but also in domain specific languages for initializing values or defining instances.
Specifying a Concrete Compound Type
Sometimes it is necessary to use a more specialized compound type as type for a compound property. This can be achieved by listing the required type after the struct keyword.
Example |
objmodel APPDUCXSAMPLE@200.200 struct<TypeCustomizationPointDef> CPManualCreated { |
In the same way as for an enumeration property, the definition of a compound property does not require a special keyword. Instead, the compound type is used as data type for the new compound property.
Example |
objmodel APPDUCXSAMPLE@200.200 struct OrderPosition { class Product : BasicObject { class Order : CompoundObject { // Compound property that is reusing the compound type |
A link property allows you to create a bidirectional relationship between objects. There are two ways to establish such a relationship. On the one hand the backlink keyword can be used to create explicitly back link objects that manage the bidirectional relationship, on the other hand just the link keyword can be used. In the second case no back-link objects are created.
The backlink keyword is used for defining a back-link property. It must be followed by a reference and curly braces.
Use link when referencing a link or back link property from an object pointer property in order to establish a bidirectional relationship.
Back link properties must be directly assigned to an object class. You cannot use the back-link properties in a compound type. However, an object pointer property referencing a back-link property can also be part of a compound type.
In the following example, an order has an object pointer property pointing to an invoice. However, an invoice also needs to know to which order it belongs to. Therefore, a back-link property is defined for pointing back to the order. The back link ensures that the integrity of the relationship between linked objects is maintained automatically. Whenever the order’s invoice object pointer property is changed, the change is reflected in the invoice.
Example |
objmodel APPDUCXSAMPLE@200.200 class Order : CompoundObject { class<ContentObjectClass> Invoice : ContentObject { |
The following example shows the use of only the link keyword. The links ensure that the integrity of the relationship between linked objects is maintained automatically. Whenever the order’s invoice object pointer property is changed, the change is reflected in the invoice and vice versa.
Example |
objmodel APPDUCXSAMPLE@200.200 class Order : CompoundObject { class<ContentObjectClass> Invoice : ContentObject { |
An expression property is a string list property to hold the expression and has additional information regarding the evaluation context. Most of this additional information can be specified with additional elements in the property definition.
Example |
objmodel APPDUCXSAMPLE@200.200 class StatusLog : BasicObject { |
This expression property describes an expression with the following behavior:
It is also possible to specify the argument list using a prototype (see chapter “Defining a Prototype”).
Example |
objmodel APPDUCXSAMPLE@200.200 class StatusLog : BasicObject { |
When defining new properties within an object class or within a compound type, you can add so-called property modifier prefixes before the property’s reference.
unique is currently the only available modifier prefix, which can be used to define that values of a list property have to be unique.
Example |
objmodel APPDUCXSAMPLE@200.200 class Order : CompoundObject { |
When defining new properties within an object class or within a compound type, you can add so-called property modifier suffixes (also referred to as modifiers) after the property’s reference. Modifiers allow you to change the appearance of a property as well as some other aspects pertaining to the representation of property values.
The following table contains a list of supported modifiers along with a brief description.
Modifier | Description |
not null | The property must contain a value. |
readonly | The property is not changeable in any circumstance. |
readonly(ui) | The property is not changeable in the user interface. |
readonly(inst) | The property is not changeable when the software component of the component object is in the “Installed” state. |
volatile | The property is not cached by the kernel. This means that the get action or value expression is evaluated each time the property is accessed. |
volatile(tx) | The property is cached in the current transaction by the kernel. This means that the get action or value expression is evaluated each first time the property is accessed in a transaction. This will improve performance if the calculation of the property is extensive. Please have in mind that the values stored in the transaction are transferred to the client in case of a detach operation. Therefore, values like large lists or contents will produce an additional higher overhead. Note: You can use COOSYSTEM@1.1:ObjectRefresh() to enforce the recalculation of all properties defined with a get action or value expression. In the first parameter of COOSYSTEM@1.1:ObjectRefresh() a property list can be specified to limit the refresh to those properties. |
invisible | The property is not visible in the user interface. |
nocopy | The property is cleared on copied objects. |
nocreate | In the user interface the create functionality is not provided. Note: In object pointer controls only. |
nosearch | In the user interface the search functionality is not provided. Note:
|
If multiple modifiers are appended to a property, they must be separated by whitespaces.
Example |
objmodel APPDUCXSAMPLE@200.200 enum OrderState { class Order : CompoundObject { |
An extended property definition allows you to define initialization values, to assign triggers to and to define constraints for your properties.
The following syntax example illustrates the structure of extended property definitions.
Syntax |
datatype reference modifiers { // Definition of the access type required to read the property // Trigger that is called when the property is created // Value constraint // Behavior in form pages: Check if property is visible |
Using the init keyword, you can assign an initialization value to a property. The property is initialized with this value when you create a new instance of the object class the property is assigned to.
In addition to static initialization values, you can also use app.ducx expression language to calculate the value used for initializing a property. An expression block is required to define an initialization expression.
Example |
objmodel APPDUCXSAMPLE@200.200 class Order : CompoundObject { |
A property can be protected with an access type for reading the property value and with an access type for changing the property:
Both the access type for reading and for changing the property value are optional.
Example |
objmodel APPDUCXSAMPLE@200.200 class Order : CompoundObject { |
Triggers are actions that are invoked or “fired” automatically when a predefined event occurs. Each trigger has assigned a specific action prototype listing the parameters that are passed to the implementation of the trigger action when it is called.
Keyword | Description |
ctor | The constructor trigger is fired when the property is created. A constructor trigger action must have assigned the prototype COOSYSTEM@1.1:AttrConstructorPrototype. |
linector | The line constructor trigger is fired when a new entry is created in a compound property. A line constructor trigger action must have assigned the prototype COOSYSTEM@1.1:AttrLineConstructorPrototype. |
dtor | The destructor trigger is fired when the property is destroyed. A destructor trigger action must have assigned the prototype COOSYSTEM@1.1:AttrDestructorPrototype. |
get | The get value trigger is fired after the property value is read from the database. A get value trigger action must have assigned the prototype COOSYSTEM@1.1:AttrGetPrototype. |
set | The set value trigger is fired before the property value is written to the database. A set value trigger action must have assigned the prototype COOSYSTEM@1.1:AttrSetPrototype. |
copy | The copy trigger is fired when an object containing the property is duplicated. A copy trigger action must have assigned the prototype COOSYSTEM@1.1:AttrCopyPrototype. |
display | The display trigger is fired to format the property value for being displayed in a list view column. A display trigger action must have assigned the prototype COOSYSTEM@1.1:AttrGetDispPrototype. |
search | The search trigger is fired when searching for values in the property. A search trigger action must have assigned the prototype COOSYSTEM@1.1:AttrSearchPrototype. |
getver | The get versioned value trigger is fired when a version of the property value is read. A get versioned value trigger action must have assigned the prototype COOSYSTEM@1.1:AttrGetVersionPrototype. |
fixver | The fix version trigger is fired when a version of the property value is created. A fix version trigger action must have assigned the prototype COOSYSTEM@1.1:AttrFixVersionPrototype. |
delver | The delete version trigger is fired when a version of the property is deleted. A delete version trigger action must have assigned the prototype COOSYSTEM@1.1:AttrDelVersionPrototype. |
restver | The restore version trigger is fired when a version of the property value is restored. A restore version trigger action must have assigned the prototype COOSYSTEM@1.1:AttrRestVersionPrototype. |
archive | The archive trigger is fired when the property value is archived. An archive trigger action must have assigned the prototype COOSYSTEM@1.1:AttrArchivePrototype. |
restore | The restore trigger is fired when the property value is being restored from an archive. A restore trigger action must have assigned the prototype COOSYSTEM@1.1:AttrRestArchivePrototype. |
Example |
objmodel APPDUCXSAMPLE@200.200 class Order : CompoundObject { |
Note: The keyword nocopy can be used to specify a property with copy = NoOperation.
Example |
objmodel APPDUCXSAMPLE@200.200 class Order : CompoundObject { |
For further information on how to implement trigger actions, please consult chapter “Implementing Triggers”.
Integrity and value constraints for calculating and validating values are used to prevent invalid data entry into a property.
Fabasoft app.ducx currently supports following types of constraints:
A value constraint can be defined for the automatic calculation of a property value.
The keyword value is used for defining a value constraint where the app.ducx expression language must be used for the implementation.
Value constraints only make sense for calculated properties that cannot be changed by the user. Therefore, in most cases, properties with value constraints should also be set to volatile and readonly using appropriate property modifiers. If the property is not read-only the app.ducx expression must have a result that can store the changeable value (e.g. a property). Additionally, value constraints cannot be used within compound properties.
Please note that volatile properties do not show up in the search dialog box by default unless you attach a search trigger action to the property as demonstrated in the example.
When implementing a value constraint in app.ducx expression language, the current object can be accessed using the local scope this or global scope ::this. The calculated value that should be displayed in the property must be passed back as the return value of the expression.
In this example, the total value of all individual order positions of an order is calculated and displayed in a property with a value constraint.
Example |
objmodel APPDUCXSAMPLE@200.200 struct OrderPosition { class Product : BasicObject { class Order : CompoundObject { for (OrderPosition @position : orderpositions) { |
Filter constraints allow you to limit the values that are selectable in an object pointer property.
The keyword filter or searchfilter is used for defining a filter constraint. The app.ducx expression language must be used for implementing the filter constraint. Search filters are used to reduce available objects of object pointer properties in context of a search.
There are two distinct variants of filter constraints that have different scopes depending on the data type of the return value of the filter expression:
Note:
Object List Filter Constraints
An object list filter constraint is an expression returning a list of objects. For properties with an object list filter constraint, users can only select values out of the list of permitted values.
For an object list filter constraint, the object containing the object pointer property can be accessed using the local scope this. The global scope ::this contains a list of objects that should be filtered, i.e. after a quick search operation in the object pointer property control. If empty, the Fabasoft app.ducx Expression should retrieve a meaningful default for objects that are usable as a value for the property.
If an object list filter is terminated with the exception COOSYSTEM@1.1:COOSTERR_BREAK, the default behavior is provided. This means, that fitting objects that have been recently used, are provided as the filter result.
Note: The type of the expression must be determinable before the expression is evaluated. To provide a precise type, a cast to OBJECTLIST() is sometimes helpful.
Boolean Filter Constraints
A Boolean filter constraint is evaluated on each of the objects that would be selectable in an object pointer property based on the definition of allowed object classes for the property. If the filter expression returns true, the object is included in the list of selectable values displayed to users. Otherwise, the object is filtered out and cannot be selected.
For a Boolean filter constraint, the local scope this contains the object that is to be selected and the global scope ::this contains the object containing the object pointer property.
For instance, if a Boolean filter constraint is defined for an object pointer property for selecting users in a domain containing 300 user objects, the filter expression would be evaluated up to 300 times as it is evaluated for each user object.
In the example, there are three object classes: product, vendor, and order. An instance of vendor has a list of products sold by the vendor. An order contains an object pointer property pointing to a vendor, and a list of products ordered from this vendor. For the order’s vendor object pointer property, a Boolean filter constraint has been defined only allowing the user to select vendors that actually sell products. Moreover, the list of ordered products has attached an object list filter constraint that limits the selectable products to the list of products offered by the vendor referenced in the vendor object pointer property.
Example |
objmodel APPDUCXSAMPLE@200.200 class Product : BasicObject { class Vendor : CompoundObject { class Order : CompoundObject { |
When a value, filter, validation or user interface change constraint is evaluated, the transaction variables of software component COOSYSTEM@1.1 listed in the following table provide you with information on the path to the currently selected property.
Transaction Variable | Description |
TV_ATTRPATHATTRDEFS | TV_ATTRPATHATTRDEFS contains the property path to the currently selected property. For example, if the object pointer property APPDUCXSAMPLE@200.200:product in the third row of the compound property APPDUCXSAMPLE@200.200:orderpositions of an order is selected, TV_ATTRPATHATTRDEFS contains an object list consisting of the elements APPDUCXSAMPLE@200.200:orderpositions and APPDUCXSAMPLE@200.200:product. |
TV_ATTRPATHINDICES | TV_ATTRPATHINDICES contains the zero-based indices of the selected rows to the currently selected property. For example, if the object pointer property APPDUCXSAMPLE@200.200:product in the third row of the compound property APPDUCXSAMPLE@200.200:orderpositions of an order is selected, TV_ATTRPATHINDICES contains an integer list consisting of the elements 2 and 0. |
The following example demonstrates how to access the transaction variables provided by software component COOSYSTEM@1.1.
Example |
objmodel APPDUCXSAMPLE@200.200 class Product : BasicObject { class Vendor : CompoundObject { struct OrderPosition { class Order : BasicObject { |
A validation constraint is an app.ducx expression that is evaluated to check whether the value entered into the property is valid in order to prevent invalid data entry into a property.
The expression must return a Boolean value. The return value true signals that the value entered by the user is valid. If the expression returns false, an error message is displayed on the form page containing the property requiring the user to correct the entered value.
Alternatively, the expression can also throw an exception for displaying a custom error message in case the validation fails.
The local scope contains the vApp state dictionary. The global scope contains a dictionary holding the keys listed in the next table.
Key | Description |
value | The value key stores the current value of the property. |
root | The root key stores the object containing the property. |
parent | This key stores the parent of the property. If the property is part of a compound type, the parent is holding the compound property. If the property is not embedded within a compound property, the parent key holds the object containing the property. Thus, for properties that are not part of a compound property, the value stored in the parent key is equal to the value stored in the root key. |
attribute | The attribute key contains the property definition. |
Example |
objmodel APPDUCXSAMPLE@200.200 class Project : CompoundObject { |
A user interface change constraint can be defined in order to trigger the re-evaluation of filter constraints or to set or clear the values of other properties.
The keywords change (value gets changed) and searchchange (value gets changed in context of a search) are used for defining a user interface change constraint. The weight constraint can be used to change the background color of an object. The app.ducx expression language must be used to implement a user interface change constraint.
The local scope contains the vApp state dictionary. When implementing a user interface change constraint in app.ducx expression language, a dictionary holding the keys listed in the previous table is made available in the global scope ::this. The expression must return the value true to trigger a round-trip to the web service, which is necessary for the re-evaluation of filter constraints.
In the following example, the invoice date, the payment date and the processing state properties are cleared if the object pointer property referencing the order is changed.
Example |
objmodel APPDUCXSAMPLE@200.200 class<ContentObjectClass> Invoice : ContentObject { |
Object pointer properties can be set as child, if the referenced object values do not exist as separate entities; but have a meaning only as part of the container object. For child properties the following are the default values:
Example |
objmodel APPDUCXSAMPLE@200.200 class Order : BasicObject { |
After an upload to the configured web service, a recycle is necessary, because GetChildredAttrDef collects the child information of the class attributes only at the first call of the class for performance reason.
Object pointer properties can be set as hierarchy, if the referenced object values are of a similar class as the container so that the values are in a hierarchical context. When selecting a value for a hierarchy property in a search form, the hierarchy relations can be specified in the user interface.
Example |
objmodel APPDUCXSAMPLE@200.200 class Order : BasicObject { Order[] suborders { |
A field is a property that does not belong to an object class. Thus, fields may be used to temporarily compute values that are not persistently stored.
The fields keyword is used to define fields. It must be nested within the objmodel block.
Each field consists of a datatype followed by a reference and a semicolon.
Example |
objmodel APPDUCXSAMPLE@200.200 fields { |
Fabasoft app.ducx allows you to extend existing object classes, enumeration types, and compound types that belong to another friend software component.
You can extend an existing object class with new properties of your software component.
Please note that you cannot extend object classes not belonging to your software component with properties not belonging to your software component. However, you can reuse existing properties of your software component for extending an existing object class, even if the object class belongs to a different software component.
When extending an existing object class, be sure to add a reference to the software component of this object class. Otherwise, your project will not compile.
The extend keyword must be used for denoting an extension, followed by the class keyword and the reference of the object class to be extended. Then the new properties that should be added to the object class can be defined or referenced inside the class block.
Syntax |
extend class reference { |
In the following example, object class FSCFOLIO@1.1001:Organisation is extended with a list of projects.
Example |
objmodel APPDUCXSAMPLE@200.200 class Project : CompoundObject { // FSCFOLIO@1.1001:Organisation is extended with the object pointer |
The extend keyword followed by the keyword property and a block holding the properties allows you to extend properties of other software components.
Syntax |
extend property reference { |
A common use case for this feature is to extend the allowed object classes in object pointer properties of other software components.
Example |
objmodel APPDUCXSAMPLE@200.200 |
With the extend keyword followed by the enum keyword and a block holding the enumeration items, you can add enumeration items to an existing enumeration type belonging to another software component.
Syntax |
extend enum reference { |
When extending an existing enumeration type, be sure to assign integer values for the new enumeration items that will not conflict with possible entries that might be added in a future version.
For example, the enumeration type FSCFOLIO@1.1001:DocState already contains enumeration items with the integer values of 10, 20, 30 and 40. A logical continuation of this sequence would include the integer values 50, 60, 70 and so. Therefore, you should avoid these integer values when extending the enumeration type.
Example |
objmodel APPDUCXSAMPLE@200.200 |
The extend keyword followed by the keyword struct and a block holding the properties allows you to extend compound types of other software components with properties belonging to your software component.
Syntax |
extend struct reference { |
When extending a compound type of another software component, reusing properties that belong to other software components is not permitted in the compound type.
Furthermore, be aware that compound types belonging to software component COOSYSTEM@1.1 cannot be extended.
Example |
objmodel APPDUCXSAMPLE@200.200 |
To define a connection between two classes, use the keyword relation. This relation can have 1:1, 1:n or n:m cardinality. A relation can have its own properties.
A special case is a “self-relation”, when the two related classes are identic.
Syntax |
// relation without properties // relation with properties //self-relation |
In order to facilitate the use of relations app.ducx implicitly defines the necessary attributes to have access from the relation instance to the class instances and from class instances to the relation instance. For the relation<ClassA, ClassB> Relation the implicitly defined attributes have the following names:
Name | Container | Type |
relation_classa | Relation | ClassA |
relation_classb | Relation | ClassB |
classa_relation | ClassA | Relation |
classb_relation | ClassB | Relation |
In case of the self-relation relation<Class> Relation:
Name | Container | Type |
relation_class1 | Relation | Class |
relation_class2 | Relation | Class |
class_relation | Class | Relation |
If ClassA is marked as multiple, then classb_relation is a list: instances of ClassB can have a set of Relation instances with different instances of ClassA.
Example |
objmodel APPDUCXSAMPLE@200.200 //1:n relation: //n:m relation: // self-relation |
For instance, assume at a company several employees can be hired. This can be modeled as a 1:n relation called Employee between the Company and Person class. In our case, the contractid and membersince properties could describe the Employee relation.
This chapter describes how to define an extend component object instances.
Using the instance keyword, you can define instances of component objects that become part of your software component. You can only define instances of object classes that are either directly or indirectly derived from COOSYSTEM@1.1:ComponentObject.
The instance keyword must be followed by the object class of which an instance should be created, and by the reference of the new instance. You can use generic assignment statements inside the instance block to assign values to the properties of the instance.
Syntax |
instance objectclass reference { |
For maintaining a well-organized project structure, it is recommended that you create a separate .ducx-om file for defining instances of component objects although – from a syntactic point of view – you could define all object model-related elements within a single .ducx-om file.
Example |
objmodel APPDUCXSAMPLE@200.200 // Definition and initialization of a document category // Definition and initialization of an active report |
Syntax |
extend instance <reference> { |
Using the extend instance keywords, it is possible to extend an existing component object.
The extend instance keywords must be followed by the reference of the component object to be extended. You can use generic assignment statements inside the extend instance block to assign values to the properties of the instance. The values are added to the current values of the extended object.
Example |
objmodel APPDUCXSAMPLE@200.200 // Exclude order and invoice objects from the list of objects allowed |
Syntax |
update instance <reference> { |
Using the update instance keywords, it is possible to update selected properties of an existing component object.
The update instance keywords must be followed by the reference or the object address of the component object to be updated. You can use generic assignment statements inside the update instance block to assign values to properties of the instance. The values of the referenced properties are replaced by the values inside the block.
Example |
objmodel APPDUCXSAMPLE@200.200 // Exclude exclusively order and invoice objects from the list of objects allowed |
In some rare cases it is necessary to define special type instances. This is done by creating an instance of a type class. Properties using this type can be declared by using a special syntax, specifying the property class and the new type.
It is even possible to declare a special property class to be able to specify new property attributes. This class has to be a subclass of an existing property class.
The software component NUMERATOR@1.1001 uses this method to define special property types for key numerators. See the sample code how to use these.
Note: Using proprietary types and property definition classes might result in unexpected behavior of the generic UI controls, so the general advice is not to use this feature unless you are really familiar with the existing object model.
Example |
objmodel APPDUCXSAMPLE@200.200 /* /* /* |
Well-Known Custom Property Classes
There are some property classes already specified in the type system, which can be used with an alias.
Password
Properties of the type password are handled like string properties, but are displayed with the password control. The AttrPrivateSet and the AtrPrivateGet actions are set automatically.
A loader object is a sequence of object descriptions to be created from a data input line consisting of unique columns.
Syntax |
loader reference { |
Columns can be references or strings.
The rest of the loader consists of loader object descriptions. These specify the object class, an optional class index, an optional name and class properties. If the create keyword is used, the object is created if not found.
This is followed by a list of property assignments. Each property assignment can have optional options before the assignment symbol.
Syntax |
[create] class [<index>] [as Name] { |
Value Assignments
The simplest assignment is just a property definition and a value. The assigned value can be any of the following types:
Aggregate Assignments
To create aggregate properties or aggregate list properties, the values of the aggregate properties have to be specified as assignments just like object descriptions.
Object Assignments
To create object pointer properties, a list of loader object definitions or loader object references can be used.
Loader object references are either the specified name or the object class with an optional index.