The concept of views may be completely foreign to many application developers, but it’s a critical one to understand when you’re developing gadgets for social networking containers.
Having a cohesive view architecture is vital to an application’s success. There are countless instances where an application with a good concept fails because its developer expects people to use it from a specific view, so he neglects the other views by simply putting an image of, or call to action to load, the larger view (or vice versa, if he develops a small view but ignores the large).
The most important point here is that a user will never use your products in exactly the way that you intended them to be used. For this reason, you must spend equal time thinking about all of the views and how they interact, rather than focusing 99% of your attention on a single view and leaving the others as an afterthought. Some users may want to interact only with the subset of functionality defined in a small view, while others want to use all of the views depending on the particular tasks they want to complete at a given time. With this in mind, let’s explore view functionality in the context of an application gadget.
Each container defines its own views and variations depending on its use for gadgets (e.g., whether the container is inherently social in nature or geared to business applications). Table 3-9 outlines the view types that can be used in many of the available containers.
Table 3-9. View types
Many of the current containers implementing the OpenSocial standard are listed on the OpenSocial wiki at http://wiki.opensocial.org/index.php?title=Containers. If you click the links to view details, you’ll find some more container-specific information, including the supported view names.
Now that you understand the container views, we’ll explore the numerous methods for defining the views that you would like the gadget to build upon.
If you are using inline markup within the Content
nodes of your gadget instead of
loading markup from an external file, you should always wrap the
content in <![CDATA[ ... Content ...
]]
tags. This will prevent the markup from being rendered
as part of the gadget XML file—that is, as gadget-specific tags—when
it loads.
A base-level Content
section
within a gadget includes a few items. Within
the ModulePrefs
element,
if you want to be able to work with views through the gadget
JavaScript layer, you will need to require the views feature by using
the Require
element. Following
that, you can define a Content
element with the view you want to load the content for. If you wish to
inline your HTML, CSS, and JavaScript within the gadget, you should
wrap the code within the Content
section in CDATA
tags. This will
prevent your HTML elements from being treated as if they were part of
the gadgets XML spec.
We can build upon our definition of a single Content
section by defining additional
sections the same way; we simply define a new Content
element with another view. These new
Content
elements may include either
inline or proxied content and will be treated in the
same way as the original section.
If you want one Content
section
to define the content for multiple sections, you may define multiple
comma-separated view names within the view
attribute. This
technique is especially useful when you have multiple small views within a container (such as home
and profile
) and wish to serve up the same
content in each.
There may be instances where you have pieces of content, such as a
header, that should appear in multiple views. Instead of repeating the
same code within each Content
section for each view, you can create multiple Content
sections with redundant views; when
the gadget is rendered, the Content
sections are concatenated together into a single code base for each
section.
If we load the preceding example in a container’s profile view, it produces the following:
This is my footer |
If we load the example in the canvas view, however, the two sections are concatenated and give us the following:
This is the content of my larger canvas view |
This is my footer |
In a social networking gadget container, Content
sections are strictly tied to
specific pages on the site. For instance, if I had a profile on a site
and a gadget loaded on that profile, I would expect the Content
section with the view
attribute set to profile
to load in that view. This is how
containers are able to delegate locations for each section of a
gadget. But this is not the only method for loading different Content
sections—there are also JavaScript
methods available within your gadget to push users through from one
view to the next while keeping them in the context of your
application. You enable these JavaScript functions by adding a
Require
element for the views
feature.
This following example defines two views, profile
and
canvas
, and uses an onclick
handler to include a button within
each view. These onclick
handlers
invoke the OpenSocial JavaScript gadgets view method requestNavigateTo
to
forward the user browser to the view specified as the function’s
parameter:
<Module> <ModulePrefs> <Require feature="views" /> </ModulePrefs> <Content view="profile"> <![CDATA[ <button onclick="gadgets.views.requestNavigateTo('canvas'), "> Click to navigate to the canvas view </button> ]]> </Content> <Content view="canvas"> <![CDATA[ <button onclick="gadgets.views.requestNavigateTo('profile'), "> Click to navigate to the profile view </button> ]]> </Content> </Module>
The full code for this sample is available at https://github.com/jcleblanc/programming-social-applications/blob/master/chapter_3/navigating_between_views.xml.
This functionality allows you to control the view flow for your application. You could define a subset of functionality on the profile view, for instance, and include a call to action for the user to click a button to go to the canvas view, where she can see the full range of functionality for the application.
While the navigation functionality is a valuable asset for controlling the flow of your application, you may need to pass data between the views during the navigation process. If the user were to set temporary state details in the profile view and then navigate to the canvas view, for example, you would want to pass that state information to the canvas view for processing.
You can pass information this way by extending the requestNavigateTo(...)
parameter list to
include a second parameter. This second parameter is a string
containing all of the information to be passed to the next
view:
<Module> <ModulePrefs> <Require feature="views" /> </ModulePrefs> <Content view="profile"> <![CDATA[ <script type="text/javascript"> function loadCanvas(postData){ gadgets.views.requestNavigateTo('canvas', postData); } </script> <button onclick="loadCanvas('user123'),"> Click to navigate to the canvas view </button> ]]> </Content> <Content view="canvas"> <![CDATA[ <div id="postData"></div> <script type="text/javascript"> var postVal = gadgets.views.getParams(); document.getElementById('postData').innerHTML = postVal; </script> ]]> </Content> </Module>
The full code for this sample is available at https://github.com/jcleblanc/programming-social-applications/blob/master/chapter_3/passing_data_between_views.xml.
Our profile view button will call a function, passing in a
string representing a user variable. This variable is
passed as the second parameter to our requestNavigate
To(...)
method. The browser
will then forward the user to the canvas view. Once the canvas view content loads, we capture the data
passed to the view with the gadgets
.views.getParams(...)
method and insert it into a div
in
the view.
Subviews are a way to define different content pages (or slates) for a single view. They allow the developer to switch the content of a particular view without having to control that content flow with JavaScript. The developer will need JavaScript only to navigate from a main view to the subview.
To define a subview, you create a Content
section as
you did previously, but you name the view using the convention
view.subviewname
. For instance, if I want
to create a subview named “sub” that resides on the canvas view, I
would set the view
attribute to
canvas.sub
. We can navigate to the subview
by making a request to requestNavigate
To(...)
, as
we’ve done before, and insert
view.subviewname
(e.g., canvas.sub
) as the first
parameter:
<Module>
<ModulePrefs>
<Require feature="views" />
</ModulePrefs>
<Content view="canvas">
<![CDATA[
<button onclick="gadgets.views.requestNavigateTo('canvas.sub'),">
Click to navigate to the canvas sub page
</button>
]]>
</Content>
<Content view="canvas.sub">
<![CDATA[
This is the content of my subpage
]]>
</Content>
</Module>
The full code for this sample is available at https://github.com/jcleblanc/programming-social-applications/blob/master/chapter_3/working_with_subviews.xml.
When the preceding code runs and the user is viewing the canvas
view, she will see a button asking her to click to navigate to the
canvas subview. When she clicks this button, the content of the canvas
view will be replaced with the content defined in the Content
section with the view
of canvas.sub
.
If you are implementing views that use a proxied content approach
(i.e., that link to an external file via the href
attribute), the OpenSocial
specification defines standard methods for handling HTTP error states
automatically within a gadget. Containers should process these content
views, but at the very least they should display meaningful error
messages to the user.
You should integrate an error view into your gadgets wherever possible. In an ideal world, your code will always work and your servers will have 100% uptime, but this is the real world—things break. An error fallback view, where you can display a pleasant “something went wrong” message with relevant links or instructions, is always better than an impersonal HTTP error message.
If containers encounter an error while trying to retrieve the
proxied content, they should
obtain the error message from a view with a name of
viewname.error
. So, for example, if the
container gets a 404 Not Found error message when trying to retrieve
the content for the canvas view, it should display an error message
from content obtained from a view named canvas.error
:
<Module> <ModulePrefs> <Require feature="views" /> </ModulePrefs> <Content view="canvas" href="http://www.mysite.com/canvas.php" /> <Content type="html" view="canvas.error"> <![CDATA[ An error occurred, please refresh the application window. ]]> </Content> </Module>
The full code for this sample is available at https://github.com/jcleblanc/programming-social-applications/blob/master/chapter_3/defining_error_view_states.xml.
In the preceding example, should a request to http://www.mysite.com/canvas.php fail, the container should display the message “An error occurred, please refresh the application window” to the user instead of something like “HTTP 404 Error: Resource Not Found.”
When a container supports these error views, they should be
defined for each Content
section
that uses a proxied content approach.
3.133.126.199