88 articles and 1,205 comments as of Thursday, September 8th, 2016

Tuesday, June 29, 2010

Client Side AJAX Applications in SharePoint 2010 – Part 6: Master-Details

Guest Author: Lee Richardson
http://rapidapplicationdevelopment.blogspot.com/

ASP.Net AJAX Templating is a compelling new client side technology from Microsoft that allows developers to more quickly build responsive and maintainable AJAX applications.  Because ASP.Net AJAX Templating and SharePoint 2010 both support the oData protocol they are a powerful combination.  This post in the series will focus on a particularly nice feature of ASP.Net AJAX Templating: master-details views.

Showing .selectedData

In order to show the currently selected item we will need some HTML that we can pop up that will bind to the currently selected list item.  Here’s the simplest possible example that will still work:

<div id="userStoryDetails" class="sys-template">{{ Title }}</div>

Notice the class of sys-template?  As mentioned in a previous post that class is required for any elements that a dataView attaches to.  It sets the element to display: none when the page is loaded, but the dataView sets it back to display: block when it renders.

Next, we’ll need a second dataView object that we’ll bind to the HTML above using the jQuery syntax:

detailsDataView = $("#userStoryDetails").dataView().get(0);

Note that we didn’t set the dataProvider or fetchOperation as we did for the main dataView.  The reason is that we will bind its data property to the main dataView’s selectedData property like this:

$create(Sys.Binding, {
	source: dataView,
	path: "selectedData",
	target: detailsDataView,
	targetProperty: "data"
});

selectedData is a special property on dataView that represents the currently selected item.  We’re pretty close to completing the master-details scenario.  The main item that remains is telling the master dataView when to update the selectedData.  We can accomplish this by simply adding sys:command="select" onto an element within the main dataView’s template.  Adding it to the button that opens the modal dialog is a logical location:

<a href="#" onclick="javascript:openDialog(); return false;" sys:command="select">
	<img src="/_layouts/images/edititem.gif" />
</a>

And now we’re in business:


Flushing out the Master’s Details

So far the details view is a bit sparse.  In the next article we will allow editing the fields of a particular user story and support saving it back to SharePoint.  For now, however, let’s add some additional read-only fields:

<div id="userStoryDetails" class="sys-template">
  <table class="ms-formtable" width="100%">
    <tr>
      <td class="ms-formlabel" width="190">Title:</td>
      <td class="ms-formbody">{{ Title }}</td>
    </tr>
    <tr>
      <td class="ms-formlabel" width="190">Points:</td>
      <td class="ms-formbody">{{ Points }}</td>
    </tr>
    <tr>
      <td colspan="2" nowrap>
        <span>Created at <span>
        <span>{{ String.format("{0:M/d/y h:m tt}", Created) }}</span>

        <input type="button" name="OK" value="OK" class="ms-ButtonHeightWidth"
          onclick="SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK); return false;" />
        <input type="button" name="Cancel" value="Cancel" class="ms-ButtonHeightWidth"
          onclick="SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel); return false;" />
      </td>
    </tr>
  </table>
</div>

There are two things to note about the details view above.  The first is that we can display fields like Points that are not displayed by the main dataView (assuming they exist in the list).  This works because the master dataView downloaded all fields off of the main list by default.  How to retrieve data from related lists will the topic of a future article.

The second item to note is how easily we can deal with dates and times.  String.format is a method provided by the AJAX Library that can format the dates provided by SharePoint exactly like the C# / VB format specifiers readers are likely used to.  Here’s what the new details view looks like:


Here is the source for the entire application so far

<%@ Assembly Name="PreDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0a3e0ca56c5f97ba" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UserStories.aspx.cs" Inherits="PreDemo.Layouts.PreDemo.UserStories" DynamicMasterPageFile="~masterurl/default.master" %>

