We have moved our blog to the following address:
http://blog.ctp.com/category/microsoft-competence/
In case you have any comment, please leave it using the new URL. Soon we will stop monitoring the blog entries in this site. Also our new material will be posted only in the new URL.
Thanks!
Introduction
In SharePoint 2013, App Parts are a great way of aggregating
the content from distinct Apps on a hosted page. App Parts use iFrames to
surface the App’s content and because Apps are in distinct domains than the hosted
web, they cannot manipulate directly the hosted page at DOM level (for details
please refer to the Same
Origin Policy that web browsers enforce).
Although security constraints, there are cases where the Apps
should be allowed to make changes on the hosted page. For instance, when the
content the Apps are displaying does not fit into their App Parts. In this
case, SharePoint 2013 allows the resizing of App Parts through the HTML5 Cross-Document
Messaging. Find below an example about how this functionality works.
The Code
Find here the page of a SharePoint 2013 App which can be referenced
by an App Part. The App page requests the hosted page to resize the App Part as
the content grows or shrinks by using the method postMessage. The hosted page in
SharePoint takes care of doing the resizing itself.
To test the code, just create a SharePoint 2013 Hosted App, add
to the project a Client Web Part (known as well as App Part), create an ASPX
page with the code below and set the Client Web Part to reference to that page.
Afterwards, deploy the App and add the Client Web Part into a page in the
hosted web. Detailed steps can be found in the References section below.
<%@ Page language="C#" Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage,
Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral,
PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<!-- SharePoint pages require this when displaying them
in an App Part. -->
<!-- (Reference:
http://msdn.microsoft.com/en-us/library/jj220046.aspx) -->
<WebPartPages:AllowFraming ID="AllowFraming" runat="server" />
<html>
<head>
<title>App Part Communica</title>
<script src="../Scripts/jquery-1.7.1.min.js"></script>
<script type="text/javascript">
// Set the style of the app part page to be consistent
with the host web.
// Get the URL of the host web and load the styling of it.
function setStyleSheet() {
var hostUrl = ""
if (document.URL.indexOf("?") != -1)
{
var params = document.URL.split("?")[1].split("&");
for (var i = 0; i < params.length;
i++) {
p = decodeURIComponent(params[i]);
if (/^SPHostUrl=/i.test(p))
{
hostUrl = p.split("=")[1];
document.write("<link
rel=\"stylesheet\" href=\"" +
hostUrl +
"/_layouts/15/defaultcss.ashx\"
/>");
break;
}
}
}
// if no host web URL was available, load the default
styling
if (hostUrl == "") {
document.write("<link
rel=\"stylesheet\" " +
"href=\"/_layouts/15/1033/styles/themable/corev15.css\"
/>");
}
}
setStyleSheet();
</script>
</head>
<body style="background-color: #f5f5f5">
<!-- the content considered for the resizing -->
<div id="content">
<p>SenderId: <span id="senderId"></span></p>
<input type="button" onclick="Communica.Part.addItem();" value="Add Item"/>
<input type="button" onclick="Communica.Part.removeItem();" value="Remove Item"/>
<ul id="itemsList">
<li>Item</li>
</ul>
</div>
<script lang="javascript">
"use strict";
// define a namespace
window.Communica = window.Communica || {};
$(document).ready(function () {
// initialise
Communica.Part.init();
});
Communica.Part = {
senderId: '', // the App Part provides a Sender Id in the URL
parameters,
// every time the App Part is loaded, a new Id is
generated.
// The Sender Id identifies the rendered App Part.
previousHeight: 0, // the height
minHeight: 0, // the minimal allowed height
firstResize: true, // On the first call of the
resize the App Part might be
// already too small for the content, so force to resize.
init: function () {
// parse the URL parameters and get the Sender Id
var params = document.URL.split("?")[1].split("&");
for (var i = 0; i < params.length;
i = i + 1) {
var param = params[i].split("=");
if (param[0].toLowerCase() == "senderid")
this.senderId
= decodeURIComponent(param[1]);
}
// find the height of the app part, uses it as the minimal
allowed height
this.previousHeight = this.minHeight = $('body').height();
// display the Sender Id
$('#senderId').text(this.senderId);
// make an initial resize (good if the content is already
bigger than the
// App Part)
this.adjustSize();
},
adjustSize: function () {
// Post the request to resize the App Part, but just if
has to make a resize
var step = 30, // the recommended increment step is of 30px. Source:
// http://msdn.microsoft.com/en-us/library/jj220046.aspx
width = $('body').width(), // the App Part width
height = $('body').height() + 7, // the App Part height
// (now it's 7px more than the body)
newHeight, // the new App Part height
contentHeight = $('#content').height(),
resizeMessage =
'<message
senderId={Sender_ID}>resize({Width}, {Height})</message>';
// if the content height is smaller than the App Part's
height,
// shrink the app part, but just until the minimal allowed
height
if (contentHeight < height -
step && contentHeight >= this.minHeight) {
height = contentHeight;
}
// if the content is bigger or smaller then the App Part
// (or is the first resize)
if (this.previousHeight !== height ||
this.firstResize === true) {
// perform the resizing
// define the new height within the given increment
newHeight = Math.floor(height / step) * step +
step * Math.ceil((height / step) - Math.floor(height / step));
// set the parameters
resizeMessage =
resizeMessage.replace("{Sender_ID}", this.senderId);
resizeMessage = resizeMessage.replace("{Height}", newHeight);
resizeMessage = resizeMessage.replace("{Width}", width);
// we are not changing the width here, but we could
// post the message
window.parent.postMessage(resizeMessage, "*");
// memorize the height
this.previousHeight = newHeight;
// further resizes are not the first ones
this.firstResize = false;
}
},
addItem:
function () {
// add an item to the list
$('#itemsList').append('<li>Item</li>');
Communica.Part.adjustSize();
},
removeItem: function () {
// remove an item from the list
$('#itemsList li:last').remove();
Communica.Part.adjustSize();
}
};
</script>
</body>
</html>
Conclusion
The App's page (grey background) in an App Part. |
After the content grew, the App Part has resized. |
If the content shrinks, the code requests the App Part to be reduced. |
References
- How to: Create app parts to deploy with apps for SharePoint
- Apps for SharePoint UX design guidelines