A composite input is an input that can take an entire entity, instead of just a single attribute. (We recommend reviewing the Exaptive Data Model, if the terms entity and value are foreign.) Composite inputs and their configuration options are important for:

  • freeing or constraining the DATAFLOW when working with your component;
  • limiting the size of a component in the dataflow because of many ports, where that is an issue; and
  • controlling component triggers and reset timing.

This article will walk through how to create composite inputs and how your choices affect these considerations. Below that there are some examples of composite input uses. 




How to Create a Composite Input


Creating a composite input is similar to the process of adding any other input port to a component. In the Component Editor's Inputs tab, open an existing port to edit it or add a port:



blob1477405255293.png

Now, let's name it. This is important. This will be your function name in the script of your component. As an example, I've created a component that takes in data about a user's location. So I name it userLocation. Then I assigned it a value type to take: entity<string firstName, string lastName, entity location>, and I gave it a default value. 


Here's what that looks like in the Component Editor:


NOTE: The default values look like a Python dictionary or JavaScript object. This is how an entity in the Exaptive Data Model looks. 


So this is a composite input. It takes in an entire entity - a group of attributes - instead of just a single attribute, like firstName.



Granular Inputs or Not?


Next, you've got a choice about whether to "SHOW GRANULAR INPUTS" or not. Will you expose each attribute in the entity to the xap builder as an individual input port or just the entire entity? 


Without granular inputs visible the component looks like this in the DATAFLOW: 


With granular inputs exposed it will look like this:
 



So what's the difference? As you'll see in the examples a little further down, exposing granular inputs gives the xap builder more control over how the data is assembled in the xap. But it can make for a busy looking component if there are a lot of other inputs. 


NOTE: Exposing granular inputs doesn't prevent the xap builder from assembling the entity and passing it to the component as a whole. They just pass the entity to the "userLocation" input, instead of using the granular inputs below it.  



How Your Component Script Uses the Composite Input's Data (the Entity)


The data is being passed into your component as an entity and upon export is converted to your component's native equivalent. Here's an example accessing the entity in Python: 

 

def userLocation(self):
#assign the export of all inputstate
input_state = self.api.inputstate.export()
# assign the entity that represents the composite inputs data, now exported to a python dictionary
user_location = input_state["userLocation"]
# access the value for firstName from the exported entity
first_name = user_location["firstName"]
# access the value for lastName
last_name = user_location["lastName"]
#access the value for location 
location = user_location["location"]

 

Here's what it would look like in a Javascript component:


 

userLocation() {
    var inputState = this.api.inputState.export();

    var userLocation = inputState.userLocation;

    var firstName = userLocation.firstName,
        lastName = userLocation.lastName,
        location = userLocation.location;    
}

 

Here's what it would like for an R component:


userLocation <- function(state){
    inputState <- v_export(state$api$inputstate)
    userLocation <- inputState$userLocation

    firstName <- userLocation$firstName
    lastName <- userLocation$lastName
    location <- userLocation$location
}


The data structure looks like this: 

  

"input_state": {
"userLocation":{
"firstName": "Mike", 
"lastName": "Smith", 
"location": {
"lat": 37, 
"lng": 52
}
}
}

  

 



Examples 


So let's go through some examples to see how composite inputs, with and without exposing the granularity, get used to address xap-timing challenges. 


Without Granular Inputs


Without granular inputs, the xap builder will likely have to use merge gates to control the flow of data and timing of triggering your Component. (The only situation they would not have to use a merge gate is one where the xap builder has a component that outputs an entity that happens to match the expected entity for your component with a composite input.)


Let's take the same "userLocation" example. Assume all potential last names are unique, but some users may share the same first name. Location can be updated multiple times for the same user, but updating occurs in an unpredictable pattern. Without granularity, we have an input that looks like this: 


The xap builder must construct the entity with all of the relevant data prior to sending it to the input. They can do this using a merge gate, like this: 

The merge gate allows the xap builder to assemble the entity in the proper structure and to design the specifics of when the output is triggered. By default the condition for the merge gate to trigger is true, meaning it will be triggered any time a new input is received. But they only want it to trigger when it has all of the data, and when it's the right data. To do that, they use the timeOf() function (learn more about merge gates here) like this: 

Now, the component will only trigger when all inputs have received a value. So, to some degree this solves the timing issue. But what about if the user being updated changes and the first value received is the first name? Now the merge gate will trigger with the new first name, but the old last name and location. To solve this, the xap builder can reset inputs either on receipt of another input or on output. A good starting point is resetting location on output. Clicking on the x2 input opens that input: 

 

Further, clicking on the reset icon opens reset options. 


Here, they've checked the box for "Reset on Output". Now, each time the merge gate triggers (based on the condition set) this input will be cleared. Now if a new first name or last name is received the merge gate won't trigger until a new location is also received. 


The last thing the xap builder can do to handle timing is "Reset on Input". In this case, if a new first name is received and a new location is received we don't want the merge gate to trigger with the old last name. They're able to add a list of all inputs we want to reset when an input is received, like this: 

In this case they only want to reset the last name, so "x1" is the only input reset. 



Example with Granular Inputs


The same thing happens more or less with Granular Inputs, but you control it in the Component Script, instead of the xap builder with a Merge Gate. 


Here's the component from the above example with the Granular Inputs exposed: 

Note there is an input called "userLocation", which is identical to the input used in the above example. This input takes an entity that's been preassembled, just like in the above example. Now, though, there is an input for each property. These can be used to assemble the entity on the component, rather than in an external merge gate. 


Wiring the same outputs from the above example into this component looks like this: 

Now, the same issue presents itself around timing of triggering the Python component. In the previous example the component was triggered each time the input was received based on the assumption the data was properly assembled when it arrived. Now, this is handled in the component using merge logic. 


Merge logic let's Xap builders design when the component should trigger, just like the merge gate in the previous example. Opening the mergeLogic input you see all of the options available in a merge gate are available here: 


To achieve the same effect as the previous example, we make the same changes. Here's the code snippet: 

 

{
  "condition": "timeOf('firstName') && timeOf('lastName') && timeOf('location')", 
  "resetOnOutput": [
    "location"
  ], 
  "resetOnInput": {
    "firstName": [
      "lastName"
    ]
  }, 
  "debug": false
}

 

So, the condition is unchanged. I want the component to only trigger if there is a value for all three inputs, remember that by default it triggers any time there's a new value for any of the inputs. I've set the location input to be reset any time the component triggers. "resetOnInput" is less intuitive, so let's break it down. "resetOnInput" is an entity that takes key value pairs where the key is the name of an input and the value is a list of the inputs that should be reset when that input is received. So, in the above snippet when the firstName input is received the lastName input will be cleared.