After reading the title of this post, you’re probably asking yourself: What’s wrong with the display templates today in SharePoint 2013? Why would I want to replace them?
Our client was looking to customize many different Content Search Web Parts in order to build modular pages with entirely search-driven content. This included getting a large chunk of data from search and filtering / sorting by a custom Managed Metadata column in the display template – which made Angular the perfect candidate. While looking into adding Angular to a display template, I found an excellent blog post by Elio Struyf on the subject.
As great a foundation as this article is, I found myself looking for something “more” to reach my end goal. For my client, the ability to have more than one Angular web part on the page was crucial – which proved a little difficult without some extra intervention. Second, we needed the ability for web parts to “share” data – to be able to get data from two result sources and display them combined in one table. Plus, a little stronger architecture and mode code structure couldn’t hurt.
ARCHITECTURE, DATA FLOW AND IMPLEMENTATION
To expand on Elio’s points, I drafted a quick diagram to outline my code’s architecture and data flow:
First, I added a line in each Control template that saved its ctx object as a global variable (firstCtx and secondCtx, created in WP_Control_FirstControltemplate.html and WP_Control_SecondControlTemplate.html, respectively). This effectively creates a snapshot of the ctx object at that point in time.
Additionally, I added a reference to the directives we’ll create in NgDirectives.js in each Control template. This will execute our directive once we’re all ready to go.
Next, I followed Elio’s instructions to create a “dummy” item template that contains the Managed Property Mappings to be used. I called this WP_Item_Angular_Blank.html.
Now we’ve completed our setup on the SharePoint Display Template end. On the bottom half of the diagram, our file invoking AngularJS, NgDirectives.js, has a custom directive for each control template:
Each Directive calls getResultRows, which works some of Elio’s magic to look into the Angular_Blank Item template, execute it, and grab the managed property mappings inside. Now, we’ve modified the ctx object in the exact same way that our Item Template would. So, in getResultRows(), we can now save our custom properties using $getItemValue() on the ctx.
getResultRows() now returns the entire data set back to the directive, which is saved in the directive’s scope and can now be utilized in the Angular Template.
EXECUTION ON PAGE
Now, here’s one of the issues I encountered during implementation – executing the Angular directive. In Elio’s example, he only has one display template with Angular on the page, so execution isn’t a huge issue. However, with multiple Angular display templates, we ran into difficulty while running angular.bootstrap(document, [‘AngularApp’]) – sometimes it executed before all of the display templates were rendered. This led to our page being unstable and web parts randomly disappearing on refresh.
So, we implemented a simple system on our page. Each Control Template supports a PostRenderCallback that is executed when the control template has completed rendering on the page. Unfortunately, we can’t call our bootstrap here, because this only runs after a single Control Template renders – and they do not necessarily render in the same order every time. So, instead, we have a global variable created in HelperFunctions.js:
Here, we can keep a running count of how many display templates have rendered on the page by this function call in each Control Template:
This function lives in HelperFunctions.js, increments the global variable, and then checks to see if all of the templates have rendered.
We also defined the number of control templates on the page using this global variable in the page layout:
Put it all together, and the execution flow looks like this:
One of the other advantages brought about by this approach is that you can actually share data sets in between web parts and display templates. Since we saved firstCtx and secondCtx as global variables on the page, they don’t necessarily need to be used in their respective directives. Moreover, we can actually use them multiple times each, and we can use more than one ctx in each directive! Let’s say we want to use the data from firstCtx in firstDirective. Let’s say we also want to use firstCtx and secondCtx in secondDirective. That’s totally cool! To accomplish this, I just added a line to set firstItemSet in our secondDirective link function. So, it ends up looking like this:
Which allows us to use it in our template, like this:
Notice how we now have two ng-repeat loops in our template, with two completely separate data sources. And our final results on the page looks something like this:
This opens up even more possibilities to leverage Angular with these data sets – we can filter and group based on a column, we can combine like items from multiple result sources, etc. As you can see, this provides us with a much more extensive code platform to work with than traditional SharePoint display templates.
SAMPLE CODE DOWNLOAD
Questions? Want to tell your friends about how awesome this blog post is? Feel free to comment below or share this article.