Monday, December 16, 2013

How to get coordinates for geolocation field (JavaScript solution)

We all probably now know about the new cool feature in SharePoint - geolocation field, a new field that allows you to specify a location for a SharePoint item.

The problem with this field is it is little complicated to set its value. Out of the box it allows you to specify its value by using your current location or by manually setting longitude and latitude, probably something you don't know. So to enable users to specify the location in a more friendly way you had to create a custom field that extends the geolocation field. This is not an option in SharePoint Online.

In this post I will show  how you can provide your users with a way to search for a location in SharePoint Online by simply using a little bit of JavaScript and Microsoft.Maps API, which contains the core types of the Bing Maps AJAX Control. To learn more about this API be sure to visit Microsoft.Maps API Reference and Maps AJAX control examples, for me this is a really powerful and easy to use way to create geolocation solutions on web.

For start I will create a list for my geolocation field. It is a simple list with a Title, Description and a geolocation field I called Location. The list will be called "Wonders of the World", so you can probably guess what it will contain. If you need help for adding a geolocation field to the list be sure to check my Adding geolocation field to SharePoint Online post.

If you try to add a new item to this list  you will get a form that looks like this:

So here you can see how your location field looks like by default. We'll first need to change it so we can more easily modify it. To do this we will need SharePoint Designer.

So open your SP Online site in your SP Designer, navigate to Lists and Libraries and select the list with your location field. Now we will create a custom NewForm (in this post I will show you this customization on a NewForm , but this  also applies on the EditForm).
At the Forms section in Designer select New and in the new list form wizard select New item form as a type of a form to create and check the box to set it as a default form.





Now when you go back to your list and try to add an item, your form will look like this:

This is great because now on the form I have two input elements for longitude and latitude that I can easily access from JavaScript.

We will need to find the id's for this input elements. You can do this by using Developer Tools in Internet Explorer, press F12 to start the developer tools and use the DOM explorer to examine those input elements, they will look something like this:

So once you locate the input elements write down their id values, we are going to need them later.

Now let's get back to our SharePoint Designer and in it examine our new CustomNewForm.  Search in it  for you location field, you should find it in a row that looks like this:



Under this row we will insert our own HTML code that we will need in our custom search for locations. So insert :
     
     

Save the change and look at your form from SharePoint, it should look like this

You can see we have added an input element where users can insert search terms or  an address for a location. We have also added a div element which will be a placeholder for displaying a location on a map.
And now for the JavaScript. Go back to the CustomNewForm in your SharePoint Designer. Locate the following line <asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server"> and  immediately under that line add the following Javascript code (You will probably have to be in Advanced mode). Be sure to change the values for longitude and latitude input field ID, and for the Bing maps key in the places I specified. Also in production you will probably want to extract this script to a separate file, but for demo purposes I am adding it directly on the page. Here is the code:



Save the page and you are ready to test it, go back to your list and start creating a new item. Enter a search term for your location and press the search button we created. If everything is OK you will get something like this:




You get a representation of your location on a Bing map and the longitude and latitude fields for your geolocation field are set, so when you save your item you will also save the location you see. Also the you can drag the pushpin  (the DragHandler function in the script) so you can simply adjust the position.

I won't go into details of my code as it is pretty straightforward, but if there are any questions feel free to contact me.



Wednesday, November 13, 2013

Missing items from virtual directories when upgrading solutions to SP2013

Recently I was working on a SharePoint migration from SP2010 to SP2013. The migration involved several custom solutions that contained various items ranging from custom web parts to web services.

The process for upgrading those solutions was pretty straightforward except for one problem that involved my custom field types. After successfully rebuilding my solution to work on SP2013 and deploying them to my test environment, when I tried to add them to my test list I was greeted with an "Cannot find ..." error for my FieldEditor control. After assuring that there was nothing wrong with my code, I inspected the field definition XML file.
In it I found that the missing field editor control is referenced like this:
....
<Field Name="FieldEditorUserControl">~/_CONTROLTEMPLATES/DatabaseLookup/CustomFieldEditor.ascx</Field>
....

And there is the problem, in SharePoint 2013 there is a small change in a way you are accessing your virtual directories (folders in 15 hive like _layouts and _controltemplates) described in this article:
http://msdn.microsoft.com/en-us/library/dn296464.aspx

"...This new system has implications for you as you develop SharePoint 2013 solutions and apps, particularly when you are using the SharePoint 2010 SDK. In any app for SharePoint (which only run in SharePoint 2013 mode) and in any SharePoint solution that you know is only going to be used in site collections that run in SharePoint 2013 mode, you need to add the "/15" yourself to all the _layouts and _controltemplates virtual paths you create in your solution/app..."

After changing my xml file to get my FieldEditorUserControl  in /_CONTROLTEMPLATES/15/... all was good. Later on I had to also adjust the calls from other systems to custom web services deployed to Sharepoint layout folder, because they were no longer found at ../_layouts/webservice.asmx, they were at
../_layouts/15/webservice.asmx.

So if you are migrating your custom solutions from SP2010 and get an error that says it cannot find some file and you are sure that it is there, check the way you are referencing those files and see if they are compliant with this new system.

Friday, August 16, 2013

How to pass parameters between your App parts?

With SharePoint web parts developers had an option to make them communicate with each other using standardized set of interfaces called connection interfaces that allowed Web Parts to exchange information with each other at run time. (Creating a Connectable Web Part)

In the new SharePoint App model, web parts are replaced with app parts. The problem is they don't have an equivalent to Web Part Connections. So how can you pass parameters from one part to another?

To answer this you have to consider several things:
  • App parts are basically small applications that are presented in your SharePoint site through iframes
  • each app is running in a separate domain and so are their app parts. Because of this any direct manipulation through some javascript code is out of the question because it would mean cross-domain scripting and all you would get is an "access denied" message
Thankfully there is a solution for this that is provided through HTML5 and the postMessage method.
This method provides a way for communicating between browsing contents in HTML documents.

To demonstrate this I will use Napa tools with my SharePoint Online to create two App parts, one to send a message and the other one to receive it.

Although theoretically you can achieve app parts to communicate directly with one another I found it a much better choice to add additional code to the page where my app parts are located (using script editor for example) and make the communication go through there.
                     
So first lets send a message from one of our app parts.

Using Napa tools I will create a new App (I named it SenderApp). Add this following JavaScript function to its ClientWebPart.aspx page.

function sayHello()
  {
   var appMsg = {
      'msg':'Hello from sender app part'      
  };
  
  window.parent.postMessage(appMsg, "*");
  }


In this function we have created an object appMsg that contains only a variable called msg with some text. Similarly you can define your own  app object that you wish to share with other apps or SharePoint. As we know our app part will run in an iframe on our SharePoint site so we can use  window.parent.postMessage to post that object to the SharePoint page on which we have placed our app part.

To execute my function I added a button to my app part that calls it on click.
<button id="bttPost" onclick="sayHello()">Say Hello</button>

This is the end result for my SenderApp part:



Now for the app part that will receive this message. Again using Napa tools I have created another app (I called it ReceiverApp). In this app we will add a listener that will wait for a message that will come from other sources using this following script.

window.addEventListener("message", receiveMessageInApp, false);
function receiveMessageInApp(event)
{   
divMsg=document.getElementById('divMessages');
divMsg.innerHTML= divMsg.innerHTML+event.data+'<br/>';
}
So to catch the message from our app part we will use window.addEventListener that looks out for incoming messages and calls receiveMessageInApp function when it picks one up. It would be a good practice to send some additional information in your post message object, so you can check which part (if more of them sends messages) posted a message. Also you can use event.origin to see that.
receiveMessageInApp  method is used to display the data we will receive through messages.
This is how my part looks in the end:



Now you can package both of your apps, deploy them to your SharePoint site and add the app parts to a page. On the same page add a script editor. In that editor we will catch the message sent from our sender app part, process the message and forward it to our receiver app. Here is how my page looks like after I add all the parts:


Add the following script to your script editor to process messages from the sender app and forward them to your receiver part.
<script>

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event){
procesHello(event.data.msg);
}

function procesHello(msgData){
var iFrame = document.getElementsByTagName("iframe")[1];   
iFrame.contentWindow.postMessage(msgData,"*");
}

</script>
So in my script editor I am first listening for a message to arrive and then I am searching in my page for the second iframe element (or the second app part) and forwarding data of that message to its window(you will probably want to check for the id or something else that defines the iframe of the app part to which you wish to send your data to). If everything is correct, when you press Say Hello button, you should see that the message was processed in the ReceiverApp part.



From this simple example we see how we can achieve communication between app parts using postMessage
So one app part sends a message to the parent page, the page receives the message and sends it of to another app part if needed.

The only limitation is it will not work on older browsers since this is a HTML5 feature but other then that this is a very effective way to achieve app parts communication.