CHUVASH.eu

CHunky Universe of Vigourous Astonishing SHarepoint :)

Tag Archives: jQuery UI

Make javascript code work with Minimal Download Strategy Part 1

I have a newer blog post about MDS, that provides a much simpler solution. Please check it before reading further.


mds_001

This is a part 1 of the blog post about Minimal Download Strategy and javascript adjustments for user code. What I initially thought should be enough for one post, is not enough, so I see it as a part 1. I wrote this post after I had read Chris O’Brien’s post about JSLink Here I want investigate how we can get his accordion list view working with MDS.

Minimal Dowload Strategy or MDS is a new feature in SharePoint 2013. By now, if you read this post, you already know about it. The simplest way to see if MDS is enabled on your site, you can recognize it on the “ugly” urls. I don’t think they are so ugly. But it is a matter of taste and habit.

No matter if you like MDS or not, MDS is enabled on many site templates and is a huge step towards a faster, more responsive architecture in SharePoint, I would say, towards the Single Page Application concept in SharePoint (but it is a long way to go).

We have to keep the MDS in mind, when we write our customizations in javascript. SharePoint 2013 loves javascript and the probability is high that you write a lot of javascript. If it doesn’t work with MDS, your code breaks and the user doesn’t see the functionality, or the site owner must disable the Minimal Download Strategy feature. I wouldn’t like to have disabling of an improvement feature as a prerequisite for my code.

In this blog post I want to dig into the techniques for getting the javascript code working with MDS. For a while ago I read a wonderful blog post in Chris O’Brien’s blog:

There he describes how JSLink works and how much you can change a standard XSLTListViewWebPart. Chris creates a jQuery UI Accordion view for his list view. As an issue he mentions the MDS.

Here I want to take Chris’ code and adjust it for MDS. My goal is to change as little as possible to find the most important steps for MDS. So I’ll continue where he has finished.

My colleages who have debugged the MDS a lot, gave me a tip: $_global. The SharePoint 2013 internally uses function inside the files which starts with $_global:

mds_002

Here we have callout.js/callout.debug.js The function is called $_global and _ and the filename callout = $_global_callout. Then the function is invoked directly in the end of the file. It is a different story than the anonymous self executing funcitons we’ve seen before.

When I search the hive folder with grepWin tool, I find 148 files containing “$_global”:

mds_003

I rewrote the the code into one wrapper function and invoked in the end of file:


// function to setup JSLink templates
function $_global_AccordionListView() {
    // function to process an accordion item..
    window.COB = window.COB || {};
    window.COB.accordionItem = {
        customItemHtml: function (ctx) {
            var accordionItemHtml = "</pre>
<h3>" + ctx.CurrentItem.Title + "</h3>
<pre>
";
            accordionItemHtml += "</pre>
<div>" + ctx.CurrentItem.AccordionItemDescription + "</div>
<pre>
";
            return accordionItemHtml;
        }
    };

    var overrideCtx = {};
    overrideCtx.Templates = {};

    overrideCtx.Templates.Header = "</pre>
<div id="\&quot;accordion\&quot;">";
 overrideCtx.Templates.Item = window.COB.accordionItem.customItemHtml;
 overrideCtx.Templates.Footer = "</div>
<pre>
";

    overrideCtx.BaseViewID = 1;
    overrideCtx.ListTemplateType = 11000;

    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);

    $(document).ready(function() {
	    // It seems SharePoint inserts a script tag in an inconvenient place that breaks jQuery UI's accordion, so let's remove it!
		// (N.B. further testing recommended for production)..
		$("#accordion").find('#scriptBodyWPQ2').remove();

		$("#accordion").width('70%');
		$("#accordion").accordion();
	});
}

$global_AccordionListView();

Unfortunately, it didn’t help. There was still no accordion. But wait, is it just the accordion that isn’t created. Indeed. The JSLink itself works. We can see it in the markup:

mds_004

Strange, maybe there was no need for rewriting the code in this case. I changed back all the javascript code to see the markup. It is the right markup. Then the problem is the accordion initialization, or the $(document).ready. Then I thought about the SharePoint-function for that: _spBodyOnLoadFunctionNames and rewrote the $(document).ready:

function onReady() {
    $("#accordion").find('#scriptBodyWPQ2').remove();
    $("#accordion").width('70%');
    $("#accordion").accordion();
}

_spBodyOnLoadFunctionNames.push("onReady");

When I deployed it, it worked… It doesn’t seem like it is the whole solution. It is too simple. Well, it is the solution for the Accordion List View. By putting the accordion initialization code into _spBodyOnLoadFunctionNames we ensure that SharePoint runs it even on pages with MDS. As the name tells us: OnLoad. This appends the code to the onload function which runs after the $(document).ready. It means the time before the text becomes an accordion is longer.

Other cases

Allright, the actual jslink works pretty fine with MDS, except the accordion. But if we hadn’t the jQuery UI Accordion, there wouldn’t be a need to make change to the javascript code. There must be other cases where we need to adjust our javascript code. In the meanwhile I discovered a couple of files in the hive folder which use RegisterModuleInit function:

mds_005

After a quick search I found this:

Sridhar writes in his post, you have to have your javascript code in a function, then call the function inside a RegisterModuleInit. It is strange. Chris O’Brien’s example makes almost the same thing as Sridhar, it changes the display with JSLink. But there was no need for RegisterModuleInit.

More investigation will be in part 2.

Thanks to my colleagues Christopher, Björn and Martin for giving me tips and discussing it with me.

Advertisements

angular jQuery UI autocomplete

Angular JS is one of the most developed MVC frameworks in the javascript world. Angular UI is a huge UI-centric extension of AngularJS (it is more or less like jQuery UI to the jQuery). It uses much jQuery UI and Twitter Bootstrap and provides many own components like modal dialogs, maps, tooltips, masked inputs and much more. And all this is easy to implement in your code just by adding a directive:

<input ui-date>

Much cleaner than listening on $(document).ready, traversing the DOM and appending the datepicker in your code:

<input id="myDate">

 

$(document).ready(function() {
   var input = $("#myDate");
   input.datepicker();
});

All this code is invoked but outside your app code.

ui-autocomplete

Unfortunately Angular UI has no official autocomplete directive. There is a discussion about it on many forums. There are some working implementations. But they are not complete. I looked at how ui-date is implemented. jQuery UI Datepicker and jQuery UI Autocomplete have one in common. They are relying on a set of options. To create a directive for each option would make the html code too dirty. Here is a directive ui-autocomplete which I have written that (like ui-date) takes a options object as parameters. This options object has to be appended to the $scope variable in the angular controller:

var uiAutocomplete = function() {
	return {
		require: '?ngModel',
		link: function(scope, element, attrs, controller) {
			var getOptions = function() {
				return angular.extend({}, scope.$eval(attrs.uiAutocomplete));
			};
			var initAutocompleteWidget = function () {
				var opts = getOptions();
				element.autocomplete(opts);
				if (opts._renderItem) {
					element.data("autocomplete")._renderItem = opts._renderItem;
				}
			};
			// Watch for changes to the directives options
			scope.$watch(getOptions, initAutocompleteWidget, true);
		}
	};
};
angular.module('ui.directives').directive('uiAutocomplete', [uiAutocomplete]);

Example code

<div ng-controller="AutoCompleteCtrl" class="ui-widget">
    <input placeholder="Search..." ng-model="term" ui-autocomplete="autocompleteOptions"/>
</div>

Here is the angular controller:

function AutoCompleteCtrl($scope, $http) {
	var search = function(request, response) {
			var callback = function(data) {
				response(data);
			};
			$http.get("/api/orders/search?q=" + $scope.term)
				.success(callback);
		},

		gotoOrderDetails = function (id) {
			window.location = "/orders/" + id;
		},

		_renderItem = function (ul, item) {
			return $("<li>")
				.data("item.autocomplete", item)
				.append("<a>" + item.title + "</a>")
				.appendTo(ul);
		},

		select = function (event, ui) {
			if (ui.item) {
				gotoOrderDetails(ui.item.orderId);
			}
		};

	$scope.autocompleteOptions = {
		minLength: 1,
		source: search,
		select: select,
		delay: 500,
		_renderItem: _renderItem
	};
}

To get the options inside the directive a function called $eval is used.

Here a standard set of jQuery UI Autocomplete is used and one more: _renderItem. It is not included in the default autocomplete options, but in my case I needed to define custom rendering logic for my items.

Enjoy

Edit 2013-02-15:

I have sent a pull request angular ui. See the details here

Edit 2013-02-23:

I have added support for uiConfig and change scope.$watch invokation based on Pull Request Feedback. I also published a small gist which shows how a simple ui-autocomplete can be used.

javascript toolkit on text file

Referencing javascript libraries from Content Delivery Networks (CDN) like jQuery can have many benefits. The self-hosted javascript files are on the other hand give you more control. But in one situation it is much better with cdns: the development. Like Marc D Anderson pointed:

I didn’t realize it at the time, but this txt file has become a standard part of my toolkit already. I’ve added it to three client environments just since last week. It’s the A #1 fastest way to get something up and running, even if you know you’re going to host the files locally later.

This kind of toolkits is really useful in situations where you have to quickly try/test some ideas without uploading multiple files to your server. Here comes my version of this javascript toolkit file:

<!--Add this to the webpart-->
<!--Inspiration got from sympmarc
http://sympmarc.com/2012/04/20/referencing-jquery-jqueryui-and-spservices-from-cdns/?utm_source=rss&utm_medium=rss&utm_campaign=referencing-jquery-jqueryui-and-spservices-from-cdns
and Thorsten Hans:
https://www.nothingbutsharepoint.com/sites/devwiki/articles/Pages/SharePoint-Development-Using-HeadJS-KnockoutJS-And-SPServices.aspx
-->

<!-- Reference the jQueryUI theme's stylesheet on the Google CDN. Here we're using the "Start" theme -->
<link  type="text/css" rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/start/jquery-ui.css" />

<!-- Reference head.js on cdnjs (Cloudflare)-->
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/headjs/0.96/head.min.js"></script>

<!-- Load all other scripts async and run sync: -->
<script type="text/javascript">
	head.js("http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js",
		"http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js", 
		"http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js",
		"http://cdnjs.cloudflare.com/ajax/libs/knockout/2.0.0/knockout-min.js",
		"http://cdnjs.cloudflare.com/ajax/libs/jquery.SPServices/0.7.1a/jquery.SPServices-0.7.1a.min.js",
		"my.js" );
</script>

In my file I even use head.js for loading multiple javascript files at the same time, like Thorsten Hans described in his blog post.

While using head.js you have to have your javascript code in a separate file and reference it in head.js as the last file. Otherwise your javascript code will be executed before jQuery is loaded and it will break the solution. If you want to have your code in the same file as your markup, you have to provide a callback function which will be run after all javascript files are loaded:

head.js("......", "....", myCallback);
function myCallback() {
  $("#div").text("hej");
}

jQuery UI Datepicker

As an alternative to asp:Calendar we can use the fancy jQuery UI Datepicker:

$(document).ready(function () {
	$.datepicker.setDefaults($.datepicker.regional["sv"]);
	$("#").datepicker({
		changeMonth: true,
		changeYear: true,
		yearRange: "-120:+0" 
	});
});

I found this a much simple and best solution for an birthdate input. We can set international options, year range, year and month navigation. Other options I have tried are asp:Calendar, ajaxtoolkit:CalendarExtender and DateJs. jQuery UI is the most simple much more than datepicker and works smoothily with SharePoint.

Вула Чăвашла

VulaCV - Чăвашла вулаттаракан сайт

Discovering SharePoint

And going crazy doing it

Bram de Jager talking Office 365, SharePoint and Azure

My view and thoughts on Productivity and more.

My programming life

and everything in between

SharePoint Development Lab by @avishnyakov

It is a good place to share some SharePoint stories and development practices.

SharePoint Dragons

Nikander & Margriet on SharePoint

Paul J. Swider - RealActivity

RealActivity is a specialized healthcare services and solution advisory firm.

Mai Omar Desouki - Avid SharePointer

Egyptian & Vodafoner - Senior SharePoint Consultant

Cameron Dwyer | Office 365, SharePoint, Outlook, OnePlace Solutions

Office 365, SharePoint, OnePlace Solutions & Life's Other Little Wonders

paul.tavares

Me and My doings!

Share SharePoint Points!!

By Mohit Vashishtha

Jimmy Janlén "Den Scrummande Konsulten"

Erfarenheter, synpunkter och raljerande om Scrum från Jimmy Janlén

SPJoel

SharePoint for everyone

SharePointRyan

Ryan Dennis is a SharePoint Solution Architect with a passion for SharePoint and PowerShell

SharePoint 2020

The Vision for a Future of Clarity

Aharoni in Unicode

Treacle tarts for great justice

... And All That JS

JavaScript, Web Apps and SharePoint