Thursday, March 20, 2014

JS Link Tomfoolery

I'm generally a big fan of using SharePoint as a CMS, then doing my own thing on the front end.  SharePoint 2013 has a really neat way of doing this for individual lists, but here are some of my personal tips on how to do this better.

1. Setting the JS Link Path Field
When setting the path to the JS Link file, DO NOT USE THE FULL PATH.  I spent a long time pulling my hair out on this one, but you must use one of the following options at the font of the path:

  • ~site
  • ~sitecollection
  • ~layouts
  • ~siteLayouts
  • ~siteCollectionLayouts
2. BaseViewID and ListTemplateType
Do not set the BaseViewID and ListTemplateType at any stage.  I've only ever had issues with clashing JS Link files and web parts on a single page whenever I've tried to be diligent and set these values correctly.  Leaving them blank seems to work better than anything else.


3. Running Extra Scripts
To do some post rendering magic, like moving things around or kicking off a slideshow, use the command:
overrideCtx.OnPostRender = setupWebPart;This will fire off the aforementioned function AFTER everything has been rendered, so you won't need a separate JS file for this, and it's all nicely encapsulated for reuse somewhere else.  Obviously there will be occasions where you want to reuse the same code in other places, so this isn't always the case.

4. JS Link Template Object
In JS Link you create an override object and have to intrinsically know what are the possible fields you can override.  These are:
  • View
  • Header
  • Body
  • Footer
  • Group
  • Item
  • Fields
  • OnPreRender
  • OnPostRender 
And that's about it.  I'll add to this list if I come across anything more magical which I think I might forget.

Wednesday, February 19, 2014

SharePoint 2013 Search Paging - Auto scroll to top

Quick Answer:

$(window).bind('hashchange', function() {
    document.getElementById("s4-workspace").scrollTop = 0;
});



My Adventure:

So it's not as easy as it seems to override the default behaviour of the paging control on a SP2013/o365 site without the use of deployed code.

  • The paging control itself has an 'onclick' attribute contains javascript similar to the following:
    $getClientControl(this).page(51);return Srch.U.cancelEvent(event);
  • As you can see, the second part of that code - the Srch.U.cancelEvent(event); - seems to prevent any other attached events from firing (I could be wrong here).  This stops me from just attaching an event using jQuery or anything else to scroll to the top of the page.
  • The paging control itself is regenerated on every page call, preventing me from simply modifying the 'onclick' attribute and removing the offending code so I can add my own click events.

So due to all of this I'm left with a few options:
  1. Find the display template which deals with the creation of the paging control and try to kill that cancelEvent() call.
  2. Try to find a way to get my attached event to fire before the 'onclick' attribute.
  3. Try something a bit different.
So I had a look into option 1, and came up blank.  I found the display template responsible for the paging control (Control_SearchResults.html), but I didn't want my change to affect ALL the search results paging controls, so I skipped that approach.

So step 2 seemed to be a bust as I couldn't find a way to beat the 'onclick' attribute - it seemed as steady as adding '!important' to an inline HTML style attribute.

