Prototypes

A prototype in FEF represents an abstract idea of an object type. Prototypes act like an interface and are designed to be implementation neutral and extensible. The most common type of prototype that a FEF developer will interact with is Data. FEF wraps raw data in a data prototype to provide functionality and to simplify coding. It is best practice in FEF to use the methods available to the developer through prototypes rather than dealing with raw objects. Data, along with other objects, must be contained in a prototype in order to render correctly within FEF components on the front end.

This page details the most commonly used prototypes that a FEF developer will come across while modifying data across the life cycle of a FEF application. Understanding how to create, modify, and retrieve data is a crucial aspect of scripting in FEF.

The following is a table that outlines the most commonly used prototypes in a FEF application. Go to each prototypes section for more information on each.

Prototype Purpose
Action A part of an actionSet - usually rendered on the screen as a button
Data All data in a FEF application is stored in a Data prototype. This prototype provides convenience methods to modify the data, retrieve the data, and create new data. FEF components render data that is contained in data prototypes
DataList DataList stores sets of data, such as datasets returned by a query or lists of embedded objects in your data design.
ResolverData Data that needs to be fetched from the server should be of type ResolverData Prototype. This tells the FEF application that the data needs to be resolved.
Design Designs structure the data in a FEF application. Designs can be created programmatically by the app developer for client side data or is implicitly loaded from the server for data on the Infor Nexus platform.
Field A field prototype wraps a field of an object.
Party Stores party objects in a FEF application.
Picklist A list that has a value and label. Can be displayed as a dropdown on the front end.
Pointer A root pointer is a type of field that references another root on the platform. On the UI, it is represented by a pointer component and through scripting it is represented by a Pointer object prototype.
Action

An Action is a part of an actionStrategy that is rendered on the screen depending on the component and the edit mode of the page. In FEF 0.9.11, buttons have been completely replaced with the idea of actions. Components have an actionSet, as well as the app overall has an actionSet. For example, the actionSet of your overall application can be obtained using

var appActionSet = Facade.Behaviors.DataCoordinator.actionStrategy().actionSet();
Your apps actionSet is , by default, the workflow actions that can be taken on the root data of the app. This can be overridden/customized - see here

A component, like a table, also has an actionSet

var myTable = Facade.Components.Table.forName(...)
var tableActionSet = myTable.dataCoordinator().actionStrategy().actionSet();

You can use the appendActions([array...]) function to append actions to the end of your actionSet or use the following function to completely override an actionSet

tableActionSet.setActions(function(behaviorFn){
    //default actionSet
    var actionSet = behaviorFn.resume();
    //logic
    //return an array of actions
    return actionSet;
});

Access a specific action using the .action() function

var action = myActionSet.action('<name of action>');

To apply a function to all actions of an actionSet, use defaultAction

var action = myActionSet.defaultAction();

Let's say we have a transition that we want to show a confirmation popover on button click before the transition executes - you can use the defineCapture to accomplish this. The following code snippet will show a popover with the set message once the action is clicked on the screen - the user will be able to cancel the action or continue, which will execute the action.

//Add confirmation prompt before action executes on server
action.defineCapture({
    captureConfig : {
        message : "Are you sure you want to execute this action?"
    }
});

To apply a callback function after an action executes successfully, use the setOnSuccess function.

action.setOnSuccess(function(bf, args){
    //access name of action that just executed
    var successfulAction = args.action;
});

Actions have the following functions:

Method Purpose
setActionType() Determines type of action - action types can be seen on the constants page
setEnabled() Determines the enable state of the button - return a boolean value indicating whether the action is enabled or disabled
setImportance(IMPORTANCE) Set the importance of an action. Possible parameters are either Facade.Constants.ActionImportance.PRIMARY,Facade.Constants.ActionImportance.SUPPORTING, or Facade.Constants.ActionImportance.DEEMPHASIZED. When an action has an importance of DEEMPHASIZED, it will render within the overflow menu of the actionSet's header component
defineCapture( captureConfig : object ) This can be used to intercept the action to provie further user consent to continue to process the action - example seen above
setExecute() Determines the function that runs when the action is triggered. If the action is represented on the screen as a button, this would be the onClick event of the button
setOnSuccess() A callback for after an action execute successfully. The name of the action can be access using the action variable of the second parameter - args.action
setPosition() Determines the position of the action
setRequiresSelection() Determines whether the action requires a selection within the component to be shown on the UI. For instance, a table action that performs an action based on the selection set of the table would require selection - return a boolean value
setVisible Determines whether the action should be rendered on the screen or not. Return true or false
Data

