Persisting your Office app settings – Behind the scenes!

One of the common requirements of any kind of app is to save its settings and state. It is same for an Office app too. But since an Office app is nothing but a web page running in an IFrame, saving any kind of information on the user’s machine seems to be a huge challenge. Fortunately the Office App model provides a simple way to save app settings or any other kind of information.

Office App model can store the settings in three different ways:

  1. For Task Pane and Content apps, Settings are stored as part of the Document itself.
  2. For mail apps, settings are stored with a specific item (mail, meeting, etc.) as CustomProperties.
  3. For mail apps, settings can also be stored in user’s mailbox and accessed from anywhere as part of RoamingSettings object.

In this blog post, we will learn more about persisting the app settings by saving settings for a Word Task Pane App. In this case all the settings are saved as JSON object in the document itself. These saved properties cannot be accessed by other apps other than the app which created them. We can add as many settings as we want as key/value pairs.

Step 1: Create the Project

Lets create a brand new Task Pane app for Word 2013 and name it “PersistSettings”. (You can follow the steps here to create a Task Pane app.)

Now that our app is created and replace the contents of <body> tag in PersistSettings.html with this:

 <div id="Content">
 <input type='button' value='Persist' id="persistValBtn" style="margin-right: 10px; padding: 0px; width: 100px;" />
 <input type='button' value='Remove' id="removeBtn" style="margin-right: 10px; padding: 0px; width: 100px;" />
 <input type="text" id="selectedData" style="margin-top: 10px;width:210px" />
 <div id="persistMsg"></div>
 </div>

 

We have added two buttons, one text box and a div to display any message we may want to show here.

Now open PersistSettings.js and replace the contents of the file with the following:

 // Add any initialization logic to this function.
 Office.initialize = function (reason) {
 $("#selectedData").val(Office.context.document.settings.get("textBoxValue"));
 // Checks for the DOM to load.
 $(document).ready(function () {
 $("#persistValBtn").click(function () {
 persistValue();
 });
 $("#removeBtn").click(function () {
 removePersistedValue();
 });
 });
 };
 
 function persistValue() {
 Office.context.document.settings.set("textBoxValue", $("#selectedData").val());
 Office.context.document.settings.saveAsync(function (result) {
 if (result.status == Office.AsyncResultStatus.Succeeded) {
 $("#persistMsg").html("The persisted text is - " + $("#selectedData").val());
 }
 });
 }
 
 function removePersistedValue() {
 Office.context.document.settings.remove("textBoxValue");
 Office.context.document.settings.saveAsync(function (result) {
 if (result.status == Office.AsyncResultStatus.Succeeded) {
 $("#selectedData").val("");
 $("#persistMsg").html("You have cleared the persisted text");
 } else if (result.status == Office.AsyncResultStatus.Failed) {
 $("#persistMsg").html(result.error.message);
 }
 });
 }

 

Step 2: Analyzing what we have written

In Office.initialize method we have hooked up our button click events and called persistValue and removePersistedValue methods for Persist and Remove buttons respectively. We have also tried to check if we had already saved some settings by name of textBoxValue and tried to retrieve it and assign it to our text box. This is done by accessing the settings object provided by the document. As mentioned earlier, settings are saved per app per document inside the document itself. Hopefully this is clear by what how we are trying to retrieve the saved setting.

In persistValue method, we have called the settings.set method. This method creates a key/value pair where the key is the setting name we have provided (textBoxValue) and the value is the actual value that we are trying to save which is the text in the text box.

One very important point here is that settings will not get saved unless we have called the settings.saveAsync method. This method provides an optional callback where we can check whether our save request succeeded.

Similarly in removePersistedValue method, we call the settings.remove method to remove our saved setting. If there is no key with the name which we are trying to remove, nothing will happen. In this case also, we have to call settings.saveAsync method to actually persist the settings.

There is another method exposed by the settings object called refreshAsync. This is useful in co-authoring scenarios e.g. multiple users are working on the same document and we want to refresh the settings of our app for all the users.

Behind the scenes

Now lets go behind the scenes to see how actually our data gets saved in the document. Open up a new Word document, insert our app and type in something in the text box of our app and click on Persist button. A message informing the success of save operation should appear. Now try to Close the Word document. You will be presented with a prompt to save the document even though you have not actually typed anything in the document! For now just click Yes, name your document and save it somewhere on your disk.

Now change the extension of your save Word document to .zip by adding .zip to the file name and answering yes to all the warning prompts. Now, our document is a zipped file. Open the file see to see a similar folder structure as shown below.

Untitled

Go to word\webextensions folder where you will see a file with the name webextension1.xml. Open the file and the structure should be something similar to this.

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <we:webextension xmlns:we="schemas.microsoft.com/office/webextensions/webextension/2010/11"
 id="{3F204971-93F1-4C77-A5E0-05729E38D1CD}">
 <we:reference id="788362e0-c142-4c23-afbc-2f402aa07cf6"  version="1.0" 
 store="\\<networkShare>\Users\Public\Documents\OfficeAppManifests" storeType="Filesystem"/>
 <we:alternateReferences/>
 <we:properties>
  <we:property name="textBoxValue" value="&quot;Persisted Text&quot;"/> 
 </we:properties>
 <we:bindings/>
 <we:snapshot xmlns:r="schemas.openxmlformats.org/officeDocument/2006/relationships"/>
 </we:webextension>

 

Here we can see our setting being saved as a Property object. Our app is identified by the we:reference id. This id is same as that specified in the manifest of our Office app. Now we know how a setting is saved per app and per document!

Caution

Since the settings are saved in the document itself, the experience for end-user can be jarring if it is not implemented properly.

  1. As in the example shown in this article, the user has not typed in anything in the document but still gets a Save prompt. This can cause confusion amongst end-users. So you should always make it clear that doing certain operation either via user-interaction or by code will change the state of the document.
  2. If the document is under source-control or some kind of content management system and the user uses your app which changes the state of the document and check out from Source Control. This can again be confusing for the user.
  3. Modifying the document would also change the Last Modified By and Last Saved Time of the document. In certain scenarios, users rely on these properties to get their work done. Your app can break their experience in these cases.

Conclusion

While settings are pretty useful and simple way of storing app properties, they should be used with caution and always inform the user while changing the document.

The complete solution created in this post is attached below.

PersistSettings.zip