2.4 | The Cubbles Javascript API
Purpose
To present how to interact with a Cubbles
component, using public methods, events and initialization.
Prerequisites
- The com.incowia.basic-html-components@1.0/cubx-textarea should be available. (You can find it at the sandbox store)
- You are familiarized with the creation of elementary components
- You are familiarized with the 2.32 | The Cubbles Tag API
Sample component
In order to make it easier to explain an understand how to interact with a component, we will use use the elementary component called cubx-textarea
, whose webpackge-id id com.incowia.basic-html-components@1.0/cubx-textarea, along the following sections. This component wraps a <textarea>
html element to enable its use as component within the Cubbles platform, allowing some of the html attributes of a textarea to be available as slots. The interface view for this component looks as follows:
Interacting with Cubbles from the outside
The Cubbles Platform offers you a set of methods which allow you to interact with a Cubble (Compound or Elementary component instance) from outside the component, e.g. from a script associated to the page where the cubble is attached. Below, these methods are presented and explained using the cubx-textarea
component:
Methods for outside interaction
The methods to interact with a cubble are generated for each slot of a component. Their names, except for slots method, are composed by a prefix related to the method. followed by the slot's name starting with capital letter, so for example the get method for a slot called value would be setValue(). The following table presents and explains the use of these methods:
Method name convention | Description |
---|---|
get<Slotid> () | Get the current internal value of the slot |
set<Slotid> (value) | Set the internal value of the slot. If the slot is an output slot, the value will be propagated afterwards. |
repropagate<Slotid> () | Trigger the propagation of the current internal value of the slot. This method is available for output slots. Note that when the internal value of a slots changes, an automatic propagation of this value is triggered. So this method is useful when you need to "force" this propagation at any time. |
slots () | Return an array containing the definitions of all slots of the component. |
addDynamicConnection (dynamicConnection) | Add a dynamic connection to the component. (See Dynamic connections section) |
Using the methods
Now in order to use these methods you should have our component already working, to aim that you might have an html page like the one shown below:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title><cubx-textarea></title> <script src="https://cubbles.world/sandbox/cubx.core.rte@2.3.0/webcomponents-lite/webcomponents-lite.js"></script> <script src="https://cubbles.world/sandbox/cubx.core.rte@2.3.0/crc-loader/js/main.js" data-crcinit-loadcif="true"></script> </head> <body> <cubx-textarea cubx-webpackage-id="com.incowia.basic-html-components@1.0"></cubx-textarea> </body> </html>
Now to interact with the component, first we need to retrieve it from DOM within a script:
... <script> (function(){ 'use strict'; // Get the component from DOM var cubxTextarea = document.querySelector('cubx-textarea'); ... })() </script> ...
Then, we should wait until the component is ready to interact:
... <script> ... // Wait until the component is ready to be used document.addEventListener('cifReady', function() { // Interact with the component using the methods presented above cubxTextarea.setId('myTextArea'); ... var textareaId = cubxTextarea.getId(); ... }); })() </script> ...
A working example
The code below follow the indications presented above using our cubx-textarea
component to:
- Get the component from DOM
- Set some of its slots' values
- Get those slots' values
- Get the list of slots' definitions of our component
- Show the slots values and the whole list of slots within the {{cubx-textarea}}
Intercepting changes on output slots
Sometimes it could be useful for you to detect when the value of an output slot has changed and get this new value, to aim that you just need to listen to the cifModelChange event. Then, to get the slot information you should use event.detail property, which is an object with the following properties:
- slot: corresponds to the name of the slot whose value has changed
- payload: corresponds to the new value of the slot
A working example
The only output slot of our cubx-textarea
component is value. The following example will add a <p> element showing the slot and the payload properties of the event detail each time the value slot changes its value. Such change can be triggered by just typing within the textarea and then clicking outside, or pressing tab key, etc.
Special outside interaction for elementary components
Since elementary components are registered as Polymer elements using the CubxPolymer object (explained a section below), you would also like to interact with it accessing the model of the elementary using Javascript as shown below:
... <script> ... // Wait until the component is ready to be used document.addEventListener('cifReady', function() { // Interact with the component using the methods presented above cubxTextarea.model.id = 'myTextArea'; ... // And the access it var textareaId = cubxTextarea.model.id; ... }); })() </script> ...
However, in Polymer the sub-properties are not observable by default, which means that in the code above cubxTextarea.model.id = 'myTextArea'
won't be propagated.
Dynamic connections
As mentioned above, there is a method called addDynamicConnection
, which allows you to add a connection between two components dynamically. Those components should be within the same Context. The argument of this method is an instance of the window.cubx.cif.DynamicConnection
object, which have the following properties:
Property name | Type | Requirement | Description |
---|---|---|---|
source | object | required | Represents the source of the connection. It has the following properties:
|
destination | object | required | Represents the target or destination of the connection. It has the following properties:
|
connectionId | string | optional (automatically generated) | Indicates the id for this connection, which should be unique within a context |
hookFunction | string | optional | Describes a function or the nema of a function to be called before the value is set in the destination slot |
repeatedValues | boolean | optional | Indicates whether same value can be propagated consecutively |
copyValue | boolean | optional | Indicates whether the payload of the connection should be copied or not |
directExecution | boolean | optional | Indicates whether the connection should start working after its creation |
Public methods
To set and validate the properties of a DynamicConnection
you can use the following methods:
Method name | Description |
---|---|
setSource (source) | Set the source object to a dynamic connection |
setSourceRuntimeId (runtimeId) | Set the runtimeId property to the source of a dynamic connection |
setSourceSlot (slotName) | Set the slot name to the source of a dynamic connection |
setDestination (destination) | Set the destination object to a dynamic connection |
setDestinationRuntimeId (runtimeId) | Set the runtimeId to the destination of a dynamic connection |
setDestinationSlot (slotName) | Set the slot name to the destination of a dynamic connection |
setConnectionId (connectionId) | Set an id to the connection When you add the connection to a component its connectionId will be generated automatically, thus you don't need to set it. |
setCopyValue (copyValue) | Set the copyValue property of the connection, which indicates whether the payload of the connection should be copied or not |
setRepeatedValues (repeatedValues) | Set the repeatedValues property of the connection, which indicates whether same value can be propagated consecutively |
setHookFunction (hookFunction) | Set a function to the connection, which will be called before the value is set in the destination slot |
setDirectExecution (directExecution) | Set the directExecution property of the dynamic connection, which indicates whether the connection should start working after its creation, i.e. the current value of the source should be propagated immediately. |
validate () | Validate all the properties of a dynamic connection. Additionally, throw and report errors when any |
Creating and adding a dynamic connection
Using the methods described above, you can create a dynamic connection to added it to an existing component. To create a connection, first you need to access the source and destination component:
... var sourceTextArea = document.querySelectorAll('cubx-textarea')[0]; var destinationTextArea = document.querySelectorAll('cubx-textarea')[1]; ...
Then, you should instantiate and set the properties of a DynamicConnection object:
... var dynCon = new window.cubx.cif.DynamicConnection(); dynCon.setSourceRuntimeId(sourceTextArea.getAttribute('runtime-id')); dynCon.setSourceSlot('value'); dynCon.setDestinationRuntimeId(destinationTextArea.getAttribute('runtime-id')); dynCon.setDestinationSlot('value'); dynCon.setDirectExecution(true); ...
Finally, you need to access the desired component (source or destination) and add the dynamic connection:
... sourceTextArea.addDynamicConnection(dynCon); ...
A working example
This time we will have two instances of our cubx-textarea
component and we will connect the their value slots. The first cubx-textarea
will be the source component, which will have an initial value, which will let us see the effect of having the directExecution property set to true. The code below allows to:
- Get the source and destination components from DOM
- Listen to the 'cifReady' event to enable the 'Add dynamic connection' button
- Listen to the 'click' event
- Instantiate and set the properties of the DynamicConnection object
- Add the DynamicConnection to the source component
- Disable the 'Add dynamic connection' button
Interacting with Cubbles from the inside (Only Elementary Components)
By interacting from the inside, we mean to control the behavior of component from its logic or its view. Thus, this process can be carried out only for elementary components, remember that compound component have no associated logic. When you are developing elementary components you will certainly need to control the behavior of your component, e.g. setting a slot value, or performing some action after a slot value changes.
From now, we will assume that we are developing our sample component, the cubx-textarea
, we will explore how to interact with the elementary from the view, the logic and the manifest of our elementary (See Create an Elementary Component). For the purpose of this tutorial we will assume the the cubx-textarea
only have three slots: id, value and label.
The CubxPolymer object
The CubxPolymer object provides the global CubxPolymer()
function used for registering a new Cubble component. This basically extends the global Polymer()
function that comes with Polymer and that is used for implementing elementary Cubbles components. These extension means:
- The addition of a model as a property of the Polymer element to enable elementary interaction
- The availability of methods for external interaction and internal interaction with that model
For more details see Polymer Project.
Interaction within the view
The interaction within the view (cubx-textarea.html file) of the elementary is posible due to the model property. In our case the cubx-textarea
view will contain:
- A
label
tag to show a label for the component when the label has a value - A
textarea
tag which is the heart of ourcubx-textarea
component
<link rel="import" href="cubx-textarea-style.html"> <dom-module id="cubx-textarea"> <template> <!-- Style for the component which in this case is included from a file called cubx-textarea-style --> <style include="cubx-textarea-style"></style> <label></label> <textarea></textarea> </template> <!-- Logic for the component which in this case is included from a file called cubx-textarea.js --> <script src="cubx-textarea.js"></script> </dom-module>
The <dom-module>
tag allow you to create and manages the local dom of the elementaries, this is a feature from Polymer, so if want to get more information about it check the Local DOM Basics and API.
Within this view we will access our component's slots using the model, but first, we need to understand some Polymer concepts:
- Data binding: connects data (the model property) from a custom element (our elementary or host element as called by polymer) to a property or attribute of an element in its local DOM (e.g. the label tag within our
cubx-textarea
). The data binding is possible due to the binding annotations (More info at Data binding):- {{ }} binding annotation: when using double curly bracket (
{{ }}
) the data flow goes down from host (elementary) to target (element in local dom) and up from target to host. - [[ ]] binding annotation: when using double square bracket (
[[ ]]
) the data flow goes only down from host (elementary) to target (element in local dom).
- {{ }} binding annotation: when using double curly bracket (
- Data paths: a path is a string that identifies a property or subproperty relative to a scope. In our case, the scope is our elementary (More info at Polymer data paths).
To access the slots you need to use a data path following this convention: model.<slotname>
. In the view we only want to bind the label and the id slots to the html elements, so our code would looks as shown below:
<link rel="import" href="cubx-textarea-style.html"> <dom-module id="cubx-textarea"> <template> <!-- Style for the component which in this case is included from a file called cubx-textarea-style --> <style include="cubx-textarea-style"></style> <!-- $ is used to bind html attributes--> <label for$="{{model.id}}">{{model.label}}</label> <textarea id$="{{model.id}}"></textarea> </template> <!-- Logic for the component which in this case is included from a file called cubx-textarea.js --> <script src="cubx-textarea.js"></script> </dom-module>
Note that in Polymer sub-properties are not observable by default. Since, all the slots are sub-properties of model, this means that any change to the slots within the local-dom (e.g. a change to the attribute id in the textarea of the cubx-textarea) will not be propagated.
A working example
Once again we will use our cubx-textarea
elementary, but having into account that its view corresponds to the one built in last section. This time, we use the 2.3 | The Cubbles Tag API to initialize the slots: id and label. Which allow us to see how useful can be to use the model property within the view.
Interaction within the logic
When programming the logic of an elementary component you will certainly need to access and edit slots' values. Within the logic you can also use the public methods used to interact from the outside, i.e. get<Slotid>(), set<Slotid>(value), slots () and repropagate<Slotid> (). In these cases, the component is referenced using this since you are inside the component, e.g. use this.getId()
in our cubx-textarea
elementary to access the value of the id slot. Additional to those methods you can use the ones explained below:
Method name convention | Description |
---|---|
model<Slotid>Changed () | Called when the value of the slot has changed due to calling the set<Slotid>(value) method. It is useful when you need to perform additional logic after the slot has changed. If you need to reset the value of the slot within this method, you should use the model property, i.e. this.model.slotid = newValue, then to propagate new value use this.repropagate<Slotid> (). Otherwise, using set<Slotid>(newValue) will cause an infinite loop. |
cubxReady () | This method will be called, when
Thus, it is useful when you need to perform some code when the components are ready to be used. |
Adding logic to the cubx-textarea component
In order to make our cubx-textarea
component to work properly when textarea value changes. To aim that, first we will need to modify the view of the component as follows:
... <textarea id$="{{model.id}}" on-change="inputFieldSlotValueChanged"></textarea> ...
Then in the logic (cubx-textarea.js file) of the component we need to add:
- The method inputFieldSlotValueChanged which is called when the value of the textarea changes.
- A method to update textarea when the slot value changes, here we access local dom using Polymer selector.
These methods wold look as follows:
... inputFieldSlotValueChanged: function (event) { // update the cubbles-model this.setValue(event.target.value); }, modelValueChanged: function (newValue) { // update the view this.$$('textarea').value = newValue; }, ...
The whole code for the view and logic would be:
View
<link rel="import" href="cubx-textarea-style.html"> <dom-module id="cubx-textarea"> <template> <style include="cubx-textarea-style"></style> <label for$="{{model.id}}">{{model.label}}</label> <textarea id$="{{model.id}}" on-change="inputFieldSlotValueChanged"></textarea> </template> <script src="cubx-textarea.js"></script> </dom-module>
Logic
(function () { 'use strict'; CubxPolymer({ is: 'cubx-textarea', /** * A handler to be called by the local textarea * @param {event} event */ inputFieldSlotValueChanged: function (event) { // update the cubbles-model this.setValue(event.target.value); }, /** * Called when slot 'value' has changed */ modelValueChanged: function (newValue) { // update the view this.$$('textarea').value = newValue; } }); }());
A similar approach, ie. using the model<Slotid>Changed () method, could be used for all the attributes and slots of the cubx-textarea
presented above.
A working example
This time, we will use two instances of the cubx-textarea
elementary but having into account that its view corresponds to the one built in last section. We use the 2.3 | The Cubbles Tag API to initialize the slots: id, value and label and create a connection and to compose a compound componen composed by the tow instances. Which allow us to see how the logic and view of our cubx-textarea
works properly within a compound.
Interaction via initialization
Input slots can be initialized in manifest, so that you can predefined an initial interaction by default. For elementary components, the slot definition object has a property called value. This value will be set to the slot during component initialization, thus when cubxReady() is called the slot will have this value.
Now we will initialized the value of the slots of our cubx-textarea
component. The slots definition should look similar to the one shown below, note that the initialization occurs due to the value property:
... "slots": [ { "slotId": "id", "type": "string", "direction": [ "input" ], "value": "myTextarea", }, { "slotId": "value", "type": "string", "direction": [ "input", "output" ], "value": "The value of my textarea", }, { "slotId": "label", "type": "string", "direction": [ "input" ] "value": "", } ] ...
Note that he propagation of these initial values will not be propagated, since this initialization is only valid for input slots.
A working example
This time we should just use the component to see the result of defining init values for the slots: