Content


Models

A model is a basic structure of representation and data modeling for a Mat|r application.
The syntax to define a model is by using the reserved word: Model.
A model permits the storage of a set of data of different type, such as simple types (characters, integers, floating point numbers) and compound types (other models, Array, Map).
Each piece of data or element stored inside a model is called an attribute.

Example of model with simple type of data:

Model Person {
	String name
	Double height
	Integer age
	Bool hasPartner
	Date birthday
}

Example of models with compound type of data:

Model Class {
	Array<Person> students
	Person teacher
}


Experiences

Experiences are models for which a visual representation is auto-generated. This representation is called Layoutand it allows us to obtain input from the user, execute rules that modify model and display the result.

Let’s analyse the following example:

Experience Profile {
	String name label("Enter username") as TextField
	Decision save action('context.rule1') label('Save')
}

We can see that the attributes of an experience admit modifiers (as TextField in the example) that indicate which visual component will be generated in the layout of the experience. And, in turn, the attribute will be bound to the visual component. In other words, once the textfield is edited, the value of the attribute will be updated in the underlying model and after editing the model, the change will be reflected in the TextField component.

The resulting layout of this example is:


Layouts in UIBuilder and binding configuration

Through UIBuilder, it is possible to preview, generate and set up the appearance of the graphic representations or layouts. The edition of graphic properties can be done in two different ways: through the property panel or by editing theXML del Layout.
Below, we can see the XML generated for the experience of the previous example:

<layout alpha="1" backgroundColor="#eeeff1" fontFamily="Roboto, sans-serif" paddingRight="18" paddingTop="18" paddingBottom="18" paddingLeft="18" name="Profile">
    <datasource type="Profile" name="profile"/>
    <navigationBar title="Profile" hideBackButton="false">
        <leftButtons/>
        <rightButtons/>
    </navigationBar>
    <textfield wpercentage="1" borderColor="#cccccc" borderWidth="1" cornerRadius="3" alpha="1" fontFamily="Roboto, sans-serif" fontSize="14" marginTop="0" marginBottom="8" marginLeft="0" marginRight="0" paddingRight="8" paddingTop="4" paddingBottom="4" paddingLeft="8" placeholder="Enter username" bind="profile.name"/>
    <button wpercentage="1" textAlignment="center" cornerRadius="2" alpha="1" backgroundColor="#a9b4ba" fontSize="15" marginTop="4" marginBottom="8" paddingRight="8" paddingTop="8" paddingBottom="8" paddingLeft="8" textColor="#ffffff" text="Save">
        <selectedState/>
        <highlightedState/>
        <event on="TapEvent">
            <action type="RunRuleAction" ruleContext="context" ruleName="rule1"/>
        </event>
    </button>
</layout>

An important property to highlight within the XML of the layout definition is the entry.

<datasource type="Profile" name="profile"/>

Here, it is indicated that the Model which will be bound to the layout is of Perfil type. Therefore, in order to view or surf this experience with a navigation statement broker.ui.push(), it is necessary to use the name of the layout as well as the model to be bound. We can also leave out the parameter of the model to be bound and an instance of the model will be auto-generated.

Example of programmatical visualization of the layout:

Application {

    OnInit {
    	Profile p = Profile()
        broker.ui.push("Profile", p)        

        //which is equivalent to do:
		broker.ui.push("Profile")        
    }
}


Decision

It is a unique and fundamental clause of an experience that generates a button on the screen without an attribute bound in the underlying model. It allows the user to set up the execution of a rule in the tap event of the button. Through the action modifier, the context and the rule to be executed are defined and the label tag defines the text of the button.

Experience Form {
	String name as TextField
	String adddress as TextField
	Decision saveBtn action("ruleContext.saveData") label("Save")
}


Automatic generation of the UI of the experience.

The syntax to define an attribute that is bound to a visual component is the following:

DataType attributeName [as VisualComponent]

Below, you can see the list of all the data types, with the visual components that can be set up:

Visual Component Description Type of data supported Sentence example
TextField Text field editable by the user. String, Integer, Double, Date String userName as TextField
Label Text field not editable by the user. String, Integer, Double, Date String userNameTittle as Label
Image Component that permits the rendering of an image through a URL. String Image profileImage as Image
Checkbox Component that permits the visualization of the status of a boolean variable. Bool Bool includeAttachment as Checkbox
Select Component that permits the visualization and selection of a list of options. In case the binding is done with a group of models, it is necessary to use templates. Array<String>, Array<Integer>, Array<Double>, Array<Date>, Array<Model> with templates Array<String> selectCountry as Select
List Component to list a group of values. In case the binding is done with a group of models, it is necesary to use templates. Array<String>, Array<Integer>, Array<Double>, Array<Date>, Array<Model> with templates Array<String> selectCountry as Select
Video Component that permits the visualization of a video through a URL. String String videoTutorial as Video
PDF Component that permits the visualization of a PDF file through a URL. String String videoTutorial as Video
MapView Component that permits the visualization of a group of geolocated points together with its information of interest. See model Marker. Array<Marker> Array<Marker> nearPlaces as MapView


Events of the experiences

When defining an experience, we can add code blocks that will be executed when life-cycle events of the experience are triggered. Therefore, it is possible to add logic at the moment an experience is created, when it can be viewed or before its destruction.

Experience Perfil {
	OnCreate {
		//When the experience is created through a push or present navigation statement for the first time.
	}

	OnResume {
		//Whenever the experience can be viewed, i.e it reaches the top of the navigation stack
	}

	OnDestroy {
		//before its destruction
	}
}


