In part one of this four part series, I started with a discussion about the new development paradigms SharePoint 2013 presents. Then, part two completed the data access layer for our SharePoint 2013 web parts. Now it's time to take a look at the web part itself. I'll finish these posts with the deployment story in part four.
Like I said in those previous posts, our web parts are built starting from the out-of-the-box visual web part template (which implies they are part of a WSP, which will be important in part three) in Visual Studio 2012. After adding a new web part, the first thing I do is pretty up the Elements.xml and whatever.webpart files as follows:
Next, let's look at the code behind. All of my web parts inherit from a base one named, creatively, "BaseWebPart." I still consider this to be kosher given Microsoft's new guidance against using the SharePoint server object model, since we're not referencing Microsoft.SharePoint.dll. Everything here is ASP.NET code that could never be blamed for botching a SharePoint upgrade or migration.
The Init method on Line #'s 4-8 are part of the Visual Studio 2012 visual web part template (with Office 2013 Tools installed). All I do here is override the Render method, pass through to our base's rendering logic, and then have a little fun with our properties. Line #14 uses reflection to grab all properties decorated with an attribute of type "CategoryAttribute" and writes a hidden input to the client with information containing the property name, value, and id of the web part that owns it. CategoryAttribute defines a single string named "Category" and Line #17 will grab the property if the category is set to "2013WebPart." Who doesn't think reflection is fun?
LoadWP, starting at the top on Line #1, takes in four parameters:
Line #4 performs the jQuery AJAX call. Line #6 specifies the get and Line #7 specifies jsonp as the data type. This is very important, as it allows communication between the SharePoint domain and our app domain over in the MVC site (which should be different URLs, and therefore a no-no with conventional JSON). It's also very awesome, because we don't have to do any extra work to de/re-serialize the model we get back from MVC; it comes down from the server pre-giftwrapped as a JSON object ready for databinding.
Line #'s 12 and 13 set the query string parameters that the controller expects as part of the request. The former calls the method (called "GetProperties" that we'll look at next) which reads in the values of the hidden inputs that the base web part generates, builds a client-side model, and serializes it; the latter is simply the current page's URL.
Line #15 defines the AJAX "success" callback's anonymous method that implements the following logic: if there's an error, alert that; if there's a model, Knockout bind that; if there's a value for the HTML, set that. Then if there's a callback, do it. The Knockout binding on Line #26 is annoying, as we have to hard-code in the zero index to the jQuery selector that specifies which data template to use; .first() doesn't work, as it appears that Knockout wants an actual DOM element, not a jQuery selector.
Finally, let's look at GetProperties. Line #'s 50 and 51 new up the web part args model, which matches MVC's WebPartArgsModel exactly. The magic happens on Line #53, when we use the web part Id passed into the method to feed a jQuery selector that gives us all its property values. I "each" my way through them, adding a new web part arg model at each index. Finally, Line #61 serializes this object for query string transmission.
Now that our data access layer is wired up, we can discuss the web part's UI. Like I said, "conventional" web parts in Visual Studio 2012 / SharePoint 2013 behave, at development time, just like user controls. The only annoyance is the fact that we can't quick deploy our ASCX files independent of their WSP installation cycle. But other that, Microsoft as really streamlined web part development.
Let's take a look at a sample web part's HTML:
The first block, Line #'s 1-12, perform the AJAX call to get the page's data from the server. Phonetically, Line #5 says "Call the Get method on the PageTiles controller and bind the resulting model to the element with an Id of "tiles." This shoots us down to Line #25, which uses the Knockout "foreach" binding against the "Tiles" array on the model and renders it with the "tiles-template" template.
This template is defined on Line #13 and identified via an Id that matches the template name in the binder element. Knockout templates are defined within script tags (of type "text/html") so that their fragmented markup isn't displayed on the UI before the binding is complete. It's actually pretty easy to use once you get a one or two displays working. Really my only complaint about Knockout is that the data binding attributes aren't stripped from the DOM. I don't think this causes any harm, but it's sort of like a painter leaving the blue tape up between the top of a wall and the ceiling. It just seems a bit sloppy.
For completeness sake, here's what a property declaration in the web part's code behind looks like:
Line #'s 1 and 3 basically wire this property into the standard tool part for configuration when the page the web part is on is in edit mode. Line #2 is our "CategoryAttribute" which I use, again, to inform the base web part that this property belongs to this particular control. This is important to allow multiple web parts of the same type to live on the same page. Finally, we have the property itself on Line #4, which will always be of type string.
Like I said, other than the boilerplate initialization code that comes with the visual web part template and the specification that the class should inherit from our base, these properties are the onlty code we'll see in the file. And since there's absolutely no traces of Microsoft.SharePoint.anything.dll, these puppies should upgrade nice and clear to next version of SharePoint (a most displeasing thought, as I'm still learning this one). That does it for the web part. Read on to part four, where we'll look at the deployment aspect of this architecture.