Work with host web data from JavaScript in the add-in web
Important
The SharePoint Add-In model in SharePoint Online has been deprecated as of November 27th 2023, checkout the full retirement announcement to learn more.
Deprecation means that the feature will not get any new investments, but it's still supported. SharePoint add-in model is retired fully on April 2nd, 2026 and is no longer available after that time. Primary replacement technology for the SharePoint add-in model is SharePoint Framework (SPFx) which continues to be supported also in future.
This is the eleventh in a series of articles about the basics of developing SharePoint-hosted SharePoint Add-ins. You should first be familiar with SharePoint Add-ins and the previous articles in this series, which you can find at Get started creating SharePoint-hosted SharePoint Add-ins | Next steps.
Note
If you have been working through this series about SharePoint-hosted add-ins, you have a Visual Studio solution that you can use to continue with this topic. You can also download the repository at SharePoint_SP-hosted_Add-Ins_Tutorials and open the BeforeHostWebData.sln file.
By default, SharePoint is designed to prevent JavaScript in an add-in from getting access to data in other SharePoint websites on the farm. This prevents script in a rogue add-in from getting access to sensitive data. But often an add-in needs to have access to the host web, or to other websites within the same site collection as the host web.
There are two parts to enabling this scenario in your add-in:
- You request permission to the host web in the add-in manifest file of your add-in. The user who installs the add-in is prompted to grant this permission, and the add-in cannot be installed if the user does not.
- Instead of using an SP.ClientContext object to make JSOM calls to the host web, you use an SP.AppContextSite object. This object enables the add-in to get a context object for websites other than the add-in web, but only for websites within the same site collection. (There is also a way to get access to any website in the SharePoint Online subscription [or an on-premises SharePoint Web Application], but that is an advanced subject.)
In this article you use the JSOM to find the orientations that are not yet started and ensure that they are scheduled on a calendar in the host web.
Prepare the host web calendar
Open the host web (your developer test website) and verify that there is a calendar on it named Employee Orientation Schedule and it has a single event on it: Orient Cassie Hicks. If there isn't, take the following steps:
From the home page of the site, select Site Contents > add an add-in > Calendar.
In the Adding Calendar dialog, enter Employee Orientation Schedule for the Name, and then select Create.
When the calendar opens, put the cursor on any date until the Add link appears on the date, and then select Add.
In the Employee Orientation Schedule - New Item dialog, enter Orient Cassi Hicks for the Title. Leave the other fields at their defaults, and select Save.
The calendar should look similar to the following:
Figure 1. Custom calendar
Create the JavaScript and a button to invoke it
Open the Add-in.js file in the Scripts node in Solution Explorer.
Add the following declarations under the declaration for
completedItems
.var notStartedItems; var calendarList; var scheduledItems;
- The
notStartedItems
references the items on the New Employees in Seattle list whose Orientation Stage is Not Started. - The
calendarList
references the calendar you created on the host web. - The
scheduledItems
references a collection of items on the calendar.
- The
When a SharePoint Add-in is run, SharePoint calls its start page and adds some query parameters to the start page URL. One of these is
SPHostUrl
which is, of course, the URL of the host web. The add-in needs this information to make calls to host web data, so near the top of the Add-in.js file, just under the variable declaration forscheduledItems
, add the following line.var hostWebURL = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
Note the following about this code:
- The
getQueryStringParameter
is a utility function that you create in the next step. - The
decodeUriComponent
is a standard JavaScript function that reverses the URI-encoding that SharePoint does on the query parameters; for example, an encoded forward slash, "%2F", is changed back to a "/".
- The
Add the following code to the bottom of the file. This function can be used to read the query parameters.
// Utility functions function getQueryStringParameter(paramToRetrieve) { var params = document.URL.split("?")[1].split("&"); var strParams = ""; for (var i = 0; i < params.length; i = i + 1) { var singleParam = params[i].split("="); if (singleParam[0] == paramToRetrieve) { return singleParam[1]; } } }
Add the following function to the Add-in.js file somewhere above the failure callbacks section.
function ensureOrientationScheduling() { var camlQuery = new SP.CamlQuery(); camlQuery.set_viewXml( '<View><Query><Where><Eq>' + '<FieldRef Name=\'OrientationStage\'/><Value Type=\'Choice\'>Not started</Value>' + '</Eq></Where></Query></View>'); notStartedItems = employeeList.getItems(camlQuery); clientContext.load(notStartedItems); clientContext.executeQueryAsync(getScheduledOrientations, onGetNotStartedItemsFail); return false; }
Note the following about this code:
- This is nearly identical to the list query method that gets the Completed items, except that it gets items that are Not Started instead of those that are Completed. We are interested in only the Not Started items because the script makes the simplifying assumption that if an orientation is past the Not Started stage, it must already be scheduled.
- You create the two callback methods in the executeQueryAsync call in later steps.
Add the following function to the Add-in.js file just under the preceding function. Notice that it uses the hostWebContext object to identify the list that is queried.
function getScheduledOrientations() { var hostWebContext = new SP.AppContextSite(clientContext, hostWebURL); calendarList = hostWebContext.get_web().get_lists().getByTitle('Employee Orientation Schedule'); var camlQuery = new SP.CamlQuery(); scheduledItems = calendarList.getItems(camlQuery); clientContext.load(scheduledItems); clientContext.executeQueryAsync(scheduleAsNeeded, onGetScheduledItemsFail); }
Note
Notice that no query markup is added to the CAML query. The effect of having no actual query in the query object is to ensure that all of the list items will be retrieved. If the list was very large, this might cause the request to the server to be unacceptably long-running. In that case, we'd want to find some other way of accomplishing our goal. But in this sample situation with a very small list (and calendar lists are almost always small), getting the whole list so that we can iterate through it on the client will actually help us minimize the number of calls to the server; that is, calls of executeQueryAsync.
Add the following function to the file.
function scheduleAsNeeded() { var unscheduledItems = false; var dayOfMonth = '10'; var listItemEnumerator = notStartedItems.getEnumerator(); while (listItemEnumerator.moveNext()) { var alreadyScheduled = false; var notStartedItem = listItemEnumerator.get_current(); var calendarEventEnumerator = scheduledItems.getEnumerator(); while (calendarEventEnumerator.moveNext()) { var scheduledEvent = calendarEventEnumerator.get_current(); // The SP.ListItem.get_item('field_name ') method gets the value of the specified field. if (scheduledEvent.get_item('Title').indexOf(notStartedItem.get_item('Title')) > -1) { alreadyScheduled = true; break; } } if (alreadyScheduled === false) { // SP.ListItemCreationInformation holds the information the SharePoint server needs to // create a list item var calendarItem = new SP.ListItemCreationInformation(); // The some_list .additem method tells the server which list to add // the item to. var itemToCreate = calendarList.addItem(calendarItem); // The some_item .set_item method sets the value of the specified field. itemToCreate.set_item('Title', 'Orient ' + notStartedItem.get_item('Title')); // The EventDate and EndDate are the start and stop times of an event. itemToCreate.set_item('EventDate', '2015-06-' + dayOfMonth + 'T21:00:00Z'); itemToCreate.set_item('EndDate', '2015-06-' + dayOfMonth + 'T23:00:00Z'); dayOfMonth++; // The update method tells the server to commit the changes to the SharePoint database. itemToCreate.update(); unscheduledItems = true; } } if (unscheduledItems) { calendarList.update(); clientContext.executeQueryAsync(onScheduleItemsSuccess, onScheduleItemsFail); } }
Note the following about this code:
The method checks to see if the title of a Not Started item in the New Employees In Seattle list, which is the name of an employee, is contained in the title of an event in the Employee Orientation Schedule calendar. There is a simplifying assumption that all entries in the calendar are created with the full employee name in the event title.
If none of the events that are already on the calendar matches a Not Started item, the script creates a calendar item for the Not Started item.
JSOM uses a lightweight ListItemCreationInformation object instead of an SPListItem object to minimize the size of the payload that is sent to the SharePoint server.
The two DateTime fields of the new calendar event are set to days in the month when this article was written:
2015-06
. Change these dates to a day in the current month and year, so you don't have to scroll back in your calendar to find the items.If any Not Started items are found to be unscheduled, the first one will be scheduled for the 10th of the month. Each additional unscheduled item will be scheduled for a day later. The simplifying assumption is that there won't be so many that some are scheduled for impossible days of the month, such as "32".
Most of this code is standard JavaScript. There are comments for the lines that use SharePoint JSOM.
Add the following success handler that is called when the previously unscheduled items are added to the calendar.
function onScheduleItemsSuccess() { alert('There was one or more unscheduled orientations and they have been added to the ' + 'Employee Orientation Schedule calendar.'); }
Add the following failure functions to the Failure callbacks section of the file.
function onGetNotStartedItemsFail(sender, args) { alert('Unable to get the not-started items. Error:' + args.get_message() + '\n' + args.get_stackTrace()); } function onGetScheduledItemsFail(sender, args) { alert('Unable to get scheduled items from host web. Error:' + args.get_message() + '\n' + args.get_stackTrace()); } function onScheduleItemsFail(sender, args) { alert('Unable to schedule items on host web calendar. Error:' + args.get_message() + '\n' + args.get_stackTrace()); }
Open the default.aspx file and find the asp:Content element with the ID PlaceHolderMain.
Add the following markup just under the
purgeCompletedItems
button.<p><asp:Button runat="server" OnClientClick="return ensureOrientationScheduling()" ID="ensureorientationschedulingbutton" Text="Ensure all items are on the Calendar" /></p>
Rebuild the project in Visual Studio.
To minimize the need to manually set the Orientation Stage of list items to Not Started while testing the add-in, open the elements.xml file for the list instance NewEmployeesInSeattle (not the elements.xml for the list template NewEmployeeOrientation), and ensure that the Orientation Stage value for at least three of the Row elements, including the row for Cassie Hicks, will have the value Not Started. Because that is the default value, the simplest way to do this is to ensure that there is no Field element for
OrientationStage
for the three (or more) rows.The following is an example of how the Rows element should look.
<Rows> <Row> <Field Name="Title">Tom Higginbotham</Field> <Field Name="Division">Manufacturing</Field> <Field Name="OrientationStage">Completed</Field> </Row> <Row> <Field Name="Title">Satomi Hayakawa</Field> </Row> <Row> <Field Name="Title">Cassi Hicks</Field> </Row> <Row> <Field Name="Title">Lertchai Treetawatchaiwong</Field> </Row> </Rows>
Specify the permissions to the host web that the add-in needs
Your add-in automatically has full control permission to its own add-in web, so until now you have not needed to specify what permissions it needs. But you must specifically request permissions to the host web to interact with its data. The Employee Orientation add-in needs permission to add items to the calendar in the host web.
From Solution Explorer, open the appmanifest.xml file.
In the manifest designer, open the Permissions tab.
In the top row of the Scope column, select List from the drop-down.
In the Permission column, select Manage.
If the Properties column is left blank, the add-in is asking for write permission to every list on the host web. It is a good practice to limit add-ins to only the permissions that they need. There isn't any way in the add-in manifest to limit permissions to a specific list instance, but it is possible to limit the add-in to only list instances that are built on a specific base list template. The base list template of a calendar is Events whose numeric ID is 106.
Select the Properties cell of the same row to make the Edit button appear in the cell. The permissions list should now look similar to the following.
Figure 2. Permission list with Edit button visible
Select Edit to open the Properties dialog.
Set Name to BaseTemplateId, and set Value to 106. The dialog should now look like the following.
Figure 3. List permission properties dialog
Select OK. The Permissions tab should now look similar to the following.
Figure 4. Permissions tab of add-in manifest designer in Visual Studio
Run and test the add-in
Be sure the host web calendar is prepared as described earlier in this article. It should have a single event, named Orient Cassi Hicks.
Enable pop-up windows in the browser that Visual Studio uses when you debug.
Use the F5 key to deploy and run your add-in. Visual Studio makes a temporary installation of the add-in on your test SharePoint site and immediately runs the add-in.
The permission consent form opens where you can grant the add-in the permission it seeks. There is a drop-down list on the page where you can choose from among all the calendars on the host web. Select Employee Orientation Schedule, and then select Trust It.
Figure 5. SharePoint Add-in consent prompt
When the add-in's start page has completely loaded, select the Ensure Items are Scheduled button.
Figure 6. Employee Orientation home page with new button
If any of the failure callback functions is run, you will see the error message alert that your callback functions create. Otherwise, you will see the success message created by the final success callback: There was one or more unscheduled orientations and they have been added to the Employee Orientation Schedule calendar.
Go to the Employee Orientation Schedule calendar on the host web. For example, select the breadcrumb link to your developer site's home page and select Site Contents. Select the Employee Orientation Schedule tile (not the Employee Orientation tile).
The calendar should look similar to the following. The script detected that there was already an event for Cassi Hicks, so it did not create a second one for her. It created events for the other two employees whose orientation was in the Not Started state. It also did not create an event for the employee whose orientation was past the Not Started state.
Figure 7. Calendar after two new events added
Ensure that you delete the two new events from the calendar before you select Ensure Items are Scheduled again.
To end the debugging session, close the browser window or stop debugging in Visual Studio. Each time that you select F5, Visual Studio retracts the previous version of the add-in and installs the latest one.
You will work with this add-in and Visual Studio solution in other articles, and it's a good practice to retract the add-in one last time when you are done working with it for a while. Right-click the project in Solution Explorer and select Retract.
Next steps
Go on to advanced work in SharePoint-hosted SharePoint Add-ins: