Lesson 09: Storing and Retrieving AppData

From VZ Developer Wiki
Jump to: navigation, search

General Guidelines | XML Specification | Features | Views | JavaScript API | REST API | Tutorials | OAuth


Lesson 08: Getting People Data Back to overview Lesson 10: Interact with your own Backend

Giving gifts

Now it's time to implement the raison d'être of your gadget, giving gifts. In this section, we will modify the gadget to allow the viewer to give a gift to one of their friends.

First, you'll need to modify the basic HTML in the gadget specification so that it can insert new information for gift giving into the layout. The resultant XML looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Gifts part 2 - Send Gifts">
    <Require feature="opensocial-0.8"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script type="text/javascript">
        /* ... */
      </script>
      <div id='main'>'''
        <div id='give'>
          <form id='gift_form'>
            Give <span id='gifts'></span> to <span id='friends'></span>. 
            <a href='javascript:void(0);' onclick='giveGift();'>Give!</a>
          </form>
        </div>'''
      </div>
    ]]>
  </Content>
</Module>

Now that there are nice hooks into the HTML, modify the output of the friends list into a set of option tags for use within a select tag. This will allow you to select a friend to receive a gift.

function onLoadFriends(data) {
  var viewer = data.get('viewer').getData();
  var viewerFriends = data.get('viewerFriends').getData();

  html = new Array();
  html.push('<select id="person">');
  viewerFriends.each(function(person) {
    if (person.getId()) {
      html.push('<option value="', person.getId(), '">', person.getDisplayName(), '</option>');
    }
  });
  html.push('</select>');
  document.getElementById('friends').innerHTML = html.join('');
}

Next, you'll need to create another selection menu of gifts you can give. The sample uses a selection of different types of nuts, but you can feel free to use whatever you like. A small update to the initialization function calls this function when the page loads.

var globalGiftList = ['a cashew nut', 'a peanut', 'a hazelnut', 'a red pistachio nut'];

function makeOptionsMenu() {
  var html = new Array();
  html.push('<select id="nut">');
  for (var i = 0; i < globalGiftList.length; i++) {
    html.push('<option value="', i, '">', globalGiftList[i], '</option>');
  }
  html.push('</select>');
  document.getElementById('gifts').innerHTML = html.join('');
}

function init() {
  loadFriends();
  makeOptionsMenu();
}

To tie all of this together, implement giveGift, the function called when a user clicks the "Give!" button in the gadget. The function loads the gift to be given and the friend to give it to, from the form, updates a global object of gifts, and saves this to the persistent storage.

var globalGivenGifts = {};

function giveGift() {
  var nut = document.getElementById('nut').value;
  var friend = document.getElementById('person').value;

  globalGivenGifts[friend] = nut;
  var json = gadgets.json.stringify(globalGivenGifts);

  var req = opensocial.newDataRequest();
  req.add(req.newUpdatePersonAppDataRequest("VIEWER", 'gifts', json));
  req.send();
}

Showing your generosity

Although your gift gadget can give gifts, once they're sent, they go into a vacuum and you're never really sure they've been sent. It would be helpful if the gadget could list the gifts that have been given. In this section we will modify the gadget to load the list of gifts that the viewer has sent to other people.

You could cheat a little bit here, and just use the global object globalGivenGifts, but that would only display gifts that you've given in any one session, because right now it isn't linked to any persistent storage. Also, you wouldn't know whether your requests were actually successful, just that you'd sent them (the JavaScript object is currently updated regardless of success). A global object is a convenient way to store the gifts that you've sent, though, so if you keep it updated by linking to persistent storage, it will serve as a suitable place to load the data.

You'll need to update the global list of gifts in two instances. First, you'll need to update it when you load the gadget, to see all gives you've given previously. Second, you'll need to update it when you give a new gift, to both make sure that the gift was sent, and to keep your local object fresh.

First, add two requests onto your dataRequest object in giveGift to fetch the viewer's information, and the viewer's friends (for association purposes), when it makes the request. Then, take advantage of the opportunity to add a callback to your request to send a gift.

function giveGift() {
  var nut = document.getElementById('nut').value;
  var friend = document.getElementById('person').value;

  globalGivenGifts[friend] = nut;
  var json = gadgets.json.stringify(globalGivenGifts);

  var req = opensocial.newDataRequest();
  req.add(req.newUpdatePersonAppDataRequest("VIEWER", 'gifts', json));
  '''req.add(req.newFetchPersonRequest("VIEWER"), 'viewer');
  
  var viewerFriends = opensocial.newIdSpec({ "userId" : "VIEWER", "groupId" : "FRIENDS" });
  var opt_params = {};
  opt_params[opensocial.DataRequest.PeopleRequestFields.MAX] = 100;
  req.add(req.newFetchPeopleRequest(viewerFriends, opt_params), 'viewerFriends');

  var viewer = opensocial.newIdSpec({ "userId" : "VIEWER" });
  req.add(req.newFetchPersonAppDataRequest(viewer, 'gifts'), 'data');'''
  req.send(onLoadFriends);
}

If you modify your initial request to load friend data, you can reuse the onLoadFriends function to handle both paths of execution.

function loadFriends() {
  var req = opensocial.newDataRequest();
  req.add(req.newFetchPersonRequest("VIEWER"), 'viewer');
	
  var viewerFriends = opensocial.newIdSpec({ "userId" : "VIEWER", "groupId" : "FRIENDS" });
  var opt_params = {};
  opt_params[opensocial.DataRequest.PeopleRequestFields.MAX] = 100;
  req.add(req.newFetchPeopleRequest(viewerFriends, opt_params), 'viewerFriends');

  var viewer = opensocial.newIdSpec({ "userId" : "VIEWER" });
  '''req.add(req.newFetchPersonAppDataRequest(viewer, 'gifts', opt_params), 'data');'''
  req.send(onLoadFriends);
}

Your onLoadFriends function now calls another function that will handle the display of your given gifts.

function onLoadFriends(data) {
  var viewer = data.get('viewer').getData();
  var viewerFriends = data.get('viewerFriends').getData();
  var giftData = data.get('data').getData();

  html = new Array();
  html.push('<select id="person">');
  viewerFriends.each(function(person) {
    if (person.getId()) {
      html.push('<option value="', person.getId(), '">', person.getDisplayName(), '</option>');
    }
  });
  html.push('</select>');
  document.getElementById('friends').innerHTML = html.join('');

  '''updateGiftList(viewer, giftData, viewerFriends);'''
}

Now, you'll need to write an updateGiftList function to update the global object and display the results, when the gadget gets back data. The sample function below is robust enough to not throw an exception when the list is blank, but bad data will cause the global list of gifts to be blank (and fail silently).

function updateGiftList(viewer, data, friends) {
  var json = null;
  if (data[viewer.getId()]) {
    json = data[viewer.getId()]['gifts'];
  }

  if (!json) {
    globalGivenGifts = {};
  }
  try {
    globalGivenGifts = gadgets.json.parse(gadgets.util.unescapeString(json));
  } catch (e) {
    globalGivenGifts = {};
  }

  var html = new Array();
  html.push('You have given:');
  html.push('<ul>');
  for (i in globalGivenGifts) {
    if (i.hasOwnProperty) {
      html.push('<li>', friends.getById(i).getDisplayName(), ' received ', globalGiftList[globalGivenGifts[i]], '</li>');
    }
  }
  html.push('</ul>');
  document.getElementById('given').innerHTML = html.join('');
}

The last thing you'll need is a hook in the HTML where you can insert the list of given gifts:

<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Gifts part 3 - Showing Gifts">
    <Require feature="opensocial-0.8"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script type="text/javascript">
        /* ... */
      </script>
      <div id='main'>
        <div id='give'>
          <form id='gift_form'>
            Give <span id='gifts'></span> to <span id='friends'></span>. <a href='javascript:void(0);' onclick='giveGift();'>Give!</a>
          </form>
        </div>
        '''<div id='given'></div>'''
      </div>
    ]]>
  </Content>
</Module>

Showing gifts you've received

So far, the gift gadget sends gifts to your friends, but your friends have no way of knowing they've received them. When they see their gift application, it doesn't tell them what other people have sent them, just what they, themselves have sent. In this section we will modify the gadget to list items that the viewer's friends have given him or her.

Adding the ability to see what others have sent you isn't too difficult, but it does require some clever use of persistent storage. Currently an application can only write to the persistent storage of the viewer of the gadget, so this gadget stores all the gifts you've given in the 'gifts' field of the viewer's application data. When one of your friends views her instance of the gadget, the gadget will have to seek out the gifts that she has received by checking the 'gifts' field of each of her friends. In short, you can only write to your own storage, but you can read the application data of your friends (as long as they have the app installed, too).

To start, add another hook into the HTML as a placeholder for the list of received gifts.

