CHUVASH.eu

CHunky Universe of Vigourous Astonishing SHarepoint :)

Make javascript code work with Minimal Download Strategy Part 2

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


 

mds_007
Minimal Download Strategy (MDS) is an important feature in SharePoint 2013. It lets you download only a page delta, only changes. There is still issues with the MDS and custom scripts and almost no documentation on msdn or technet. In this blog post I want to learn more about adjusting custom scripts for MDS. As in my previous post, I want to take a real problem and try to solve it. The goal is to find a solution, not nececerilly the most optimal solution, at least for now.

Global Navigation and MDS

Recently I read in the Waldek Mastykarz’ blog about how to create a Global Navigation using Managed Metadata and knockout.js. These are two posts which cover this solution, where you can learn a lot from:

I wanted to try it out in my environment and test it in an MDS-enabled site. As a first step I configured it in a site without MDS. There were no problems to get it working in a site without MDS, except some changed I had to make (The final code is available on my github repository):

  • sp.js had to be loaded, because it wasn’t loaded. I added: SP.SOD.executeFunc(“sp.js”, “SP.Utilities.Utility”, …)
  • The script didn’t need to load the navigation in modal dialogs and other views with hidden suiteLinksBox

I added this to the Mavention.GlobalNavigation.init to determine if the element with id suiteLinksBox was hidden and if so, just finish the function:

var suiteLinksBox = document.getElementById("suiteLinksBox");
if (suiteLinksBox && suiteLinksBox.offsetHeight === 0
                  && suiteLinksBox.offsetWidth === 0) {
	//suiteLinksBox is hidden, no need to load any navigation items
	return;
}

Enabling Minimal Download Strategy

The Global Navigation works! Now it is time to enable Minimal Download Strategy. My site is a team site, so I can go to “Manage Site Features” and enable it:
mds_008

And… it stopped working, of course. Now the actual work begins. While troubleshooting, I discovered this:

  • MasterUrl and CustomMasterUrl have to be the same Master in order to work, otherwise MDS fails
  • with MDS knockout is not loaded, the Mavention.GlobalNavigation does not run
  • Moving the code outside the SharePoint:AjaxDelta control in the masterpage doesn’t affect anything

As previously, I tried to create a module and register it with RegisterModuleInit with no success. Maybe I’ve overseen something, but RegisterModuleInit doesn’t work in my environment.

Then I tried to wrap the whole Mavention.GlobalNavigation into a function and called it $_global_Mavention_GlobalNavigation, as it is in callout.js:

mds_002

I added $_global_Mavention_GlobalNavigation() in the end of the javascript file and I added this line of code in the master page. Unfortunately, no success. The code still didn’t run with MDS, literally, it didn’t run at all.

Breakthrough

I tried almost everything. The code started to run in MDS after these changes in the masterpage:

  • the plain <script> tags were converted to <SharePoint:ScriptLink> tags
  • In the masterpage I ran the $_global_Mavention_GlobalNavigation() again

Fortunately it started to run the code, but there was another error: ko is not defined.

Allright. I still had knockout in a plain <script> tag. So I converted it to a ScriptLink and I had to download a copy of knockout.js and provision it to the Style Library. That didn’t help much. After that I actually did the same thing with knockout as with the Mavention.GlobalNavigation code, I put everything in a function and called it $_global_knockout and invoked in the end of the script and in the script in the master page.

mds_009

The masterpage contains the following code now:

<!-- START Mavention Global Navigation -->
<div>
<ul class="ms-core-suiteLinkList" data-bind="foreach: globalMenuItems">
			<span>
				<span data-bind="text: title"></span>
				<!-- ko if: location.href.slice(0, url.length) == url || (location.href.slice(0, 8) === 'https://' && location.href.slice(0, url.replace(':443/', '/').length) == url.replace(':443/', '/')) -->
				<span class="ms-suitenav-caratBox" id="Suite_ActiveLinkIndicator_Clip">
					<img class="ms-suitenav-caratIcon" id="Suite_ActiveLinkIndicator" src="/_layouts/15/images/spcommon.png?rev=23" />
				</span>
				<!-- /ko -->
			</span></ul>
</div>
<SharePoint:ScriptLink Language="javascript"
	Name="~site/Style Library/Mavention/knockout.js" runat="server"
	OnDemand="False" LoadAfterUI="True"
	Localizable="false"/>
<SharePoint:ScriptLink Language="javascript"
	Name="~site/Style Library/Mavention/Mavention.GlobalNavigation.js" runat="server"
	OnDemand="False" LoadAfterUI="True"
	Localizable="false"/>
<SharePoint:ScriptBlock runat="server">
	$_global_knockout();
	$_global_Mavention_GlobalNavigation();
	Mavention.GlobalNavigation.init('Managed Metadata Service', 'd604c487-0119-4c9e-8b2a-b7c10cf058d6');
</SharePoint:ScriptBlock>
<!-- END Mavention Global Navigation -->

The final code is available on my github repository.

$_global_

There is a convention in SharePoint for naming these functions. But it is not necessary to use it. We can call anything. The only thing is to think about is to call it in the end of a script and in the page. Why $_global_ convention does work is this part of init.js:

NotifyEventAndExecuteWaitingJobs(eventName);
if (typeof g_MinimalDownload != 'undefined' && Boolean(g_MinimalDownload) && typeof RegisterModuleInit != 'undefined') {
    var lastSlashPos = scriptFileName.lastIndexOf('/');

    if (-1 != lastSlashPos) {
        scriptFileName = scriptFileName.substring(lastSlashPos + 1);
    }
    var lastdotPos = scriptFileName.lastIndexOf('.');
    var funcName = null;

    if (-1 == lastdotPos)
        funcName = scriptFileName;
    else
        funcName = scriptFileName.substring(0, lastdotPos);
    funcName = funcName.replace(/\./g, '_');
    var funcPattern = new RegExp("^[A-Za-z0-9_\\-\$]+$");

    if (Boolean(funcPattern.exec(funcName))) {
        funcName = "$_global_" + funcName;
        var initFuncName = eval("'undefined' != typeof (" + funcName + ") ? " + funcName + " : null");

        if (null != initFuncName) {
            RegisterModuleInit(scriptFileName, initFuncName);
        }
    }
}

Summary

In this post I have had a goal to try out a good javascript solution and try to make it work with MDS. I have used Waldek Mastykarz’ code for rendering a Global Navigation. The navigation is rendered in the masterpage through a custom script that loads data from a term store.

  • To make a custom javascript code, and this Global Navigation example particularly, work with MDS, we need:
  • Create a function (not an anonymous) and call it directly in the end of the script file
  • Use a ScriptLink instead of a plain script tag
  • Rewrite your javascript libraries like your custom javascript files

I want to learn more about the MDS feature and adjusting custom javascript code. So it maybe will be a part three.

Advertisements

3 responses to “Make javascript code work with Minimal Download Strategy Part 2

  1. Pingback: Custom JavaScript and MDS: Some rules I learned | Geek Time

  2. Pingback: Minimal Download Strategy. Simple | CHUVASH.eu

  3. Syed Shoaib Adil 2017-01-23 at 22:33

    I have just done RegisterModuleInit as mentioned in (https://blogs.technet.microsoft.com/sharepointdevelopersupport/2013/02/08/register-csr-override-on-mds-enabled-sharepoint-2013-site/#comment-14915) blog and it worked for me in SP2013 and working seamlessly. But when I deployed same code I SP2016, its not working for me. Did u tried ur code with SP2016 ?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Вула Чăвашла

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, ya mama

Treacle tarts for great justice

... And All That JS

JavaScript, Web Apps and SharePoint

%d bloggers like this: