EndUserSharePoint 2010 » popup http://www.endusersharepoint.com/EUSP2010 Just another WordPress weblog Fri, 27 Aug 2010 14:00:36 +0000 http://wordpress.org/?v=2.9.2 en hourly 1 Client Side AJAX Applications in SharePoint 2010 – Part 7: Live Bindings http://www.endusersharepoint.com/EUSP2010/2010/07/12/client-side-ajax-applications-in-sharepoint-2010-%e2%80%93-part-7-live-bindings/ http://www.endusersharepoint.com/EUSP2010/2010/07/12/client-side-ajax-applications-in-sharepoint-2010-%e2%80%93-part-7-live-bindings/#comments Mon, 12 Jul 2010 14:00:48 +0000 Lee Richardson http://www.endusersharepoint.com/EUSP2010/?p=979 This entry is part of a series, Client Side AJAX Applications in SharePoint 2010»

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 saving data back to SharePoint using the ASP.Net AJAX templating live binding syntax.

One-Way, Two-Way, Live!

The read-only {{ [FieldName] }} type template syntax we’ve explored previously is far more maintainable than string concatenation, which is the primary alternative to converting JSON data into HTML.  But where ASPNet AJAX Templating really shines is in saving data back to SharePoint (or any oData provider for that matter).  The syntax is called live binding and it comes in two flavors: two-way, and one-way data binding.  We’ll explore two-way data binding first.

In order to implement two-way data binding we simply place an INPUT element on the page and set its value to { binding [FieldName] }.  When it renders, the templating engine will replace the binding syntax with the current JSON object, just as it did with one-way binding.  But now when the user changes the value, the templating engine will automatically update the in-memory JSON objects behind the scenes.

So if we replace the master-details popup we wrote previously with live bindings like this:

<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">
              <input type="text" sys:value="{ binding Title }" />
            </td>
        </tr>
        ...

Then the user can change the title.  But it won’t be too impressive yet because they won’t see the results of their changes in the user story cards.  We can fix that by replacing the titles of the cards with the one-way live binding syntax:

<div class="userStoryTitle">
    { binding Title }
    <span class="userStoryButtons">
    ...

Note how the one-way live binding syntax above looks identical to the two-way binding syntax.  That’s because ASP.Net AJAX Templating automatically uses two-way binding when the binding is located in an input form element. 

Our popup now looks like this:

When we mouse out of any field with two way data binding any elements on the page with one-way binding automatically reflect the change.

Saving to SharePoint

What we have so far is nice, but as soon as the user refreshes the page the values are reverted.  That’s because we never saved the values back to SharePoint.  Fortunately this is extremely easy to do with a call to dataContext.saveChanges(). 

We want the save to occur when the user clicks the OK button.  Assuming we pass in the function onDialogClose to the dialogReturnValueCallback parameter when we called SP.UI.ModalDialog.showModalDialog, then our code would look like this:

function onDialogClose(dialogResult, returnValue) {
	if (dialogResult == SP.UI.DialogResult.OK) {
		dataContext.saveChanges();
	}
}

That’s it!  The value is saved back to SharePoint and when we refresh the page the value has been updated.  But what’s going on behind the scenes?

Batch Processing

If we open up Fiddler to watch the HTTP traffic and click OK, we’ll see a post to _vti_bin/ListData.svc/$batch.  The post data looks like this:

--batch_e9ea-1b95-0f95
Content-Type: multipart/mixed;boundary=changeset_973c-961a-5b1e

--changeset_973c-961a-5b1e
Content-Type: application/http
Content-Transfer-Encoding: binary

MERGE http://localhost/Demo/_vti_bin/ListData.svc/UserStories(12) HTTP/1.1
If-Match: W/"10"
Host: nic-lee7
Accept: application/json
Accept-Charset: utf-8
Content-Type: application/json;charset=utf-8

