Tải bản đầy đủ (.pdf) (50 trang)

Tài liệu Building OpenSocial Apps- P6 doc

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (654.44 KB, 50 trang )

ptg
224 Chapter 10 OSML, Gadgets, and the Data Pipeline
Table 10.6 os:ActivitiesRequest Attributes
Attribute Description
Key Required. A string value used to identify this data within
the DataContext. The key value must be unique
within the app across all data tags.
userId Required. A comma-delimited list of IDs to use in
conjunction with the
groupId attribute. Valid values are
@viewer
@owner
a specific user ID
groupId Optional. The group of users to get, relative to the value
defined in the
userId attribute. If this is not speci-
fied, it defaults to
@self, which means the person
object(s) corresponding to the value of the
userId
attribute. Valid values are
@self
@friends
The value @self means the records specified in the
userId attribute. The value @friends means friends
of the user(s) specified in the
userId attribute.
Fields Optional. A comma-delimited list of OpenSocial activity
fields to return with each record.
startIndex Optional. In a paged list of records, this specifies the
starting index for results.


Count Optional. An integer value specifying the maximum
number of records to return.
Table 10.7 os:DataRequest Attributes
Attribute Description
Key Required. A string value used to identify this data within
the DataContext. The key value must be unique
within the app across all data tags.
method The REST endpoint method used for this request.
Currently, only GET requests are allowed. Valid
requests are
people.get (equivalent to os:PeopleRequest)
activities.get (equivalent to
os:ActivitiesRequest)
More endpoints are being added all the time, so check
the MySpace developer site for the latest information.
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
JavaScript Blocks in OSML Apps
Warning
Gadget XML and OSML must be a well-formed XML document. To prevent surprise parsing
errors stemming from your JavaScript code, always make use of CDATA tags in your
JavaScript blocks.
All apps that make use of OSML or Data Pipelining must be in gadget XML
format and consist of well-formed XHTML content only. If your Content blocks
wrap their innards in CDATA tags as is suggested in some of the older Gadget
XML documentation, none of the OSML or Data Pipeline tags will be evaluated.
As a result, your app code must consist of well-formed XHTML content only.Any
JavaScript code containing a “less-than” or “greater-than” test will violate this
requirement.

