Using the Repetition Component
The Repetition component is used to produce a repeating group of elements based on an array of values. All elements nested inside of the Repetition element will repeat in each iteration. The content inside of a repetition is managed by a controller. You can set the Repetition component’s content
property manually with a standard array for a simple repetition. You can also expand the component’s capabilities by assigning a RangeController component to its contentController
property.
You can use the Repetition component as a building block to repeat any number of user interface components. (The Mod List component, for example, uses the Repetition component to support selection management.) All elements nested inside of the Repetition element will repeat in each iteration of the repetition. You can use the bindable iteration.object
property to point to the current list item.
A Simple Repetition
The following example shows a simple Repetition (items
) that produces three text items. Each iteration is associated with a corresponding item in a source collection, which is populated through the Repetition component’s content
property.
<div data-mod-id="content" class="Content">
<ul data-mod-id="items">
<li data-mod-id="item"></li>
</ul>
</div>
"owner": {
"values": {
"element": { "#": "content" }
}
},
"items": {
"prototype": "mod/ui/repetition.mod",
"values": {
"element": { "#": "items" },
"content": { "<-": "@owner.myListProperty" }
}
},
"item": {
"prototype": "mod/ui/text.mod",
"values": {
"element": { "#": "item" },
"value": { "<-": "@items:iteration.object.quote" },
"classList.has('highlight')": { "<-": "@items:iteration.object.important" }
}
}
.highlight {
font-weight: bold;
}
var Component = require("mod/ui/component").Component;
exports.Content = Component.specialize({
myListProperty: {
value: [{
"quote": "If music be the food of love, play on.",
"important": false
}, {
"quote": "O Romeo, Romeo! wherefore art thou Romeo?",
"important": true
}, {
"quote": "All that glitters is not gold.",
"important": false
}, {
"quote": "I am amazed and know not what to say.",
"important": false
}]
}
});
Preview:
View in Mfiddle: http://montagejs.github.io/mfiddle/#!/7882151
Using a RangeController with a Repetition
In this example:
- The Repetition component’s
contentController
property is set to be aRangeController
for managing some content. - The
Text
component is a child of the Repetition and derives itsvalue
property from itsiteration.object
property. - Clicking the Change Content button replaces the managed content with new random content.
<div data-mod-id="component">
<button data-mod-id="button"></button>
<ul data-mod-id="repetition">
<li data-mod-id="value"></li>
</ul>
</div>
"owner": {
"values": {
"element": {"#": "component"}
}
},
"repetition": {
"prototype": "mod/ui/repetition.mod",
"values": {
"element": {"#": "repetition"},
"contentController": {"@": "rangeController"}
}
},
"rangeController": {
"prototype": "mod/core/range-controller"
},
"value": {
"prototype": "mod/ui/text.mod",
"values": {
"element": {"#": "value"},
"value": {"<-": "@repetition:iteration.object.quote"}
}
},
"changeButton": {
"prototype": "digit/ui/button.mod",
"values": {
"element": {"#": "button"},
"label": "Change Content"
},
"listeners": [
{
"type": "action",
"listener": {"@": "owner"}
}
]
}
var Component = require("mod/ui/component").Component;
exports.Owner = Component.specialize({
constructor: {
value: function Owner() {
this.content = [{
"quote": "If music be the food of love, play on.",
"important": false
}, {
"quote": "O Romeo, Romeo! wherefore art thou Romeo?",
"important": true
}, {
"quote": "All that glitters is not gold.",
"important": false
}, {
"quote": "I am amazed and know not what to say.",
"important": false
}];
}
},
templateDidLoad: {
value: function () {
this.templateObjects.rangeController.content = this.content;
}
},
handleChangeButtonAction: {
value: function (evt) {
var randomContentIndex = Math.floor(Math.random() * this.content.length);
this.templateObjects.rangeController.add(this.content[randomContentIndex]);
}
}
});
Preview:
View in Mfiddle: http://montagejs.github.io/mfiddle/#!/7883458
Sorting and Filtering the Items in a Repetition
To sort and filter items in a repetition you can use use FRB expressions on the RangeController.
- Adding or removing items from the source collection automatically updates the returned items.
- When the value of the
filterPath
isfalse
, the item will not be included in the repetition. - Using
sortPath
returns the items in sequential order, sorted by the value of theproperty
when the path is a single property.
You can also use complex expressions inside of sort and filter operations. For example, when the repetition’s content has an index property that is a sequence of integers, you can filter out the odd numbers like this: !(index%2)
.
<div data-mod-id="component">
<button data-mod-id="filterButton"></button>
<button data-mod-id="sortButton"></button>
<ul data-mod-id="repetition">
<li data-mod-id="quote"></li>
</ul>
</div>
"owner": {
"values": {
"element": {"#": "component"}
}
},
"repetition": {
"prototype": "mod/ui/repetition.mod",
"values": {
"element": {"#": "repetition"},
"contentController": {"@": "rangeController"}
}
},
"rangeController": {
"prototype": "mod/core/range-controller"
},
"quote": {
"prototype": "mod/ui/text.mod",
"values": {
"element": {"#": "quote"},
"value": {"<-": "@repetition:iteration.object.quote"}
}
},
"filterButton": {
"prototype": "digit/ui/button.mod",
"values": {
"element": {"#": "filterButton"},
"label": "Filter"
},
"listeners": [
{
"type": "action",
"listener": {"@": "owner"}
}
]
},
"sortButton": {
"prototype": "digit/ui/button.mod",
"values": {
"element": {"#": "sortButton"},
"label": "Sort"
},
"listeners": [
{
"type": "action",
"listener": {"@": "owner"}
}
]
}
var Component = require("mod/ui/component").Component;
exports.Owner = Component.specialize({
constructor: {
value: function Owner() {
this.content = [{
"quote": "If music be the food of love, play on.",
"important": false
}, {
"quote": "O Romeo, Romeo! wherefore art thou Romeo?",
"important": true
}, {
"quote": "All that glitters is not gold.",
"important": false
}, {
"quote": "I am amazed and know not what to say.",
"important": false
}];
}
},
templateDidLoad: {
value: function () {
this.templateObjects.rangeController.content = this.content;
}
},
handleFilterButtonAction: {
value: function () {
var rangeController = this.templateObjects.rangeController;
rangeController.filterPath = rangeController.filterPath ? "" : "important";
}
},
handleSortButtonAction: {
value: function () {
var rangeController = this.templateObjects.rangeController;
rangeController.sortPath = rangeController.sortPath ? "" : "quote" ;
}
}
});
Preview:
View in Mfiddle: http://montagejs.github.io/mfiddle/#!/7884201
Allowing Users to Select Repetition Items
To allow users to select an item in a repetition:
- Set the Repetition component’s
isSelectionEnabled
property totrue
. - The
selected
CSS rule is automatically applied to the selected item.
Note that users could select multiple items.
<div data-mod-id="component">
<div data-mod-id="repetition">
<p data-mod-id="value"></p>
</div>
<select data-mod-id="select"></select>
<p data-mod-id="log"></p>
</div>
"owner": {
"values": {
"element": {"#": "component"}
}
},
"repetition": {
"prototype": "mod/ui/repetition.mod",
"values": {
"element": {"#": "repetition"},
"isSelectionEnabled": true,
"contentController": {"@": "rangeController"}
}
},
"rangeController": {
"prototype": "mod/core/range-controller",
"values": {
"selection": []
}
},
"value": {
"prototype": "mod/ui/text.mod",
"values": {
"element": {"#": "value"},
"value": {"<-": "@repetition:iteration.object.quote"}
}
},
"select": {
"prototype": "digit/ui/select.mod",
"values": {
"element": {"#": "select"},
"contentController": {"@": "rangeController"},
"labelPropertyName": "quote"
}
},
"log": {
"prototype": "mod/ui/text.mod",
"values": {
"element": {"#": "log"},
"value": {"<-": "@owner.message"}
}
}
.selected {
color:red;
}
var Component = require("mod/ui/component").Component;
exports.Owner = Component.specialize({
constructor: {
value: function Owner() {
this.content = [{
"quote": "If music be the food of love, play on.",
"important": false
}, {
"quote": "O Romeo, Romeo! wherefore art thou Romeo?",
"important": true
}, {
"quote": "All that glitters is not gold.",
"important": false
}, {
"quote": "I am amazed and know not what to say.",
"important": false
}];
}
},
templateDidLoad: {
value: function () {
this.templateObjects.rangeController.content = this.content;
//Observe the selection for changes
this.templateObjects.rangeController.addRangeAtPathChangeListener(
"selection", this, "handleSelectionChange");
}
},
handleSelectionChange: {
value: function (plus, minus) {
this.message = "Selection changed from: "
+ (minus[0] ? minus[0].quote : "nothing")
+ " -> "
+ (plus[0] ? plus[0].quote : "nothing");
}
}
});
Preview:
View in Mfiddle: http://montagejs.github.io/mfiddle/#!/7884716