{
    "__metadata": {
    "uri":"http://localhost/Demo/_vti_bin/ListData.svc/UserStories(12)",
    "etag":"W/\"10\"",
    "type":"Microsoft.SharePoint.DataService.UserStoriesItem"
},
"ContentTypeID":"0x010800B0CD2DCB798D704EA602F275139B7056",
"Title":"Story #12 UPDATED!",
"Priority":{"__deferred":{"uri":"http://nic-lee7/Demo/_vti_bin/ListData.svc/UserStories(12)/Priority"}},
"PriorityValue":"(2) Normal",
...
}
 ...
--changeset_973c-961a-5b1e--
--batch_e9ea-1b95-0f95--

ASP.Net AJAX Templating has kept track of all changes to all JSON objects, and on the call to saveChanges() it’s batched them all up and sent them across the wire as one big JSON post.  This means we can write applications that are less chatty and more performant with absolutely no extra effort.  That’s powerful!

Reverting Changes

The only thing that really remains to complete our user story application is to revert changes if someone clicks Cancel.  Unfortunately there is no elegant way to accomplish this.  The best we can do is re-retrieve all data from the server with a call to dataView.fetchData().  So our final onCloseDialog looks like this:

function onDialogClose(dialogResult, returnValue) {
	if (dialogResult == SP.UI.DialogResult.OK) {
		dataContext.saveChanges();
	}
	if (dialogResult == SP.UI.DialogResult.cancel) {
		dataView.fetchData();
	}
}

Conclusion

So far we’ve seen that templating is a more maintainable approach to displaying multiple list items on a page.   And we’ve seen that the master-details feature simplifies the job of cleaning up the UI.  In this post we’ve seen that the live bindings feature allows us to save data back to SharePoint in batches with a small amount of effort.  Implementing this functionality by hand would otherwise have required far, far more code.

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

]]>
http://www.endusersharepoint.com/EUSP2010/2010/07/12/client-side-ajax-applications-in-sharepoint-2010-%e2%80%93-part-7-live-bindings/feed/ 0
Client Side AJAX Applications in SharePoint 2010 – Part 6: Master-Details http://www.endusersharepoint.com/EUSP2010/2010/06/29/client-side-ajax-applications-in-sharepoint-2010-%e2%80%93-part-6-master-details/ http://www.endusersharepoint.com/EUSP2010/2010/06/29/client-side-ajax-applications-in-sharepoint-2010-%e2%80%93-part-6-master-details/#comments Tue, 29 Jun 2010 14:01:34 +0000 Lee Richardson http://www.endusersharepoint.com/EUSP2010/?p=949 This entry is part of a series, Client Side AJAX Applications in SharePoint 2010»

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

]]>
http://www.endusersharepoint.com/EUSP2010/2010/06/29/client-side-ajax-applications-in-sharepoint-2010-%e2%80%93-part-6-master-details/feed/ 0
Client Side AJAX Applications in SharePoint 2010 – Part 5: Modal Dialogs http://www.endusersharepoint.com/EUSP2010/2010/05/27/client-side-ajax-applications-in-sharepoint-2010-%e2%80%93-part-5-modal-dialogs/ http://www.endusersharepoint.com/EUSP2010/2010/05/27/client-side-ajax-applications-in-sharepoint-2010-%e2%80%93-part-5-modal-dialogs/#comments Thu, 27 May 2010 14:02:21 +0000 Lee Richardson http://www.endusersharepoint.com/EUSP2010/?p=663 This entry is part of a series, Client Side AJAX Applications in SharePoint 2010»

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

A significant portion of SharePoint 2010’s Web 2.0 look and feel derives from its use of modal popups.  This post deviates briefly from the ASP.Net AJAX Templating discussion to explore how to open remote pages as modal dialogs, how to open dialogs based on local HTML, and how to respond to OK or Cancel events.

Modaling to Another Page

To get started let’s throw an edit icon onto the index cards we’ve built previously that will open a modal dialog:

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

There’s nothing fancy going on here yet, just a call to an as yet unimplemented openDialog() function.  The result looks like this:


