SharePoint 2010 Single Page Apps without a Master Page
- by David Jacobus
Originally posted on: http://geekswithblogs.net/djacobus/archive/2014/06/06/156827.aspxWell, maybe a stretch, but I am inclined to believe it is so. I have been using the JavaScript Client Object Model (JCSOM) for about 6 months now and I believe it can do about 80% of my job quickly without much fanfare. When building sites in SharePoint no one wants the OOTB list views, etc. They want a custom look and feel! I used to think in previous engagements that this would mean some custom server code or at least a data-form web part. Since coming on-board in my current engagement, I have been forced because we don’t own the hosting site to come up with innovative ways to customize the UI of SharePoint. We can push content via sandbox solutions and use JCSOM from within SharePoint Designer to do almost all customizations. I have been using the following methodology to accomplish this: 1. Create an HTML file, which links CSS and JavaScript Files 2. Create and ASPX Web Part Page, Include a Content Editor Web Part and link to the HTML page created above. So basically once it was done, I could copy , paste, and rename those 4 items: CC, JS, HTML. ASPX and using MVVM just change the Model, View, and View-Model in the JavaScript file. in about 5 minutes, I could create a completely new web part with SharePoint data. Styling would take a little longer. Some issues that would crop up: 1. Multiple(s) of these web parts would not work well together on the same page (context). 2. To separate the Web parts and context I would create a separate page for each web part and link them to a tabs layout via a Page Viewer web part or I frame. Easy to do and not a problem but a big load problem as these web part pages even with minimal master had huge footprints. (master page and page web part zones) I kept thinking of my experience with SharePoint 2013 and apps! The JavaScript was loaded from within the app, why can’t we do that in 2010 and skip the master page and web part zones. I thought at first, just link to sp.js but that didn’t work so I searched the web and found a link which did not work at all in my environment but helped me create a solution that would kudos to (Will). <!DOCTYPE html>
<%@ Page %>
<%@ Register Tagprefix="SharePoint"
Namespace="Microsoft.SharePoint.WebControls"
Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<html>
<head>
<link rel="Stylesheet" type="text/css" href="../CSS/smoothness/jquery-ui-1.10.4.custom.min.css">
</head>
<body >
<form runat="server">
<!-- the following 5 js files are required to use CSOM -->
<script src="/_layouts/1033/init.js" type="text/javascript" ></script>
<script src="/_layouts/MicrosoftAjax.js" type="text/javascript" ></script>
<script src="/_layouts/sp.core.js" type="text/javascript" ></script>
<script src="/_layouts/sp.runtime.js" type="text/javascript" ></script>
<script src="/_layouts/sp.js" type="text/javascript" ></script>
<!-- include your app code -->
<script src="../scripts/jquery-1.9.1.js" type="text/javascript" ></script>
<script src="../Scripts/jquery-ui-1.10.3.custom.min.js" type="text/javascript"></script>
<script src="../scripts/App.js" type="text/javascript" ></script>
<div ID="Wrapper">
</div>
<SharePoint:FormDigest ID="FormDigest1" runat="server"></SharePoint:FormDigest>
</form>
</body>
</html>
Notice that I have the scripts loaded within the body! I discovered this by accident in trying to get Will’s solution to work, it made this work just like normal JCSOM from the master page. I am sure there are other ways to do this, but I am a full time developer, so I’ll let someone else investigate the alternatives. I have an example page showing an Announcements list as a Booklet which is a JQuery Plug-In.
Here is the page source notice the footprint is light.
<!DOCTYPE html>
<html>
<head>
<link rel="Stylesheet" type="text/css" href="../CSS/smoothness/jquery-ui-1.10.4.custom.min.css">
<link href="../CSS/jquery.booklet.latest.css" type="text/css" rel="stylesheet" media="screen, projection, tv" />
<link href="../CSS/bookletannouncement.css" type="text/css" rel="stylesheet" media="screen, projection, tv" />
</head>
<body >
<form name="ctl00" method="post" action="BookletAnnouncements2.aspx" onsubmit="javascript:return WebForm_OnSubmit();" id="ctl00">
<div>
<input type="hidden" name="__REQUESTDIGEST" id="__REQUESTDIGEST" value="0x3384922A8349572E3D76DC68A3F7A0984CEC14CB9669817CCA584681B54417F7FDD579F940335DCEC95CFFAC78ADDD60420F7AA82F60A8BC1BB4B9B9A57F9309,06 Jun 2014 14:13:27 -0000" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUBMGRk20t+bh/NWY1sZwphwb24pIxjUbo=" />
</div>
<script type="text/javascript">
//<![CDATA[
var g_presenceEnabled = true;var _fV4UI=true;var _spPageContextInfo = {webServerRelativeUrl: "\u002fsites\u002fDemo50\u002fTeamSite", webLanguage: 1033, currentLanguage: 1033, webUIVersion:4,pageListId:"{ee707b5f-e246-4246-9e55-8db11d09a8cb}",pageItemId:167,userId:1, alertsEnabled:false, siteServerRelativeUrl: "\u002fsites\u002fdemo50", allowSilverlightPrompt:'True'};//]]>
</script>
<script type="text/javascript" src="/_layouts/1033/init.js?rev=lEi61hsCxcBAfvfQNZA%2FsQ%3D%3D"></script>
<script type="text/javascript">
//<![CDATA[
function WebForm_OnSubmit() {
UpdateFormDigest('\u002fsites\u002fDemo50\u002fTeamSite', 1440000);
return true;
}
//]]>
</script>
<!-- the following 5 js files are required to use CSOM -->
<script src="/_layouts/1033/init.js"></script>
<script src="/_layouts/MicrosoftAjax.js"></script>
<script src="/_layouts/sp.core.js"></script>
<script src="/_layouts/sp.runtime.js"></script>
<script src="/_layouts/sp.js"></script>
<!-- include your app code -->
<script src="../scripts/jquery-1.9.1.js"></script>
<script src="../Scripts/jquery-ui-1.10.3.custom.min.js" type="text/javascript"></script>
<script src="../Scripts/jquery.easing.1.3.js"></script>
<script src="../Scripts/jquery.booklet.latest.min.js"></script>
<script src="../scripts/Announcementsbooklet.js"></script>
<div ID="Accord">
</div>
<script type="text/javascript">
//<![CDATA[
var _spFormDigestRefreshInterval = 1440000;//]]>
</script>
</form>
</body>
</html>
Here is the source to make the booklet work:
ExecuteOrDelayUntilScriptLoaded(retrieveListItems, "sp.js");
var context;
var collListItem;
var web;
var listRootFolder;
var oList;
//retieve the list items from the host web
function retrieveListItems() {
context = SP.ClientContext.get_current();
web = context.get_web();
oList = context.get_web().get_lists().getByTitle('Announcements');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><RowLimit>10</RowLimit></View>');
collListItem = oList.getItems(camlQuery);
listRootFolder = oList.get_rootFolder();
context.load(listRootFolder);
context.load(web);
context.load(collListItem);
context.executeQueryAsync(onQuerySucceeded, onQueryFailed);
}
//Model object
var Dev = function (id, title, body, expires, url) {
var self = this;
self.ID = id;
self.Title = title;
self.Body = body;
self.Expires = expires;
self.Url = url;
}
//View model
var DevVM = new ListViewModel()
function ListViewModel() {
var self = this;
self.items = new Array();
}
function onQuerySucceeded(sender, args) {
var listItemEnumerator = collListItem.getEnumerator();
while (listItemEnumerator.moveNext()) {
var oListItem = listItemEnumerator.get_current();
var javaDate = oListItem.get_item('Expires');
var fmtExpires = javaDate.format('dd MMM yyyy');
var url = "";
var goodUrl = oListItem.get_item('Url');
if (goodUrl == null) {
url = web.get_serverRelativeUrl() + "/Lists/Announcements/EditForm.aspx?ID=" + oListItem.get_item('ID');
}
else {
url = web.get_serverRelativeUrl() + oListItem.get_item('Url')
}
DevVM.items.push(new Dev(oListItem.get_item('ID'), oListItem.get_item('Title'), oListItem.get_item('Body'), fmtExpires, url));
}
$.each(DevVM.items, function (index) {
$("#Accord").append(createAccordNode(DevVM.items[index].Title, DevVM.items[index].Body, " Expires: " + DevVM.items[index].Expires, DevVM.items[index].Url));
});
$("#Accord").booklet();
}
function createAccordNode(title, body, expires, url) {
return (
$("<div><h3>" + title + "</h3><p><span class='titlespan'><a href='" + url + "'>" + title + "</a></span><span class='dicussionspan'>" + body + "</span><span class='expiresspan'>" + expires + "</span></p></div>")
);
}
function onQueryFailed(sender, args) {
alert('Request failed. ' + args.get_message() +
'\n' + args.get_stackTrace());
}
The idea behind this post is that this could be used to:
1. Create landing pages that are very un-SharePoint like!
2. Make lightweight pages that could be used in page viewer web part or I Frame.
3. Utilize Deep Zoom Composer and Sea-Dragon/or Silver light
I will be using this for much of my development work!