And I finally cracked it with step 3 when I stumbled across a blog (couldn't find it again to reference) which mentioned that you can use the "$(window).bind('hashchange', functionName);" function to fire whenever the page contents change from an ajax call.  So rather than override or add to the click event, I just wait for the page to change due to a filter to be applied, or a page to be changed, and it'll jumped to the top.

Not the most elegant of solutions, and certainly won't cater to every need, but good enough for me for my scenario.

Tuesday, April 17, 2012

Display all XML information

Sometimes it's useful to see exactly what fields are coming through the datasource on a DVWP/CQWP and display the values for each of those.


Use this code to display each element with all values set as attributes of that element:


<xsl:template match="/">
   <xmp><xsl:copy-of select="*"/></xmp>
</xsl:template>

Wednesday, February 15, 2012

Creating a basic news system

A regular task which is used in SharePoint is the creation of a customised news feed. A basic approach to this is a simple three step approach:

1 - Modify the content type to include the fields not included in the default article content type
2 - Create a new page layout which includes the newly added fields for editing
3 - Modify the ItemStyle.xsl to create a new Content Query web part. You can use a data view web part (by pointing to the Pages library and filter on the content type matching the required value if you'd prefer).

Step 1
Create a new Content Type in the System Settings based on the existing Page Layout -> Article page (I called mine News Page). This part isn't particularly tricky, although it's worth pointing out that I added two fields, one called RollupText and one call ShowOnHomePage.

Step 2
This can be a bit trickier. The easiest approach is to copy one of the Article page layouts, then make the required changes. You can use the tag to add sections which are only visible when the page is in edit mode (useful for adding metadata which isn't meant to be visible on the news page itself).

Here is a snippet of my News.aspx page layout with my new RollupText and ShowOnHomePage fields:



<PublishingWebControls:editmodepanel runat="server" id="editmodepanel2">
<table class="header">
<tr>
<td class="rollup-text">
<SharePointWebControls:NoteField FieldName="RollupText" runat="server"/>
</td>
<td class="rollup-text-description">
<span>This text will appear in the rollup summary web part effectively acting as an article summary. Only plain text can be entered into this field (i.e. no font formatting, images or HTML will be accepted).</span>
</td>
</tr>
<tr>
<td class="show-on-home-page">
<SharePointWebControls:BooleanField FieldName="ShowOnHomePage" runat="server"/>
</td>
</tr>
</table>
</PublishingWebControls:editmodepanel>


Step 3
The hardest part of all is the small little things that can go wrong if you've never tried it before. Things I encountered were:
- ItemStyle.xsl can be difficult to recreate due to my inexperience with XML. Here is my XML chunk which I ended up using for my web part:

<xsl:template name="ImageAlternate" match="Row[@Style='ImageAlternate']" mode="itemstyle">
<xsl:variable name="CurPosition" select="count(./preceding-sibling::*)" />
<xsl:variable name="css-class">
<xsl:choose>
<xsl:when test="($CurPosition mod 2 = 0)">news-rollup-image-right</xsl:when>
<xsl:otherwise>news-rollup-image-left</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="SafeLinkUrl">
<xsl:call-template name="OuterTemplate.GetSafeLink">
<xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="SafeImageUrl">
<xsl:call-template name="OuterTemplate.GetSafeStaticUrl">
<xsl:with-param name="UrlColumnName" select="'ImageUrl'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="DisplayTitle">
<xsl:call-template name="OuterTemplate.GetTitle">
<xsl:with-param name="Title" select="@Title"/>
<xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="LinkTarget">
<xsl:if test="@OpenInNewWindow = 'True'" >_blank</xsl:if>
</xsl:variable>
<div class="news-rollup-container" link="{$SafeLinkUrl}">
<div id="linkitem" class="news-rollup-item">
<xsl:if test="string-length($SafeImageUrl) != 0">
<div class="news-rollup-image {$css-class}">
<img class="news-rollup-image" src="{$SafeImageUrl}" alt="{@ImageUrlAltText}" />
</div>
</xsl:if>
<div class="news-rollup-text">
<xsl:call-template name="OuterTemplate.CallPresenceStatusIconTemplate"/>
<div class="news-rollup-title">
<xsl:value-of select="$DisplayTitle"/>
</div>
<div class="news-rollup-description">
<xsl:value-of select="@RollupText" disable-output-escaping="yes"/>
</div>
</div>
</div>
</div>
</xsl:template>

- After you add the Content Query web part to the page that you intend to use, then you need to make sure you've selected your new page layout as the source. After you've done this you MUST EXPORT THE WEBPART and add in the CommonViewFields property the new properties that you've added if you want to view them in the web part. CQWPs have a very limited selection of fields to display.

- After this it was really all styling and javascript to make it look and operate correctly. I like to add a 'link' attribute to the outer containing div for each element which links to the news article, then use javascript on the page load to set up a mouse over CSS style and click event. I would prefer to user the CSS :hover tag, but versions of IE that I must support doesn't work with it.

- Also worth changing can be the UseCache setting in the web part.  This is set to True by default, but can prevent certain fields coming through if they are recently added.  I had to set this to false as I could see content in my CQWP in edit mode, but then nothing would appear in the standard view.

- If you can't find out what the field type for the new column you've added is, try creating a new page layout in SharePoint designer, and add the fields manually.  Designer should create the correct fields for you, so even if you don't want to use designer for creating your page layouts, it can help with small annoying things (like exactly what field type a "Yes/No" field is (it's a SharePointWebControls:BooleanField).

Monday, January 23, 2012

IE flicker when using jQuery slide animation

Everytime that I have tried to use the jQuery slide animation in IE I have been put off by the flicker that occurs when the animation completes. In order to get around this, try the following code in the document ready function:

// Define overriding method.
jQuery.fx.prototype.hide = function(){

// Remember where we started, so that we can go back to it later
this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
this.options.hide = true;

// Begin the animation
this.custom(this.cur(), 1);
}

Friday, January 20, 2012

Returning new ID with SPServices 'UpdateListItems'

When using the results of SPServices there is a single line of code that saves an extra server call to either return the most recently added ID. That line is:

var newId = $(xData.responseXML).find("[nodeName='z:row']").attr("ows_ID");

The beautiful thing about this code is that it works in all browsers (no more checking if you're using Chrome and changing the nodeName to z\\:row), and it's faster than using another web service to return the new ID for you to use.

Here's a basic sample SPService call which uses it:

$().SPServices({
operation: "UpdateListItems",
listName: thisGrid.attr("ListName"),
batchCmd: "New",
async: false,
valuepairs: [
["Title", $('.edit-title-textbox', thisGrid).val() ],
["Description", $('.edit-description-textbox', thisGrid).val() ],
["Link", $('.edit-link-textbox', thisGrid).val() ],
["Image", $('.edit-image-textbox', thisGrid).val()]
],
completefunc: function(xData, Status) {
var newId = $(xData.responseXML).find("[nodeName='z:row']").attr("ows_ID");
thisGrid.find('.box-item[SharePointID=' + thisGrid.attr('SharePointID') + ']').attr('SharePointID', newId);
thisGrid.find('img[SharePointID=' + thisGrid.attr('SharePointID') + ']', newId);
thisGrid.attr('SharePointID', newId);
}
});