To open an AJAX modal dialog in SharePoint 2010 either specify a remote page to open, or specify an HTML element in the page.  Opening a remote page is a little easier.  The openDialog() function would look like this:

function openDialog() {
	var options = {
		url: "../../Lists/User%20Stories/DispForm.aspx?Id=1",
		width: 800,
		height: 600,
		title: "User Story",
	};
	SP.UI.ModalDialog.showModalDialog(options);
}

Obviously hard coding an id in the url parameter isn’t exactly best practice, but it works for demonstration purposes.  The result looks like this:


This is a useful technique for opening existing SharePoint pages as in the example above or for opening pages as a dialog that a user might also navigate to directly.  However, in our application we want to edit information that is in-memory in the browser (which is where ASP.Net AJAX Templating stores its data).  The showModalDialog() function will support this scenario, but it’s slightly more complicated.

Modaling to Your Own Page

First we’ll need an HTML element that we’re going to pop up.  A first draft might looks like this:

<div id="userStoryDetails">
    Hello World!
</div>

Since the options parameter that we passed to showModalDialog() supports an ‘html’ parameter instead of a ‘url’ parameter it might seem like we could simply retrieve the userStoryDetails element inside of openDialog and pass that to options.  However, there’s a problem with this approach.  By default SharePoint’s showModalDialog() function will destroy the DOM element that are passed to it.  The result is that the dialog can be opened once, but it fails each subsequent time it is opened.

To avoid this behavior we can cache the DOM element in a global variable rather than keeping it as a local variable scoped at the function level.  The code to will look like this:

var userStoryDetails;

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

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

With this code we can close and open the modal dialog multiple times.  The result looks like this:


The only thing we’re missing now is the ability to close the dialog and handle the results.

Goodbye World

Closing the dialog and handling results is also pretty simple.  It takes two steps.  First we’ll need to call commonModalDialogClose() in our popup code and next we’ll implement a callback method that SharePoint will call when the dialog has been closed. 

To call commonModalDialogClose() we can modify the code that we are going to pop up like this:

<div id="userStoryDetails">
  <input
    type="button"
    value="OK"
    onclick="SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK, 'Ok clicked'); return false;"
    class="ms-ButtonHeightWidth"
    target="_self"
    />
  <input
    type="button"
    value="Cancel"
    onclick="SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel, 'Cancel clicked'); return false;"
    class="ms-ButtonHeightWidth"
    target="_self"
    />
</div>

Notice the first parmeter to commonModalDialogClose().  It’s a DialogResult.  This parameter is important because our callback method will need to differentiate the ways in which a user could close the dialog.  For instance when a user clicks the X in the upper right of the dialog SharePoint passes DialogResult.cancel for the first parameter. 

The second parameter to commonModalDialogClose() is simply passed through as the second parameter to our handler as we’ll see shortly.

To handle the results of closing the dialog we can pass a function to the dialogReturnValueCallback argument in the options parameter of showModalDialog.  The result would look like this:

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

function onDialogClose(dialogResult, returnValue) {
	if (dialogResult == SP.UI.DialogResult.OK) {
		alert('goodbye world!');
	}
	if (dialogResult == SP.UI.DialogResult.cancel) {
		alert(returnValue);
	}
}

And now we can open and close SharePoint modal dialogs and handle the results.  If needed there are other adjustments we can make to modal dialogs through the options parameter.  For instance X, Y, allowMaximize, showMaximized and showClose.  The documentation is sparse, but hopefully this page on MSDN will eventually be updated with more details.

Summary

This post should provide everything needed for opening dialogs that look like the ones SharePoint uses.  And more importantly it will provide a foundation for the next post in the series in which we will edit user story information using a master-details view inside of a modal popup.

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

]]>
http://www.endusersharepoint.com/EUSP2010/2010/05/27/client-side-ajax-applications-in-sharepoint-2010-%e2%80%93-part-5-modal-dialogs/feed/ 7