<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Gifts part 4 - Showing What You Got">
    <Require feature="opensocial-0.8"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script type="text/javascript">
        /* ... */
      </script>
      <div id='main'>
        <div id='give'>
          <form id='gift_form'>
            Give <span id='gifts'></span> to <span id='friends'></span>. <a href="javascript:void(0);" onclick='giveGift();'>Give!</a>
          </form>
        </div>
        <div id='given'></div>
        '''<div id='received'></div>'''
      </div>
    ]]>
  </Content>
</Module>

Next, you'll need to make a number of small changes to the functions that load persistent data. First, update loadFriends to request the application data for the viewer's friends.

function loadFriends() {
  var req = opensocial.newDataRequest();
  req.add(req.newFetchPersonRequest("VIEWER"), 'viewer');
	
  var viewerFriends = opensocial.newIdSpec({ "userId" : "VIEWER", "groupId" : "FRIENDS" });
  var opt_params = {};
  opt_params[opensocial.DataRequest.PeopleRequestFields.MAX] = 100;
  req.add(req.newFetchPeopleRequest(viewerFriends, opt_params), 'viewerFriends');

  var viewer = opensocial.newIdSpec({ "userId" : "VIEWER" });
  req.add(req.newFetchPersonAppDataRequest(viewer, 'gifts'), 'data');

  '''req.add(req.newFetchPersonAppDataRequest(viewerFriends, 'gifts', opt_params), 'viewerFriendData');'''
  req.send(onLoadFriends);
}

Then, update giveGift to do the same. (Remember that these two entry points to updating data rely on one callback function, so the data needs to be consistently fetched.)

function giveGift() {
  var nut = document.getElementById('nut').value;
  var friend = document.getElementById('person').value;

  givenGifts[friend] = nut;
  var json = gadgets.json.stringify(givenGifts);

  var req = opensocial.newDataRequest();
  req.add(req.newUpdatePersonAppDataRequest("VIEWER", 'gifts', json));
  req.add(req.newFetchPersonRequest("VIEWER"), 'viewer');
	
  var viewerFriends = opensocial.newIdSpec({ "userId" : "VIEWER", "groupId" : "FRIENDS" });
  var opt_params = {};
  opt_params[opensocial.DataRequest.PeopleRequestFields.MAX] = 100;
  req.add(req.newFetchPeopleRequest(viewerFriends, opt_params), 'viewerFriends');

  var viewer = opensocial.newIdSpec({ "userId" : "VIEWER" });
  req.add(req.newFetchPersonAppDataRequest(viewer, 'gifts'), 'data');

  '''req.add(req.newFetchPersonAppDataRequest(viewerFriends, 'gifts', opt_params), 'viewerFriendData');'''
  req.send(onLoadFriends);
}

Third, update the callback function onLoadFriends to pull the data of the owner's friends out of the returned data, and pass it along to the function that will do the real work, updateReceivedList.

function onLoadFriends(data) {
  var viewer = data.get('viewer').getData();
  var viewerFriends = data.get('viewerFriends').getData();
  var giftData = data.get('data').getData();
  '''var viewerFriendData = data.get('viewerFriendData').getData();'''

  html = new Array();
  html.push('<select id="person">');
  viewerFriends.each(function(person) {
    if (person.getId()) {
      html.push('<option value="', person.getId(), '">', person.getDisplayName(), '</option>');
    }
  });
  html.push('</select>');
  document.getElementById('friends').innerHTML = html.join('');

  updateGiftList(viewer, giftData, viewerFriends);
  '''updateReceivedList(viewer, viewerFriendData, viewerFriends);'''
}

The final change, implementing updateReceivedList closely parallels updateGiftList, but rather than iterating once through the list of gifts you've sent, iterates once through the gifts each of your friends have sent, and pulls out just the ones for you. These are collected nicely, and displayed.

function updateReceivedList(viewer, data, friends) {
  var viewerId = viewer.getId();

  var html = new Array();
  html.push('You have received:<ul>');
  friends.each(function(person) {
    if (data[person.getId()]) {
      var json = data[person.getId()]['gifts'];

      var gifts = {}
      if (!json) {
        gifts = {};
      }
      try {
        gifts = gadgets.json.parse(gadgets.util.unescapeString(json));
      } catch (e) {
        gifts = {};
      }

      for (i in gifts) {
        if (i.hasOwnProperty && i == viewerId) {
          html.push('<li>', globalGiftList[gifts[i]], ' from ', person.getDisplayName(), '</li>');
        }
      }
    }
  });
  html.push('</ul>');
  document.getElementById('received').innerHTML = html.join('');
}



  • Lesson 09: Storing and Retrieving AppData