Here is an example:
if(i > 0){
The solution is to wrap all your JavaScript blocks in a CDATA section:
<script type="text/javascript">
//<![CDATA[
function foo(){
// Do something
}
//]]>
</script>
Wrapping the client script block contents with CDATA sections instructs the OSML
parser to ignore things that might otherwise look like tags inside. Even though tags are
not evaluated, expression language statements will be.Therefore, the following statement
will still work:
var greeting = "Welcome to my app, ${vwr.displayName}";
OpenSocial Markup Language
The OpenSocial Markup Language, or OSML, is a tag-based markup language for
adding and defining reusable user interface components, easily displaying content from
an external server, and managing simple control flow.When OSML is coupled with Data
Pipelining, it becomes a formidable new way of writing apps.
In this section we introduce some of the basic tags available in the initial version of
OSML.
OpenSocial Markup Language 225
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
Basic Display Tags
There are a limited number of convenience tags defined within OSML.These tags allow
simple and common UI elements to be easily rendered.The tag set is currently small, but
look for more complex UI elements to be added over time.Table 10.8 identifies the cur-

rent built-in OSML markup tags.
Remote Content Display Tags
HTML partial fragment rendering has become a common Ajax technique. OSML
provides a simple tag for displaying inline content from remote servers via a GET
request. MySpace also provides a more general-purpose display extension tag.Table 10.9
identifies these two tags.
Control Flow Tags
There are two basic mechanisms provided for display control flow: os:If and
os:Repeat. Additionally, MySpace provides an extension to os:If to give it an “else”
syntax as well.Table 10.10 identifies the control flow tags available in OSML
Putting It Together: OSML Tic-Tac-Toe
Our previous “Hello World” gadget app gave a light introduction to using Data Pipeline
tags along with the general gadget structure. Now we’re going to take what we’ve seen
so far and apply it to our existing Tic-Tac-Toe app to see what happens.
226 Chapter 10 OSML, Gadgets, and the Data Pipeline
Table 10.8 OSML Markup Tags
Tag Purpose
os:Name The specified person’s display name, hyperlinked to the
person’s Profile
os:Badge The formatted Profile thumbnail image and name,
hyperlinked to the person’s Profile
os:PeopleSelector Displays a FriendPicker, scoped to the identified group
Table 10.9 Remote Content Display Tags
Tag Purpose
os:Get Fetches the markup from an arbitrary URL via an HTTP
GET request and displays it inline at the tag location
myspace:RenderRequest Fetches the markup using any HTTP method supported
by
gadgets.io.makeRequest. Contents may be
displayed inline or in a specified element.

From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
Setting Up the Gadget
Our first step is to move the existing code as is into the gadget XML format. For this
step we’ll make use of our desktop code editor to paste all three of the existing app
surfaces into an empty gadget XML file.The OpenSocial Sandbox tool will be our best
friend for validating our work in this initial stage.
Creating the Initial Gadget File from Existing Code
Here we will construct the initial app gadget XML source file from the code blocks we
have already developed using the app Surface Editor. At the end of these steps we will
have our same app in the gadget XML format.
1. Navigate to the Sandbox by clicking Tools → OSTool/Harness from the developer
site and clicking the Try the Beta OpenSocial Sandbox Editor link to open the
Sandbox Editor (shown in Figure 10.1).
2. Copy the contents of the Blank Sample Gadget and paste it into a new code file
in your code editor (Aptana for us).
3. Change the value of the “title” attribute of the <ModulePrefs> element from
“Hello World” to “OpenSocial Tic-Tac-Toe.”
4. Save your file as OpenSocialTTTGadget.xml.
5. Open the source for the Home Tic-Tac-Toe surface in your code editor.
6. Copy the entire contents of the Profile code file and paste it into your new gadget
in the Profile view template script.The surrounding code will look like this:
<Content type="html" view="home">
<script type="text/os-template">
// PASTE THE CODE FROM Tic-Tac-Toe HOME SURFACE HERE
</script>
</Content>
7. Copy the contents of your new gadget file and paste them into the Sandbox
Editor.

Putting It Together: OSML Tic-Tac-Toe 227
Table 10.10 OSML Control Flow Tags
Tag Purpose
os:If A conditional block that is shown if the expression
statement evaluates to
true
osx:Else Inverse conditional support for os:If. This is a
MySpace extension to the OSML specification. The
osx:Else tag must appear immediately adjacent to
an
os:If tag.
os:Repeat Iterates over an array of data items and displays the
contents of the repeat tag, evaluated for each data item
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
8. Select the Home view from the Views drop-down and click Render.You will see
an error similar to the following:
An error occurred while parsing EntityName. Line 210, position 43.
// First check if there was an error back from the proxy
if(!response || (response.errors && response.errors.length > 0)
|| response.errorCode){
retryRequest();
}
We need to fix these parsing errors before we can get any further.
Fix Parsing Errors
The error we encountered was caused by a greater-than comparison occurring within a
JavaScript block.To get around this, we’ll wrap all client JavaScript script tag contents in
CDATA sections:
1. Go back to the gadget code in your source editor.

2. Search for all the <script> tags that do not have a type attribute of
text/os-template or text/os-data.
228 Chapter 10 OSML, Gadgets, and the Data Pipeline
Figure 10.1 OpenSocial Sandbox Editor.
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
3. Add an opening CDATA tag immediately after the script tag and a closing
CDATA tag immediately before the closing </script> tag.These tags should be
preceded by JavaScript inline comment characters. Here is an example of what the
updated script block will look like:
<script type="text/javascript">
//<![CDATA[
. . . //JavaScript code excluded for brevity
//]]>
</script>
4. Save the results and re-render using the Sandbox Editor as before.When it is
working correctly, you should see the Home view, rendered as in the live app (as
shown in Figure 10.2).
Adding Other Surfaces
The other two surfaces must also be added to the gadget file. Paste the Profile and
Canvas code into the templates in the appropriate Content blocks of your gadget file.
Make sure you add CDATA sections to all JavaScript blocks until the Sandbox Editor
can render all three surfaces without any parsing errors.
There is one more issue that will surface when reconciling the existing JavaScript app
with the gadget/OSML format. Activity templates make use of an expression marker in
their templates that’s similar to one found in the Data Pipelining expression language.
Both use the format ${SOME_VAR} to define an expression.The OSML renderer, finding
the embedded activity templates, will attempt to resolve the contained tokens as
Putting It Together: OSML Tic-Tac-Toe 229

Figure 10.2 Sandbox rendering of Home.
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
expressions and hiccup.The resolution is to build up the activity template strings in a
way that does not look like an expression to the server-side OSML processor.
Search the gadget code for "${" and make the following changes:
// Version using the default template
function rsmNotification(recipient, game_id){
// Set up all the data we'll need
var body = "$"
body += "{sender} has finished their move ";
body += "in $"
body += "{app}, it's your turn!";
This splits the activity template expression up so the server doesn’t recognize it, but the
template still looks the same to the client code.
Reusing Common Content
We have now created a single-source-file behemoth for our app, weighing in at almost
2500 lines of code. It’s nice to have a single source file for storage and tracking purposes,
but it can be a bit ungainly to manage from a code maintenance standpoint.The Aptana
IDE does a nice job of allowing you to expand and collapse nodes for easier visibility,
but that’s no substitute for cleaning up our code.We need to DRY things out a bit.
230 Chapter 10 OSML, Gadgets, and the Data Pipeline
The DRY Principle
The acronym DRY stands for Don’t Repeat Yourself. It’s a clever catchphrase to remind
developers to reuse code as much as possible. One of the failings of using the Surface
Editor for writing apps is that you will find yourself repeating the same code over and
over again. With an OSML gadget an app can reuse parts of the code through the use of
shared
Content blocks.

If we look at our code, we’ll see a lot of repeated lines across the three surfaces. In
fact, putting the original Home and Profile source files in a diff tool (we used
WinMerge), we find that there are only five differences, consisting of an additional style
on Profile, some slightly different text on the button between the two, and a trailing div
on Profile.Through the use of shared
Content blocks, we can almost immediately
remove 98% of the code associated with one of these surfaces.
Merging Home and Profile with Shared Content Blocks
Gadgets and, by extension, OSML define their different surfaces with <Content> blocks.
The view attribute of a Content block identifies the surface for which the enclosed
content is valid. During rendering of the OSML gadget, any and all Content blocks that
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
have a view value matching the current surface are rendered in the order in which they
are encountered in the gadget XML file. If a particular block of content is valid on more
than one surface, it may be identified as such by using a comma-delimited list of surface
names for which it is to be rendered.This is in contrast to the app Surface Editor, which
allows one and only one discrete view per surface.
Our diff of the Home and Profile sources showed that there are relatively few differ-
ences between the two. Since Profile seems to be a superset of the Home view, with
some small textual changes, we’ll use that as the basis for both surfaces.
Creating Shared Style Content Blocks
One of the powers of the gadget XML format is the ability to stream shared code blocks
across multiple surface views. In this section we use the notion of specifying multiple
valid
view values in a single Content block in order to share common markup, styles,
and JavaScript between the Home and Profile surfaces.
1. Edit your app’s gadget XML file to completely delete the home Content block.
2. Change the profile Content block to also include the Home view, like this:

<Content type="html" view="profile, home">
3. Save your changes and view them using the Sandbox tool.The Home and Profile
surfaces should now match.
4. Add three new Content blocks with template script tags above the existing com-
bined Home and Profile block.The first block will be used to hold the common
styles.The second two will hold view-specific styles.The code should look like
this:
<Content type="html" view="profile, home">
<script type="text/os-template">
</script>
</Content>
<Content type="html" view="profile">
<script type="text/os-template">
</script>
</Content>
<Content type="html" view="home">
<script type="text/os-template">
</script>
</Content>
5. Add the complete <style> block to the first shared Content block.
Putting It Together: OSML Tic-Tac-Toe 231
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
6. Create new style elements in both the subsequent blocks and cut and paste the
subtitle-style class definition out of the shared style area and into each private
style block. Modify the Home surface’s version to be display:none; the two
private style blocks should appear as shown here:
<Content type="html" view="profile">
<script type="text/os-template">

<style>
.subtitle
{
margin:5px 0;
}
</style>
</script>
</Content>
<Content type="html" view="home">
<script type="text/os-template">
<style>
.subtitle
{
display:none;
}
</style>
</script>
</Content>
7. Save and view the results.The Profile surface should still show the subtitle, but the
Home surface should be free of the subtitle.
There is still one visible issue.The button text is incorrect on the Home view.We’ll
fix that by adding some client-side JavaScript to modify the button’s text for the Home
view.
Customizing the Button Text Between Views
There are supposed to be some minor differences between the Home and Profile views
of our app. In this section we add some subtle changes in a
Content block specific to
the Home view that fixes the button to show appropriate text whether on the Home
view or the Profile view.
1. Find the button element in the shared Profile/Home content template. It should

look like this:
<button onclick="rNT(gadgets.views.ViewType.CANVAS);">
Click here to challenge me!</button>
2. Modify the element to add an attribute of id="playButton" to the button
element.
232 Chapter 10 OSML, Gadgets, and the Data Pipeline
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
3. Add a new Content block after the shared Profile/Home Content block that is
specific to the Home view. Include the template script tags and client-side
JavaScript script tags.
<Content type="html" view="home">
<script type="text/os-template">
<script type="text/javascript">
//<![CDATA[
//]]>
</script>
</script>
</Content>
4. Add an inline JavaScript statement to change the text of the button by
manipulating the DOM:
document.getElementById("playButton").innerHTML =
"Click here to play now!";
5. Save and view the results.The Home view should now show the proper button
text. Here is an abbreviated code listing of the updated Home/Profile content
blocks:
<Content type="html" view="profile, home">
<script type="text/os-template">
<style>

body
{
background-color:#1E4C9F;
color:#fff;
margin:0;
padding:0;
}
img
{
border:0;
}

</style>
</script>
</Content>
<Content type="html" view="profile">
<script type="text/os-template">
Putting It Together: OSML Tic-Tac-Toe 233
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
<style>
.subtitle
{
margin:5px 0;
}
</style>
</script>
</Content>
<Content type="html" view="home">

<script type="text/os-template">
<style>
.subtitle
{
display:none;
}
</style>
</script>
</Content>
<Content type="html" view="profile, home">
<script type="text/os-template">
<! Your PROFILE surface content here >
<div id="container">

</div>
<script type="text/javascript">
//<![CDATA[

//]]>
</script>
<div id="output"></div>
</script>
</Content>
<Content type="html" view="home">
<script type="text/os-template">
234 Chapter 10 OSML, Gadgets, and the Data Pipeline
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
<script type="text/javascript">

//<![CDATA[
document.getElementById("playButton").innerHTML = "Click here to play now!";
//]]>
</script>
</script>
</Content>
Working with Data
Having shared display code among the surfaces is great. Having shared data is even more
awesome.The next step is to start migrating some of our data calls and our display to
make use of the Data Pipeline.
We’ll start by changing the Viewer request to an os:ViewerRequest and use the
expression language to display the Viewer info.
Getting Viewer Information with os:ViewerRequest
1. Edit the canvas Content block to add a new os-data section above the
os-template section.The modified code section will look like this:
<Content type="html" view="home">
<script type="text/os-data">
</script>
<script type="text/os-template">
2. Add a new os:ViewerRequest tag to this os-data section with the key viewer.
This tag will retrieve the basic Viewer fields by default. Since our code uses several
extended fields, we will specify the full field set by adding the value @all to the
fields attribute.
<os:ViewerRequest key="viewer" fields="@all" />
3. Edit the contents of the myinfo div to pull the Viewer image and name from our
new pipelined data stored under the viewer key in the DataContext. An expres-
sion language statement is used to set the name and image URL:
<div id="myinfo" class="player_info right_column">
<img class="profile_image"
<div>${viewer.DisplayName}<div>

4. Search for the call to printPerson in the JavaScript. It is in the
getDataCallback function. Comment out this call:
// printPerson(document.getElementById("myinfo"));
Putting It Together: OSML Tic-Tac-Toe 235
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
5. Save and view your results in the Sandbox Editor.The app will now render with
the Viewer information prepopulated on the rendered surface.
That’s pretty exciting.We got access to all of that information without a single line of
JavaScript. Now, let’s fix the More button to show the player bio again instead of allow-
ing the app to get stuck in lightbox mode.
Updating the Player Bio Lightbox to Use Client-Side DataContext
In this section we make some minor refactors to the existing JavaScript function that
controls the display of the opponent box. At this point we only swap out some of
the data calls with calls to the DataContext and leave most of the code intact.
Moving to using a template will be introduced in the next chapter: Chapter 11,
Advanced OSML.
1. Add a new div below the
myinfo div with the ID of playerBioWrapper and set
the style equal to display:none.
2. Search for the printPerson function. Notice all of the HTML being built later
in the function. In the original version of this function the div element for the
lightbox is created dynamically.We’re going to code the lightbox content container
directly on the page.
3. Copy out the wrapping divs and close button divs.Add these to the div created in
step 1.Your markup should appear like this:
<div id="playerBioWrapper" style="display:none;">
<div id='player_bio'><div id='player_bio_title'>Player Bio</div>
<div id="bio_contents"></div>

<div class='clear'></div>
<div id='bio_close'><a
href='javascript:TTT.LightBox.hide();'>close </a></div>
</div>
</div>
4. Add a new JavaScript function called showViewerBio.This holds the logic for
triggering the display of the Viewer’s bio in the lightbox.
5. Add a line to this function to get the Viewer from the client DataContext:
var vwr = opensocial.data.getDataContext().getDataSet("viewer");
6. Build out the contents of the lightbox using the vwr object. Client DataContext
objects are in JSON format and have their properties directly accessible as
properties.
var str = "";
str += "<div style='margin:5px;'>"
str += "<div class='left' style='height:300px;'>"
str += "<img class='profile_image' 236 Chapter 10 OSML, Gadgets, and the Data Pipeline
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
str += vwr.thumbnailUrl
str += "' /></div><div>\n";
var tryAppendLine = function(val){
if(val && val != ""){
return val + "<br />\n";
}
}
str += tryAppendLine (vwr.DisplayName);
str += tryAppendLine (vwr.DateOfBirth);
7. Assign the contents of your string to the innerHTML of the element internal to
the player bio div and assign the entire bio contents to the lightbox:

var bioElem = document.getElementById("playerBioWrapper");
var bioContents = document.getElementById("bio_contents");
bioContents.innerHTML = str;
//Add to lightbox
TTT.LightBox.setContent(bioElem.innerHTML);
//Now show
TTT.LightBox.show();
8. Update the More link to fire the new showViewerBio function instead of directly
invoking the lightbox.Your code should properly show the lightbox again.
Displaying Data Lists
The next operation is to display a list of friends.This is done by combining the results of
an os:PeopleRequest tag with an os:Repeat control.We’re going to update the
Invite tab to use OSML and the Data Pipeline instead of client XHR requests.
Displaying Friends with a Repeater
A repeater allows your app to apply the same display content to multiple items in a list.
The most common use case is to display a list of friends. In this section we’ll use a
repeater to display the friends in the Invite tab of our app instead of the previously
designed JavaScript function for manually outputting this code.
1. Add an os:PeopleRequest tag to the Data Pipeline script (text/os-data) for our
Canvas view.We’ll place it under the key "friends" and get the Viewer’s friends
with it.The following tag states that “I’m going to get a group of friends”—denoted
by the special value "@friends" in the groupId attribute—”and these friends will
be friends of the Viewer”—denoted by the special value "@viewer" in the userId
attribute:
<os:PeopleRequest key="friends" userId="@viewer" groupId="@friends" />
Putting It Together: OSML Tic-Tac-Toe 237
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
2. Find the invite_container div.We’re going to modify this div to add a repeat

tag to iterate over friends. Ahead of the last closing div on this element, add the
following:
<os:Repeat expression="${friends}" >
<div class="friend"><div><img src="${Cur.thumbnailUrl}" /></div>
${Cur.DisplayName}
</div>
</os:Repeat>
3. Save and view.The Invite tab should now show all of the Viewer’s friends prepop-
ulated.We leave it to the reader to rewire the client script call to
rsaInviteClicked.
Summary
OSML is a powerful, convenient, and simple way to build apps. Operations that are
tedious and repetitive with JavaScript are a snap with OSML and Data Pipelining.We’ve
only just introduced some common elements, though. In the next chapter we’ll discuss
some more advanced topics, such as tag templates and internationalization.
The astute reader will also notice that as we ported over our Tic-Tac-Toe app to use
OSML, we were not cleaning out the retired JavaScript.With the exception of com-
menting out some clashing client JavaScript calls, the now unused script code is still in
place. A partial listing of the retired JavaScript functions includes
n
getInitialData
n
getDataCallback
n
printPerson
Since proper refactoring and cleanup of old code can be an arduous process, we will
leave the code in place for now.We leave it to the reader to fully identify superseded
code blocks and clean them out of the app.
What we’ve seen so far are some of the most common operations in OSML.
Remember, OSML is a late addition to the OpenSocial spec and supported starting only

in version 0.9. At the time of this writing, OSML and Data Pipelining are not even fully
released and aren’t recognized by previous versions, whereas the OpenSocial JavaScript
API has more than a year under its belt and is fairly stable.We encourage you to push
the limits with OSML, though, and give your feedback both to MySpace and to the
OpenSocial community so that we may continue to improve things.
Note
Code listings and/or code examples for this chapter can be found on our Google Code
page under .
238
Chapter 10 OSML, Gadgets, and the Data Pipeline
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
11
Advanced OSML: Templates,
Internationalization, and View
Navigation
This chapter will introduce you to some of the more advanced techniques available to
you when using OSML. After reading it, you should be able to define your own custom
tags in your apps and use them on both the client and the server.We’ll explore directly
rendering HTML fragment content from external servers and interacting with data
through data listeners. And to wrap it all up, we’ll internationalize our app and localize it
into different languages.
OSML provides significant features beyond simple markup reuse and tag-based data
declaration. In this chapter we’re going to explore some of the more advanced features
of OSML.These features include defining custom tags, translating your app to different
cultures and languages, and direct rendering from your external server.
In the preceding chapter we covered some different ways of solving the same prob-
lems we’ve previously solved, but using OSML. In this chapter we’ll do a little more of
that, plus introduce some new features that open even more doors to our app.

Remember, though, OSML is a very late addition to the OpenSocial spec, and it is
supported beginning only with version 0.9 of the OpenSocial specification; previous
versions do not recognize OSML.
Inline Tag Templates
Inline tag templates are reusable components that are declared directly inside the main
gadget XML.They look very similar to standard inline templates with one exception:
the presence of a tag attribute in the declaring template script tag.Templates defined
in this manner are not rendered in place but are instead registered as custom tags for use
later in the app.
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
Defining and Using a Tag Template
Tag templates are an easy and convenient way to create reusable display elements. For the
Tic-Tac-Toe app, we’re going to convert the Player Info box (shown in Figure 11.1) to a
custom tag.This element is used for the current Viewer as well as an opponent. Our
custom tag will display based on the person object passed in as a parameter.
Creating the Custom Tag Template Definition
The first step is to create our custom tag definition. In the following steps we’ll convert
the existing Player Info box into a custom tag template for use in our app.We’ll
generalize it so that it can be used for both the Viewer and the opponent.
1. Create a new template script element in the Content block of your Canvas code.
This element should appear at the top of the block, before any other template
scripts. In addition to the normal template script tags, it has an additional attribute
of tag="my:PlayerInfo".
<Content type="html" view="canvas">
<script type="text/os-template" tag="my:PlayerInfo">
</script>
2. Find the Player Info box markup in our existing app gadget. It can be found by
searching for the div with an ID of myinfo. Copy and paste the contents of the

myinfo div into our new tag:
<script type="text/os-template" tag="my:PlayerInfo">
<div id="myinfo" class="player_info right_column">
<div class="left"><a href="javascript:openProfile();">
<img class="profile_image" src="${viewer.thumbnailUrl}" /></a>
<div class="record" id="myrecord"></div></div>
<div>${viewer.displayName}<div></div><div></div><div></div>
<div class="more">
<a href="javascript:showViewerBio();">More </a></div></div>
<div class="clear"></div>
</div>
</script>
240 Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
Figure 11.1 Player Info box.
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
Inline Tag Templates 241
3. Now we’re going to clean out the hard-coded data and change the tag template to
use parameters. Parameters are local values passed in from a tag instance.They are
accessible through the reserved variable ${My}. Our tag requires that a person
object be passed into the tag instance in the Player element. Replace all the
${viewer.X} statements with ${My.Player.X} statements:
<script type="text/os-template" tag="my:PlayerInfo">
<div id="myinfo" class="player_info right_column">
<div class="left"><a href="javascript:openProfile();">
<img class="profile_image" <div class="record" id="myrecord"></div></div>
<div>${My.Player.displayName}<div></div><div></div><div></div>
<div class="more">
<a href="javascript:showPlayerBio(${My.Player.id});">

More </a></div></div>
<div class="clear"></div>
</div>
</script>
4. Add an additional borderColor parameter to specify the border color.This
parameter value will be placed in an inline style in the containing div.
<div id="myinfo" style="border:2px solid ${My.borderColor};"
class="player_info right_column" >
5. Remove the id="myinfo" attribute from the main div. Each instance tag has an
individual ID, so this should not be in the template. ID values from the tag
instance are carried through to the resolved tag.
6. Modify the styles to accommodate the blue/pink (male/female) background colors
on this element. It would be a little tedious to do an "if" test within this markup
for male/female on the My.Player object, so we’ll accommodate this feature by
using style class names instead.The player_info style definition will have a blue
background (default), and an additional player_info_FEMALE style will be
defined to override the background to pink:
.player_info
{
border:solid 2px black;
padding:5px;
background-color:#09f;
}
.player_info_FEMALE
{
background-color:#fcf;
}
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg

7. Add an expression statement as a final CSS class name in the main div so that the
gender is included in the style class name. In this way the style class name evaluates
to "player_info_MALE" or "player_info_FEMALE", removing the need for any
"if" checks.
<div style="border:2px solid ${My.borderColor};"
class="player_info right_column player_info_${My.Player.gender.key}">
Using the Custom Tag
Now that we’ve defined our template, the next step is to use it.We’re going to add
instances of the custom tag for both the current Viewer and for the opponent, if present.
1. Search again in the code for the div with an ID of
myinfo. Delete this entire div
and all contained elements.
2. Add in place of the myinfo div an instance of our my:PlayerInfo tag with the
id="myinfo":
<my:PlayerInfo id="myinfo" >
</my:PlayerInfo>
3. Add in parameter elements for Player and borderColor.The Player value
should be ${viewer} and the borderColor can be any valid CSS color string or
color hex.We chose the color green.
<my:PlayerInfo id="myinfo" >
<Player>${viewer}</Player>
<borderColor>green</borderColor>
</my:PlayerInfo>
4. Save and view your updated app in the Sandbox Editor.The Player Info box
should display as before.
You can download this code and a full code listing for this chapter from our Google
Code repository at />trunk/chapter11.
Using Client-Side Templates
Custom templates are also available for use in client-side code.All your custom templates
are available for use on the client side via the new opensocial.template namespace.

This allows your app to create and render new template instances on the client.The call
sequence looks something like this:
var templateInst = opensocial.template.getTemplate("my:CustomTag");
var tdata = {}
tdata["background"] = "red";
242 Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
Inline Tag Templates 243
tdata["otherParam"] = 1;
templateInst.renderInto("target_element_id", tdata);
This code creates a new instance of the tag template defined for my:CustomTag. It then
builds some data to pass in to the template as local parameters and renders the instance
to a target div element target_element_id.
Warning
Client-side templates are processed by the browser’s DOM engine. As such, they are
sometimes subject to the various “features” of each DOM implementation. Some browsers
attempt to perform tag balancing or add in new elements to get the markup to conform to
their idea of what a proper DOM structure should look like. The net result is that templates
that render perfectly on the server may not render correctly on the client.
To avoid client-side template-rendering issues, use only complete DOM element blocks
as a template—for example, div elements, fully enclosed tables, and the like. Avoid start-
ing your templates with elements that are typically nested in other elements, such as list
items (
LI) and table rows (TR). Tables in particular are problematic because many
browsers introduce a multitude of new elements, such as
TBODY, THEAD, and TFOOT,
without telling you. Favor styled div blocks in these instances. The same problem also
exists with client-side repeaters and tables. If you wish to repeat a table row (

TR) element,
you must make use of attribute-based repeaters, not the
os:Repeat element in your
template.
We’re going to use client templates to render the opponent’s display block using our
my:PlayerInfo custom tag template.
Client-Side Rendering with Custom Tags
Because apps are dynamic, the information that is needed doesn’t always exist at server
render time. User interactions affect the app. In our case, the opponent is selected after
the initial app load.To address this, we use the templating JavaScript methods to
dynamically render our custom tag after the opponent has been selected.
In the following steps we will convert the opponentinfo div element from markup
that required tedious editing in JavaScript code to a second implementation of our
custom my:PlayerInfo template.
1. Edit the div element with an ID of opponentinfo and delete all the contents.
Also remove the style class attribute so that we are left with an empty div element.
<div id="opponentinfo" >
</div>
2. Add an os:PeopleSelector tag element immediately above the opponentinfo
div defined in step 1. It should specify the group as the contents of our friends
data item and a var="selectedOpponent" to have the selected friend placed in
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
the DataContext under that key.We’ll also tie its select behavior into our existing
selectOpponent method.This replaces the client-side FriendPicker we added in
Chapter 7, Flushing and Fleshing.
<os:PeopleSelector group="${friends}" var="selectedOpponent"
onselect="selectOpponent()" />
</div>

3. Remove the div with an ID of opponentPicker and comment out the call to
loadFriendPicker since this is now being handled by the os:PeopleSelector.
You may skip this step if you did not implement app data person-to-person play
(discussed and outlined in Chapter 7).
4. Define a new function to trigger the client-side rendering of a new template
instance in the
opponentinfo div named updateOpponentTemplate.This
function gets a template instance using the opensocial.template.getTemplate
function and then calls renderInto to render the template into our
opponentinfo div.The function looks like this:
function updateOpponentTemplate(player){
var elem = document.getElementById("opponentinfo");
if(!player){
elem.innerHTML = "";
return;
}
//Get a new instance of the template
var tplate = opensocial.template.getTemplate("my:PlayerInfo");
//Construct the data to pass into the template
var data = {};
data["Player"] = player;
data["borderColor"] = "red";
//Render the template into our target div
tplate.renderInto(elem, data);
}
5. Update the function selectOpponent to invoke this new method:
function selectOpponent(player){
updateOpponentTemplate(player);

}

Now, in addition to setting up a game challenge, the display will update to show the
opponent’s information using our template.
244 Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
Working with Subviews 245
Working with Subviews
Subviews are a new concept introduced with OSML. So, yes, that means they work only
starting with version 0.9 of the OpenSocial spec. If you’re writing or editing an app for a
lower version, you won’t be able to use subviews.
In addition to the main surface views (Home, Profile, and Canvas), subviews allow for
multiple views or view parts to be defined on a particular surface.The app can then
“navigate” to these subviews without forcing a page reload.
Using subviews is quite simple.All you have to do is name the subview in a Content
block’s view attribute, then use the following call:
gadgets.views.requestNavigateTo(<target_view_name>);
This call causes the target subview to show, and all other subviews are hidden.
Subviews are useful, but they do have some design and behavior characteristics you
should be aware of:
n
Subview Content blocks must consist of coherent, well-formed XHTML.
n
Adjacent non-subview Content blocks must also be well formed (this means no
partial nesting of elements).
n
By default, activating one subview turns off all other subviews.
n
On initial load, all subviews are hidden.
Converting Tabs to Subviews

We’re going to reimplement our tab interface (shown in Figure 11.2) by making use of
subviews. Changing the interface in this way allows us to make use of the built-in view
navigation call gadgets.views.requestNavigateTo instead of manually manipulating
CSS classes or styles on the elements. It gives us the added benefit of not having to write
loops to manage turning off other elements that should be hidden; it’s all handled by the
subview processing.
Figure 11.2 Tab interface in app.
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
246 Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
Editing the Markup to Use Subviews for Tab Content
Tabs are currently defined as div blocks.We need to modify the markup so that the
header is a Content block, the footer is a Content block, and each subview is a
Content block.This allows our app to control the display of the tabs as subviews but still
display the header and footer in a consistent manner.
1. Find the location of the first tab content container by searching for the div with
an ID of play_container.
2. Close out the Content block above this by adding the closing template script and
Content block tags.The code will look like this:
</div>
</script>
</Content>
<div id="play_container"
3. Add a new Content block and template script tag around the play_container
div. For the view attribute, set the value to canvas.play. This defines the play
subview on the Canvas.
<Content type="html" view="canvas.play">
<script type="text/os-template">
<div id="play_container" class="container">


</div>
</script>
</Content>
4. Add similar wrappers around the other tab content div elements. Name the new
subviews as defined in Table 11.1 (the subview name goes into the view attribute
of the Content block element).
5. Edit each tab content element’s CSS class attribute to remove the hide class:
<div id="invite_container" class="container">
Table 11.1 New Subview Names
Tab Div Subview Name
invite_container canvas.invite
challenge_container canvas.challenge
set_background_container canvas.pickBackground
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
ptg
6. Enclose the markup after the tab content container elements in a standard
Content block with the view attribute set to canvas.
Modifying the Script to Use Subviews
The code currently has a TTT.Tabs object.We will modify this object to use subviews
instead of direct DOM manipulation to do tab management.
1. Edit the TTT.Tab object to simplify its representation. It will now hold the sub-
view name instead of a reference to the tab container DOM element.
TTT.Tab = function(tab_dom, view, action){
this.tab_dom = tab_dom;
this.view = view;
this.action = action;
};
2. Modify the initTabs function in TTT.Tabs to pass in the view name instead of

the DOM reference when creating each TTT.Tab object:
_initTabs:function(){
this._tabs[0] = new TTT.Tab(document.getElementById("tab0"),
"canvas.play", playClicked);
this._tabs[1] = new TTT.Tab(document.getElementById("tab1"),
"canvas.invite", inviteClicked);
this._tabs[2] = new TTT.Tab(document.getElementById("tab2"),
"canvas.challenge", challengeClicked);
this._tabs[3] = new TTT.Tab(document.getElementById("tab3"),
"canvas.setBackground", setBackgroundClicked);
}
3. Modify the selectTab function in TTT.Tabs to use the requestNavigateTo
call. As part of this modification, you must also remove the calls to
container.show(); and container.hide();.
selectTab:function(index){
if(0 === this._tabs.length){
this._initTabs();
}
var tab = this._tabs[index];
if(tab){
gadgets.views.requestNavigateTo(tab.view);
}
else{
return;
}

}
Working with Subviews 247
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.

ptg
4. Add a call in the loadInitializer to activate the canvas.play subview:
function loadInitializer(){

gadgets.views.requestNavigateTo("canvas.play");

}
We haven’t actually saved much code by making this change. Primarily, this is
because the code still has to modify the display of the tab.The OpenSocial spec
group is considering proposals to add activate/deactivate event handlers to subviews,
but at the time of this writing this functionality has not been settled in the OSML
specification.
The well-formedness constraint can be a problem in some interfaces with tag nesting.
As a result, subviews tend to lend themselves best to absolutely positioned elements (for
example,
<div style="position:absolute;">xxx</div>) or root children DOM
elements of the view. In this way the subviews do not interfere with layout flow when
being enabled or disabled.
HTML Fragment Rendering
One of the most common paradigms we have found with OpenSocial apps is that of
content rewriting. Many apps perform an initial load of a Bootstrap page, then call back
to their external servers for the actual app content and perform a wholesale innerHTML
rewrite of the app.While this is very flexible, it is also a very slow user experience.As we
previously mentioned, OSML brings a number of new options to the table for fine-
grained content rewriting.
In our app we’re going to add a tab to view one of our favorite sites and then render
a banner ad from a pretend ad server URL (actually, a historic Web site).
Adding Content with os:Get
In this section we’ll create a new tab that displays content from an external site.We’ll use
the os:Get tag to pull content directly from the external site and display it inline on a

subview.
1. Add a new tab to our series of tabs. Search for the div with an ID of tab3. Create
a copy of this immediately after as tab4 and adjust the text to be “Laugh.” Also
adjust the CSS class definitions as shown here:
<div id="tab3" class="left tab" onclick="TTT.Tabs.selectTab(3);">
Set Background</div>
<div id="tab4" class="left tab tab_last"
onclick="TTT.Tabs.selectTab(4);">
Laugh</div>
248 Chapter 11 Advanced OSML: Templates, Internationalization, and View Navigation
From the Library of Lee Bogdanoff
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.

×