Data can be accessed programmatically through the DataRegistry or by retrieving the page's primary data.

//Get Page's Primary Data - returns a Data Prototype object
var primaryData = Facade.PageRegistry.getPrimaryData();
//Get data from DataRegistry - returns a Data Prototype object
var myData = Facade.DataRegistry.get('someData');

Data Prototype objects provide some convenience methods, as listed in the below table. It is bad practice to modify raw data within FEF for a couple of reasons; the most important being that FEF cannot track whether the data is dirty or not if it is modified in its raw form. Keep data within its Data Prototype to avoid frusteration; i.e., access a field of your data by using .get() as seen in code snippet above instead of attempting to modify the raw data of the prototype.

Method Purpose
getDesign() This will return the design of your data. The object returned will be a Design Prototype.
getId() Gets the instance ID if the data represents an instance of an object on the Infor Nexus platform.
getRaw() Gets the raw data from the Data Prototype. It is NOT best practice to use this method, but it can be useful while debugging your FEF application to see the raw data in your application.
getResolveStatus() Returns the resolveStatus of the data. The resolveStatus can be useful to use when the data is resolving from the server. This will return RESOLVED when your data has been returned from server and is thus resolved.
getType() All data should have a type. This method will return the type of the data. If the data is instance data, the type will be the global identifier of the object.
isDirty() Returns a boolean depending on whether the data is dirty or not. Dirty data means the data on the client has been modified and does not directly reflect the server data.
isList() Returns a boolean whether the data is a list or not.
isDraft() Returns a boolean which reflects whether the data is a draft or not
isMap() Returns a boolean whether the data is a map or not.
isNew() Returns a boolean whether the data is new or not. New data means that the data does not yet have a unique id, or UID, on the system.
hasDraft(name) Returns a boolean that reflects whether the data has a draft named with by the name parameter
resetClean() This method will reset the data to its clean state; meaning that any client side modification that caused the data to be dirty will be reverted.

Get/Set Field of Data

To get a field of your data, use the get('fieldName') method. In the below example, let's say we have a field on our primary data called startDate. To get the value of startDate programmatically within FEF, do the following:

var data = Facade.PageRegistry.getPrimaryData();
var startDate = data.get('startDate');

To set a field in your data, use the set method. The set method has two parameters: the first is the field in your data design that you are setting and the second is the value which you are setting the field to. In the following example, set a field named comments to a string value.

var data = Facade.PageRegistry.getPrimaryData();
data.set('comments', 'Some random comments');

Get/Set Root Pointer

From a data store, use the getPointer() function to get a root pointer from there. From there, use the following functions in the example to get root pointer fields.

var data = Facade.PageRegistry.getPrimaryData();
//Get root pointer field
var pointer = data.getPointer('<pointerField>');
//get root pointer ID
pointer.getTargetId()
//get root pointer label
pointer.getLabel()
//get root pointer type
pointer.getTargetType();
//set root pointer label
pointer.setLabel('<label>')
//set root pointer Id
pointer.setTargetId('<id>');

Party Data Functions

From a data store, you use the regular .get() function to get a party field. You can use the following fields to get further information about a party field

var data = Facade.PageRegistry.getPrimaryData();
//Get party field
var buyer = data.get('myBuyer');
//Get Buyer Org Id
buyer.getOrganizationId();
//Get Buyer name
buyer.getName();
//Get Party role
buyer.getRole();
//Get org address
buyer.getAddress1();

Create/Register Data

Client side data can be created within a FEF application by creating a new Data Prototype and then registering that data in your DataRegistry. Once the data is registered in the DataRegistry, it can be displayed on the UI by adding a data property to a core-component, such as a core-form , core-section, core-table, etc. When data is created, it is best practice to give it a type. The type indicates what design the data has; the design determines how the data is structured. If you create client side data with a certain type, make sure that type is also registered in your DesignRegistry to structure the data to fit your needs.

Below, we create some client side data and a client side design that will tell the UI how to render the data.

