When you’re working with large quantities of user social data, one question naturally arises in the context of OpenSocial templating: how do you handle repeating content, such as a list of friends or any measure of activity data? We certainly won’t be creating a markup definition for each repeating node in the template, will we? The short answer is no.
Content looping in OpenSocial allows a developer to define a block of markup to be used to render a series of repeating elements, without having to resort to handling each repeating node as a separate instance of markup. There are a few ways to define a loop structure within an OpenSocial template specification, which we’ll go over next.
The first method is to build a loop structure that outputs the value of a piece of data as an escaped string, much like we saw with conditional statements. Let’s say that we have a customized subset of activity data that is returned to us through a data pipe request:
activities: [ { title: "Jonathan just added a new comment on Twitter", body: "See what Jonathan is saying by adding him to Twitter", url: "http://www.twitter.com/jcleblanc" }, { title: "Michael updated his profile", body: "See more about what Michael is doing", url: "http://www.container.com/profiles/michael" }, { title: "Diane just posted a comment on your wall", body: "Reply to Diane's message", url: "http://www.container.com/messages" } ];
This content structure has several repeating nodes within the object that we can use to build out a complimentary repeating block within an OpenSocial template:
<script type="text/os-template"> <ul> <li repeat="${activities}"> <a href="${Cur.url}"> ${Cur.title} </a><br /> ${Cur.body} </li> </ul> </script>
We wrap our template content in our script
block with the specialized type
definition to start out the template declaration. The markup within
the script
block first defines an
unordered list to display the activities. Instead of defining a list
item for each element, we simply define a single <li>
that contains the repeat
declaration:
<li repeat="${activities}">
Here we’ve stated that this element and its content should be
repeated based on the objects contained within activities
, which we defined earlier. Within
the repeated element, we can easily refer to the current object being
exposed in the loop by using a provided reference variable:
${Cur.title}
${Cur}
allows us to refer to
the current object in the loop in question. In this way, we can create
an entire markup construct with a smaller code base than we would by
defining straight markup. Given the preceding loop, the application
would render the complete markup structure for all repeated objects
that we iterated over:
<ul> <li> <a href="http://www.twitter.com/jcleblanc"> Jonathan just added a new comment on Twitter </a><br /> See what Jonathan is saying by adding him to Twitter </li> <li> <a href="http://www.container.com/profiles/michael"> Michael updated his profile </a><br /> See more about what Michael is doing </li> <li> <a href="http://www.container.com/messages"> Diane just posted a comment on your wall </a><br /> Reply to Diane's message </li> </ul>
The alternate method for looping within an OpenSocial template is to use the
os:Repeat
tag. This will instruct
the template to render any code within the data objects being
displayed as actual markup—not just the string representations of the
markup.
The looping structure is essentially the same as the previous
example, but instead of applying a repeat
attribute to the block of markup to
be repeated, we wrap the entire block in the os:Repeat
tag. If we
modify our previous example, the repeat tag would look like:
<script type="text/os-template"> <ul> <os:Repeat expression="${activities}"> <li> <a href="${Cur.url}"> ${Cur.title} </a><br /> ${Cur.body} </li> </os:Repeat> </ul> </script>
This produces the same results as the previous example. In
addition to the standard repeat syntax, you can use the var
attribute to assign a different variable
name to the repeater:
<os:Repeat expression="${activities}" var="myActivities"> <li> <a href="${myActivities.url}"> ${myActivities.title} </a><br /> ${myActivities.body} </li> </os:Repeat>
When working with repeaters, you may need to nest multiple loop
structures together to get the desired visual results. But nesting
repeaters has its own challenges when it comes to accessing the
current result object in the loop using ${Cur}
.
Variable naming is an excellent method for obtaining the desired
object data if you’re using nested repeaters. It allows you to access
the current object of the current or parent loop (if the parent is
subsequently named). When you specify a var
attribute, the object being iterated
over will be stored in the variable name (e.g., ${MyActivities}
) as well as within ${Cur}
.
For instance, let’s assume that we have the same activity data
within the activities
object for
each one of the user’s friends, stored in the variable friends
. Our friend data object contains the name of the
friend and an association to the activity data. We may want to display
an unordered list of activities for each friend, using multiple
repeaters:
<div repeat="${friends}" var="myFriends"> <b>Activities for ${myFriends.name}</b> <div repeat="${activities}" var="myActivities"> <a href="${myActivities.url}"> ${myActivities.title} </a><br /> ${myActivities.body} </div> </div>
Using var
names, not only can
we ensure that we are accessing the exact data that we want, but we
also keep our template code readable so that when we’re working with
it later, we can follow the object references more easily than if we’d
used ${Cur}
throughout.
Using the ${Context}
special variable is handy for capturing the index of the
current object being iterated over, but what if you are
working with nested repeaters and want to access the indices for both
loops within the inner loops? Support for this type of functionality
is valuable if you are working with a grid interface for your
application, separating loops into x and
y planes.
This type of problem is exactly what custom indices attempt to
solve. Using the index
attribute on
a repeater will allow you to access that variable at any point within
the loop structure:
<div repeat="${xplane}" index="x"> <div repeat="${yplane}" index="y"> <span id="${x}_${y}"> Current grid index is ${x} : ${y} </span> </div> </div>
We apply CSS styles to build a visual grid of our div
s. Using the index values, we can then
keep track of which grid section we are currently in. These indices
can be applied to the id
attributes
of the div
s, for instance, to give
us tracking capabilities.
During the course of a loop, you may need to use or collect the current
index of, or the total number of results available within, the looped
object. This is the purpose of the
special variable ${Context}
. As we discussed earlier,
${Context}
contains three reference
objects you can use within the context of a loop:
UniqueId
A unique identifier for the current template being processed
Index
The index number of the current object being iterated over
Count
The total number of objects to be iterated over
Using this special variable, you can add another dimension to the markup to be generated from the loop:
<ul>
<li repeat="${activities}">
Activity ${Context.Index + 1} of ${Context.Count}<br />
<a href="${Cur.url}">
${Cur.title}
</a><br />
<div id="${Context.UniqueId}-${Context.Index}">${Cur.body}</div>
</li>
</ul>
In the preceding template, we apply the ${Context}
variable to the existing template
markup that we built earlier. When this result renders, the user will
be presented with markup
containing the current index number (e.g., Activity 1 of 10) above
every activity that is generated:
<ul> <li> Activity 1 of 3 <a href="http://www.twitter.com/jcleblanc"> Jonathan just added a new comment on Twitter </a> <div id="tpl1234-1"> See what Jonathan is saying by adding him to Twitter </div> </li> <li> ...
One of the best methods for customizing a loop structure is to
apply conditional statements to the loop itself, specifying that you
want the loop to run only if a specific condition has been met.
Repeaters will be evaluated before conditionals, meaning that when a
conditional is used within a repeater, the conditional will be applied
to each result within the repeating data set, much like running a
standard loop with an if
statement
embedded:
for (var i = 0; i < result.length; i++){ if (result[i] === "value"){ //render markup } }
Our first method, using the repeat
attribute on the block to be
repeated, applies the conditional statement directly within the same
node:
<li repeat="${activities}" if="${Cur.url == 'http://www.container.com/messages'"> <a href="${Cur.url}"> ${Cur.title} </a><br /> ${Cur.body} </li>
This would render the markup only for any object whose URL is http://www.container.com/messages.
If we want to use this same type of conditional loop on
an os:Repeat
tag, we
have to implement it in a slightly different manner. os:Repeat
does not support repeat
or if
attributes, so we will need to embed them
within the repeater block:
<os:Repeat expression="${activities}"> <os:If condition="${Cur.url == 'http://www.container.com/messages'"> <li> <a href="${Cur.url}"> ${Cur.title} </a><br /> ${Cur.body} </li> </os:If> </os:Repeat>
These two statements are equivalent in the order of their execution. Using this mix of repeaters and conditionals, developers can build scalable template designs suited to their application needs.
18.117.189.228