<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
	<STYLE type="text/css">
		.sys-template
		{
			display: none;
		}
		.userStoryBackground
		{
			background-image: url('Images/corkboard4.png');
			width: 100%;
			height: 600px;
			position: relative;
		}
		.userStoryCard
		{
			border: 1px solid #777777;
			width: 200px;
			cursor: move;
			background-image: url('Images/blankcard4.png');
			position: absolute;
		}
		.userStoryDescription
		{
			font-size: 13px;
			padding: 0px 5px 5px 5px;
		}
		.userStoryTitle
		{
			font-size: 15px;
			font-weight: bold;
			padding: 0px 0px 0px 0px;
			padding: 0px 5px 0px 5px;
		}
		.userStoryButtons
		{
			position: absolute;
			right: 0px;
			padding: 2px 2px 0 0;
		}
		.userStoryButtons img
		{
			border: 0 none;
		}
	</STYLE>

	<script
		src="../Scripts/jquery-ui-1.8.1.custom/js/jquery-1.4.2.min.js"
		type="text/javascript"></script>

	<script
		src="../Scripts/jquery-ui-1.8.1.custom/js/jquery-ui-1.8.1.custom.min.js"
		type="text/javascript"></script>

	<script
		src="../Scripts/MicrosoftAjax/jQueryStart.js"
		type="text/javascript"></script>

	<script
		src="../Scripts/MicrosoftAjax/MicrosoftAjax.js"
		type="text/javascript"></script>

	<script type="text/javascript">
		Sys.require([
			Sys.scripts.jQuery,
			Sys.components.dataView,
			Sys.components.openDataServiceProxy
			]);

		var dataView;
		var detailsDataView;
		var dataContext;
		var userStoryDetails;

		Sys.onReady(function () {
			userStoryDetails = document.getElementById('userStoryDetails');

			dataContext = Sys.create.openDataContext({
				serviceUri: "/Demo/_vti_bin/ListData.svc"
			});

			dataView = $("#userStories").dataView({
				dataProvider: dataContext,
				fetchOperation: "UserStories",
				fetchParameters: { orderby: 'Title' },
				autoFetch: "true",
				rendered: onRendered
			}).get(0);

			detailsDataView = $("#userStoryDetails").dataView().get(0);

			$create(Sys.Binding, {
				source: dataView,
				path: "selectedData",
				target: detailsDataView,
				targetProperty: "data"
			});
		});

		function onRendered() {
			$(".userStoryCard").draggable({
				stop: onDragStop
			});
		}

		function onDragStop(event, ui) {
			var userStoryCard = ui.helper[0];
			var selectedUserStoryJsonObject = dataView.findContext(userStoryCard).dataItem;

			var newX = ui.position.left;
			var newY = ui.position.top;

			Sys.Observer.setValue(selectedUserStoryJsonObject, "X", newX);
			Sys.Observer.setValue(selectedUserStoryJsonObject, "Y", newY);

			dataContext.saveChanges();
		}

		function openDialog() {
			var options = {
				html: userStoryDetails,
				width: 500,
				height: 200,
				title: "User Story",
				dialogReturnValueCallback: onDialogClose
			};
			SP.UI.ModalDialog.showModalDialog(options);
		}

		function onDialogClose(dialogResult, returnValue) {
			if (dialogResult == SP.UI.DialogResult.OK) {

			}
			if (dialogResult == SP.UI.DialogResult.cancel) {

			}
		}
	</script>
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
	<div id="userStoryDetails" class="sys-template">
		<table class="ms-formtable" width="100%">
			<tr>
				<td class="ms-formlabel" width="190">Title:</td>
				<td class="ms-formbody">{{ Title }}</td>
			</tr>
			<tr>
				<td class="ms-formlabel" width="190">Points:</td>
				<td class="ms-formbody">{{ Points }}</td>
			</tr>
			<tr>
				<td colspan="2">
					<span>Created at <span>
					<span>{{ String.format("{0:M/d/y h:m tt}", Created) }}</span>
					<span>
					<input type="button" name="OK" value="OK"
						onclick="SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK); return false;" accesskey="O" class="ms-ButtonHeightWidth" target="_self" />
					<input type="button" name="Cancel" value="Cancel"
						onclick="SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel, 'Cancelled clicked'); return false;" accesskey="C" class="ms-ButtonHeightWidth" target="_self" />
					</span>
				</td>
			</tr>
		</table>
	</div>

	<div id="userStories"
		class="sys-template userStoryBackground"
		xmlns:sys="javascript:Sys">

		<div class="userStoryCard"
			sys:style="{{ 'left: ' + X + 'px; top: ' + Y + 'px;'}}">

			<div class="userStoryTitle">
				{{ Title }}
				<span class="userStoryButtons">
					<a href="#" onclick="javascript:openDialog(); return false;" sys:command="select">
						<img src="/_layouts/images/edititem.gif" />
					</a>
				</span>
			</div>
			<div class="userStoryDescription"><div>{{ Description }}</div>
		</div>
	</div>
</asp:Content>

<asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
Pre Demo
</asp:Content>

<asp:Content ID="PageTitleInTitleArea" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server" >
Pre Demo
</asp:Content>

Summary

We’ve seen how to data bind a details dataView to the currently selected item of a master dataView and formatting options for different data types.  The next article in the series will update the detailsView to support editing list items.

Guest Author: Lee Richardson
http://rapidapplicationdevelopment.blogspot.com/

Lee Richardson is a Senior Software Engineer at Near Infinity Corporation, an enterprise software development and consulting services company based in Reston, Virginia. He is a Microsoft Certified Solution Developer (MCSD), a Project Management Professional (PMP), a Certified SCRUM master and has over ten years of experience consulting for the public and private sector. You can follow Lee on Twitter at @lprichar

Bookmark and Share
 

Subscribe without commenting

Speak and you will be heard.

We check comments hourly.
If you want a pic to show with your comment, go get a gravatar!