//Register a client design in your design registry
var clientDesign = Facade.DesignRegistry.register(
                new Facade.Prototypes.Design(undefined, { designType : 'myDataType' } );
//Add a text field and a picklist field to your design
clientDesign.addField('textField').setFunctionalType(Facade.Constants.FunctionalType.TEXT);
clientDesign.addField('picklist').setFunctionalType(Facade.Constants.FunctionalType.PICKLIST);

//Create data
var newData = new Facade.Prototypes.Data({
    picklist : 'picklistValue' ,
    textField : 'Default value'
    }, { type : 'myDataType' });
//Register your data
Facade.DataRegistry.register('newData' , newData);

Create a New Object Instance

Creating a new object instance in FEF is as simple as creating a new Data prototype with an isNew=true parameter and then persisting the data to the server. This will automatically create a new object and save it to the server. Use the following code as an example to create a new object of type $SurveyS1.
//Define the data of your new object
var payload = { ... };
    
var newObject = new Facade.Prototypes.Data(payload, 
                                            { type : '$SurveyS1' , isNew : true });

//Save the object - if you have the isNew flag set, it will create a new object
Facade.Resolver.persist(newObject).then(function(obj){
    //Get the unique identifier(UID) of your newly created object
    var id = obj.getId();
});
DataList

A DataList is an array of Data prototype objects. Lists in your design, such as line item lists or lists of embedded objects, are represented in FEF as DataLists.

Method Purpose
clearResults() Remove all of the data from your dataList.
forEach(function()) Replaces a for and for-in loop for iterating over lists of Data within FEF. See below for usage
getLength() Get the length of your datalist. Important to note that if your list has buffered data , i.e. if the list is defined from a query returned from the sever, the length value will be of the remote data set and not of the data retrieved in the buffer
insert(idx,data) Add data to a dataList at a specific index by passing a Data Prototype and an index into a dataList's insertResult function.You can also use the deprecated insertResult() function, which does the same thing.
push(data) Add data to a datalist by passing a Data Prototype into a datalist's pushResult function. You can also use the deprecated pushResult function, which does the same thing.
transformList({ filter : function() } ) Filters a list. From within the anonymous function, return true to include the list item in your list or return false to remove an item from your list. See example below for more information

Iterate over a Data List

Here, we iterate over a datalist using the forEach method. The anonymous function within the forEach function has two parameters: item and index. Item is the piece of data at a specific index. Item is either a primitive for a list of strings,numbers,etc. or an object if it is a list of objects. Index, the second parameter, is the index of the list. Iteration can be halted if you return a truthy value from the anonymous function. In the example below, we iterate through a list with the intention of removing a string end. Once we remove this string from the list we will halt iteration.

NOTE: Do not use a regular for loop to iterate over a datalist. When dealing with remote data that could be buffering from the server, there will holes in the iteration. To avoid these potential pitfalls, use the below forEach loop example
var myDataList = Facade.DataRegistry.get('myObjectList');
var removeIndx;                        
myDataList.forEach(function(item,indx){
    if(item == 'end'){
        removeIndx = indx;
        //halt iteration
        return true;
    }
});
myDataList.remove(removeIndx);

Creating Data Lists

Here, we can create a client side data list. Similar to a Data Prototype, it is best practice to give your DataList a type.

//Create datalist prototype
var dataList = new Facade.Prototypes.DataList( [] , { type : 'myDataListType' });
//Add data to your dataList
dataList.push(new Facade.Prototypes.Data( {
    dataField : 'some value' ,
    anotherField : 'another value'
});
//register your datalist
Facade.DataRegistry.register('myDataList' , dataList);

Filter a Query's Result Set

In order to filter a query's result set in your FEF client side code, use the transformList function. This allows you to add a filter function that will be applied to each item in your list. Within the function, either return true to keep the item in the list or false to remove the item. Refer to the following example to help illustrate this technique. Here, we will search for a type of object and then we will remove every object that has a field priority with a value of 0 from the list.
//Search for a type of object
Facade.Builders.Query(OBJECT_TYPE)
    .param('oql' ,"status='active'")
    .onSuccess(function(results){
        var filteredList = results.transformList({
            filter : function(item){
                //remove items with priority of 0
                if(item.get('priority') == 0 )
                    return false;
                else
                    return true;
            }
        })
        Facade.DataRegistry.register('filteredList' , filteredList );
    })
    .execute({ sessionManaged : true });

In the html markup, back a table with your filteredList

<core-table data="filteredList">
    ...
<core-table>
ResolverData

ResolverData is a type of data that has a resolver() function that resolves data from the server. ResolverData inherits all the functions of a Data and DataList prototypes. The methods below are most commonly used with ResolverData

Method Purpose
resolve(resolverOptions) Executes the resolver() function of your data. ResolverData will not attempt to fetch from the server if it has already been resolved. To force the ResolverData to communicate with the server everytime, pass in resolverOnServer : true within the resolverOption parameter object
getResolveStatus() Returns the resolve state of your data. It will return either UNRESOLVED , RESOLVING , or RESOLVED

When your FEF application needs to fetch data from the server, you can use ResolverData to do so. ResolverData signals to the UI that data is expected and once it is processed and returned by the server it is displayed.

Here, we have an example of a table that displays data that is fetched asynchronously from the server. It is best practice to add your data's type. The resolver field function returns a promise.

Facade.Components.Table.forName('dataDisplayingResolverData').setData(
    new Facade.Prototypes.ResolverData( undefined , {
        type : '$ObjectTypeQ1',
        isList : true,
        resolver : function(){
            return Facade.Resolver.query('$ObjectTypeQ1', {
                    params : {
                        oql : 'status="active"'
                    }
                }
            )
        }
    })
)
<core-table name="dataDisplayingResolverData">
    <core-field field="fieldOfData"></core-field>
    ...
</core-table>

Let's say we want to resolve our data on click of a button in our application and we also want to indicate to the user the status of our resolverData. Do something like the following. Set up your data as resolverData in the DataRegistry and resolve the data every click of the button. To show the user the status of the data, set the label of a core-text component to your data's getResolveStatus function

Facade.Behaviors.App.onLoad(function(){
    var resolverData = new Facade.Prototypes.ResolverData(undefined, {
        type : 'User',
        isList : true,
        resolver : function(){
            //return a promise - this is your query that will resolve into your data
            return Facade.Resolver.query(...);
        }
    });
    //Register data in your DataRegistry
    Facade.DataRegistry.register('loadingData' , resolverData)
});
//Set your text field to return the getResolveStatus of your data
Facade.FunctionRegistry.register('myText', function(){
    var dataToCheck = Facade.DataRegistry.get('loadingData');
    if(dataToCheck){
        return dataToCheck.getResolveStatus();
    }
    return 'No Data';
})
Facade.Components.Button.forName('myBtn1').setOnClick(function(b){
    var resolverData = Facade.DataRegistry.get('loadingData');
    resolverData.resolve({ resolveOnServer : true });
});
....
<core-text label-fn="myText"></core-text>
...
<core-button name="myBtn1" label="Resolve My Data"></core-button>
...             

Using Resolver Data's Correlation Key

Another useful function of the ResolverData is the correlationKey property. The correlationKey specifies when ResolverData resolves; that is, when the correlationKey changes, the ResolverData's resolver function will be triggered. The correlationKey property is a function where a string is returned. This can be helpful when you want to send queries that depend on other data in the application. When the user changes the data, you can use the correlationKey pattern to re-query against the server.

Here is an example of how to use a correlation key. In the following scenario, we send a query depending on a field on our primary data. When the field value changes, the resolver function of the resolverData would re query against the server

Facade.DataRegistry.register('correlatedActions' , new Facade.Prototypes.ResolverData(undefined , {
        resolver : function(ro){
                var val = Facade.PageRegistry.getPrimaryData().get('fieldX');
                return Facade.Builders.Query(IDENTIFIER)
                    .registerAs('identifier' + val )
                    .param("oql","value contains '" + val + "'")
                    .execute({ retryOnDisconnect : true , sessionManaged : true });
        },
        correlationKey : function(){
            return Facade.PageRegistry.getPrimaryData().get('fieldX');
        }
    }));
Design

A Design prototype describes how a specific kind of data is modeled in a FEF application. Design Prototypes are to be registered in the DesignRegistry. Any object that is loaded into your FEF application will have a design registered in the DesignRegistry implicitly. You can access a design of an object with the global identifier $ObjectType by its identifier as a key in the DesignRegistry, like so:

var design = Facade.DesignRegistry.get('$ObjectType');

Design prototypes have the following methods that can be accessed programmatically within a FEF application.

Method Purpose
addField(str) Add a field to your design. Append a setFunctionalType method to your newly added field to give it a field type
design.addField('textField')
    .setFunctionalType(Facade.Constants.FunctionalType.TEXT)
addVirtualField(str, fnct, fnct) Add a virtual field to a design. Virtual fields are fields that have data that depends on other aspects of your FEF application. An example of a virtual field is below this table.
getDesignType() Get the type of the design.
getField(str) Get a field of your design by its name. This will return a Field Prototype.
getFields() Returns an array of fields in a design.

Add a Virtual Field

Here, we add a virtual field to a design. We demonstrate read/write virtual fields here with targetDuration. Its value is calculated to be the number of days between the startDate and targetEndDate. Only if both values are set does it return a value. The setter also is functional: whatever value is set is added to the startDate to become the targetEndDate.

projectDesign.addVirtualField("targetDuration",function(){
		var startDate = this.get("startDate")
		var endDate = this.get("targetEndDate")
		if (startDate && endDate) {
			return (Date.parse(endDate)
               - Date.parse(startDate)) / 1000 / 60 / 60 / 24
		}
		return undefined
	},function(value){
		if (isNaN(value)) {
			return
		}
		var startDate = this.get("startDate")
		if (startDate) {
			var endTime = Date.parse(startDate) + value * 24 * 60 * 60 * 1000
			var endDate = new Date(endTime)
			this.set("targetEndDate",endDate.getFullYear() + "/" +
   (endDate.getMonth() + 1 < 10 ? "0" : "") + (endDate.getMonth() + 1)
   + "/" + (endDate.getDate() < 10 ? "0" : "") + endDate.getDate())
		}
}).setFunctionalType(Facade.Constants.FunctionalType.INTEGER)
Field

Field Prototypes make up a design. Here, we iterate over the fields of a design.

var design = Facade.DesignRegistry.get('$ObjectType');
var fields = design.getFields();
for(var i = 0; i < fields.length; i++){
    var field = fields[i];
    ...
}

The following methods can be used for Field prototypes.

Method Purpose
addValidation(function) Add a validation to your field.
getFieldMask() Get the mask of the field as defined by the design. The possible values will be NORMAL, READONLY, and HIDDEN.
getFunctionalType() Get functional type of this field as defined by the design.
getIsRequired() Returns a boolean whether the field is required or not.
getValidations() Get any validations on the field. Will return an array of validations.
isPrimitive Returns a boolean whether or not the field is a primitive value or not.
Party

Party data is wrapped in a Party Prototype within a FEF application.

The following methods are available on Party Prototypes.

MethodPurpose
getAddress1() Get first line of your party's address - which usually contains the street and street number of your party's address.
getFullAddress() Get full address of your party.
getCity() Get city of your party.
getCountry() Get country of your party.
Picklist

Picklist's within a FEF application are of type Picklist prototype and need to be registered in the Picklist Registry. Here is an example of a client side picklist that is initialized, then registered in the Picklist Registry, and then displayed on the front end as a dropdown on a field.

Facade.PicklistRegistry.register("sectionPicklist",new Facade.Prototypes.Picklist(
        [ {value: "rowType1", label: "Section 1"},
          {value: "rowType2", label: "Section 2"},
          {value: "rowType3", label: "Section 3"}]));

//Tell a field to render as a picklist
Facade.Components.Field.forName('pickListField')
    .setValueType(Facade.Constants.FieldComponentType.PICKLIST);

//Add your picklist to a field
Facade.Components.Field.forName('pickListField').setPickType('sectionPicklist');

Now, on your template html - just display a field. The field will render as a picklist because that's what its value type is and will use the picklist that we just registered in the Picklist Registry.

<core-form>
    ...
    <core-field field="..." name="pickListField"></core-field
    ...
</core-form>
Pointer

Access a pointer within a data root using the getPointer function

//Access root pointer field myPointer of your primary data root
Facade.Behaviors.App.onLoad(function(){
    var primaryData = Facade.PageRegistry.getPrimaryData();
    var myPointerProto = primaryData.getPointer('myPointer');
});

The following convenience functions are available on a pointer prototype

Method Purpose
assignTo(target) Set a root pointer which references the target object - the target parameter being an instance data object
getLabel() Returns the reference of a root pointer; which is used as the label for a root pointer on the UI
getTarget() Returns the root object that is referenced by the root pointer. If it is not defined in the Data Registry, it will be fetched through the API
getTargetId() Returns the rootId of the root pointer, which is the uid of the root object on the platform
getTargetIndex() Returns the index within the DataRegistry of the root object referenced by the root pointer
getTargetType() Returns the global type of the root pointer
isSet() Returns true if the root pointer is set and false elsewise
setLabel(label) Set the label of the root pointer