Application Clause and Application Events

As the experience has a life cycle and we can execute statements in its events, within the Application clause we can add code blocks to be executed for the different events in the life cycle of the application. Moreover, in this section it is possible to declare variables of global scope.

Application {
	Bool appIsInBackground = false //Global variable can be accessed from all the app's code blocks

	OnInit {
		//Executed at application startup
	}

	OnResume {
    //executed when application's state goes to foreground
		appIsInBackground = false
	}

	OnEnterBackground {
    //executed just before application's state goes to background
		appIsInBackground = true
	}
}


Rules and Contexts

The rules in Mat|r constitute the way to group code statements to be executed as a reaction to different events. These code statements are defined by the key word Rule and have to be declared within a context that group them together.

The syntax of a rule is the following:

Rule ruleName [listen clause] [when clause] {
	[rule body]
}

Events that trigger the execution of a rule:

  • Tap a button on the screen by interaction of the user Decision.
  • Tap a visual component, to which an action of rule execution was bound from Configuración de Eventos en UIBuilder.
  • By start-up of a listen: the model and/or attribute which the rule is observing was modified.


Listen Clause

The listen clause allows the user to configure a rule, so that it can observe one or more attributes or chain of attributes of a model and runs if any of its values changes.

The syntax of a rule with listen clause is the following:

listen([* | attributeName1 [, attributeName2]*] from model as varName):
  • attributeName: name of the attribute to be observed. The * character can be used to observe all the attributes of the model.
  • model: name of the model that contains the attribute to be observed.
  • varName: name of the local variable that will be used within the body of the rule to specify the instance of the model whose attribute was modified.

In the following example, we can see how temperature conversion can be modelled. When the user updates the value of the celsius attribute, through the edition of the first TextField of the experience TemperatureConverter, the rule updateKelvin will be executed.

Experience TemperatureConverter {
   Double celsius as TextField
   Double kelvin as TextField
}

RuleContext UpdateRules {
   Rule updateKelvin listen(celsius from TemperatureConverter as ct) {
      ct.kelvin =  ct.celsius + 273,15
   }

   Rule updateCelsius listen(kelvin from TemperatureConverter as ct) {
      ct.celsius =  ct.kelvin - 273,15
   }
}


When Clause

The when clause allows the user to add conditions for the execution of the rule, so that they can be assessed and, depending on the result, the rule will be executed or not.

The syntax of a rule with when clause is the following:

Rule ruleName when (boolean expression) [&& [(boolean expression)] | [ || [(boolean expression)]

Notice that the expressions to be assessed can be combined with the logical operators and && and or ||.

If we go back to the example of temperature conversion, we add a boolean attribute, tied to a checkbox component. We then write a new rule in charge of persisting the value of the temperature in celsius, validating if the persistence checkbox is activated under the condition that the value is other than null.

Experience TemperatureConverter {
   Double celsius value(0) as TextField
   Double kelvin value(0) as TextField
   Bool persistSample as Checkbox
}

RuleContext PersistenceRules {
   Rule persistCelsiusSample listen(* from TemperatureConverter as ct) when ct.persistSample && ct.celsius != null {
       broker.localPersistence.save("lastCelsiusSample", ct.celsius)   
   }
}


Execution order and listen behaviour

During the execution of the body of a rule A, it is possible that the attribute of a model which is being observed by another rule B is modified (listen clause). At that time, the execution of rule A will be interrupted and the execution of rule B will take place, and, in turn, rule A will complete its execution once rule B is finished. This nested behaviour applies to rule B once again, triggering the serial execution of many other rules, before rule A -which started the process- completes its execution.

There is an exception to the behaviour described above: when, within a rule, the attribute of a model that observes the same rule is modified, the execution of the rule is not interrupted. In other words, a rule cannot trigger its own interruption.

Execution order in chains of attributes

When the user changes the value in an observed chain of attributes, the rules will be executed in relation to the trajectory of the chain, triggering the execution of rules that observe changes from the first attribute on the right, to the last one on the left. In case attributes of equal depth are affected, the order in which the rules were declared is considered.

In the following example, you can see the models:

Model Coordinates {
    Double lat
    Double long
}

Model Country {
    Coordinates coors
}

Model State {
    Coordinates coors
}

Model User {
    Country country
}

And rules with different depths of chains of attributes to be observed:

Rule changeUserCoordinates { 
        //global variable 'user' of type User
        user.country.coors = Coordinates(lat: -32.88, long: -68,8)
}

Rule listen_lat_from_coors listen (lat from Coordinates as l) {
	//... rule body observing changes of 'lat' attribute of models type Coordinates
}

Rule listen_long_from_coors listen (long from Coordinates as l) {
	//... rule body observing changes of 'long' attribute of models type Coordinates
}

Rule listen_coors_from_country listen (coors from Country as c) {
	//... rule body observing changes of 'coors' attribute of models type Country
}

Rule listen_country_coors_lat_from_user listen (country.coors.lat from User as u) {
	//... rule body observing changes of 'country.coors.lat' chain of attributes of models type Country
}

Thus, the execution of the rule changeUserCoordinates, will trigger the execution of the following rules in this order:

  1. Coordinates Model, rule listen_lat_from_coors
  2. Coordinates Model, rule listen_long_from_coors
  3. Country Model, rule listen_coors_from_country
  4. User Model, rule listen_country_coors_lat_from_user