<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SPServices Archives - Michael Soriano</title>
	<atom:link href="https://michaelsoriano.com/tag/spservices/feed/" rel="self" type="application/rss+xml" />
	<link>https://michaelsoriano.com/tag/spservices/</link>
	<description>I turn code into captivating user experiences for the web</description>
	<lastBuildDate>Fri, 17 Jul 2015 16:23:46 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.7.4</generator>
	<item>
		<title>How to make a SharePoint List work with Bootstrap &#8211; using SPServices and jQuery</title>
		<link>https://michaelsoriano.com/sharepoint-bootstrap-using-spservices-and-jquery/</link>
					<comments>https://michaelsoriano.com/sharepoint-bootstrap-using-spservices-and-jquery/#comments</comments>
		
		<dc:creator><![CDATA[Michael Soriano]]></dc:creator>
		<pubDate>Fri, 17 Jul 2015 16:23:46 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[SharePoint]]></category>
		<category><![CDATA[Bootstrap]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[SOAP]]></category>
		<category><![CDATA[SPServices]]></category>
		<guid isPermaLink="false">http://fearlessflyer.com/?p=4446</guid>

					<description><![CDATA[<p>For those of you who have worked with SharePoint in the past, particularly with lists, CEWPs (Content Editor Web Part) and front end code, then you probably know that these lists do not work well in mobile. The markup is just too messy &#8211; invalid tags, divs inside spans, inline Javascripts, inline styles and even [&#8230;]</p>
<p>The post <a href="https://michaelsoriano.com/sharepoint-bootstrap-using-spservices-and-jquery/">How to make a SharePoint List work with Bootstrap &#8211; using SPServices and jQuery</a> appeared first on <a href="https://michaelsoriano.com">Michael Soriano</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>For those of you who have worked with SharePoint in the past, particularly with lists, CEWPs (<a href="https://support.office.com/en-ca/article/Content-Editor-Web-Part-c1350ff6-934c-4c2e-8e53-1ec3b548a0dc">Content Editor Web Part</a>) and front end code, then you probably know that these lists do not work well in mobile. The markup is just too messy &#8211; invalid tags, divs inside spans, inline Javascripts, inline styles and even tables! So it&#8217;s no surprise that making SharePoint work with Bootstrap is not an easy task.</p>
<h5>Note that this plugin will require the following files:</h5>
<ul>
<li>jquery.min.js</li>
<li>jquery.SPServices-2014.02.min.js</li>
<li>bootstrap.min.css</li>
</ul>
<p>In this post, I will show you how to use a Javascript plugin that I&#8217;ve written to basically recreate the markup of these SP lists so it works with Bootstrap / Mobile. It also has extra functionality like paging, filters, expand/collapse and other options. By the time we&#8217;re done, your list will look real good in desktop, tablet and mobile view:<br />
<a href="https://github.com/michaelsoriano/sp-bootstrap-list-viewer">View in Github</a></p>
<h3>How to use the Plugin:</h3>
<p>Note that I&#8217;ve only tested this with SharePoint 2013. You may need administrator rights to your website, as well as you should know how to work with custom lists and document libraries and such.<br />
After you have downloaded the files, open the &#8220;sp-bootstrap-list-viewer.html&#8221; in you text editor. This is where you will add the plugin information.<br />
You have to change the CHANGE-TO-YOUR-PATH in the JavaScript and CSS inclusion &#8211; to your downloaded path.<br />
Then, you add 2 things: the list name, the &#8220;Answer&#8221; column. The rest of the options are optional such as the &#8220;filterBy&#8221; and &#8220;rowLimit&#8221;. The table below will show you want the rest of the options do.<br />
<img fetchpriority="high" decoding="async" class="alignnone size-full" src="https://michaelsoriano.com/wp-content/uploads/2015/07/sp-list3.jpg" alt="sp-list1" width="412" height="358" /><br />
Once you have edited the html file, upload it to a location in your SharePoint site, typically a document library. You will need to copy the location of the file.<br />
<img decoding="async" class="alignnone size-full" src="https://michaelsoriano.com/wp-content/uploads/2015/07/sp-list2.jpg" alt="sp-list1" width="412" height="358" /><br />
Let&#8217;s go ahead and add a CEWP into your SharePoint page. in the &#8220;Content Link&#8221; input, add the location of the html file you&#8217;ve just uploaded.<br />
<img decoding="async" class="alignnone size-full" src="https://michaelsoriano.com/wp-content/uploads/2015/07/sp-list1.jpg" alt="sp-list1" width="412" height="358" /><br />
Click &#8220;OK&#8221; and if everything is configured correctly, you should see the plugin do its thing:<br />
<img decoding="async" class="alignnone size-full wp-image-4447 noborder" src="https://michaelsoriano.com/wp-content/uploads/2015/07/sp-bootstrap-list-viewer.gif" alt="sp-bootstrap-list-viewer" width="787" height="520" /><br />
The table below contains the options for the plugin and an explanation of what they&#8217;re for:</p>
<table class="table table-bordered">
<thead>
<tr>
<th>Option name</th>
<th>Default Value</th>
<th>Required?</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">instance</th>
<td>Random Value</td>
<td>Yes</td>
<td>Used as the &#8220;Id&#8221; of your plugin</td>
</tr>
<tr>
<th scope="row">listName</th>
<td>none</td>
<td>Yes</td>
<td>Name of the SP list</td>
</tr>
<tr>
<th scope="row">question</th>
<td>Title</td>
<td>Yes</td>
<td>Title of the row</td>
</tr>
<tr>
<th scope="row">answer</th>
<td>none</td>
<td>Yes</td>
<td>Body of the row</td>
</tr>
<tr>
<th scope="row">filterBy</th>
<td>none</td>
<td>No</td>
<td>Category column to filter rows</td>
</tr>
<tr>
<th scope="row">rowLimit</th>
<td>5</td>
<td>No</td>
<td>Number of rows to show</td>
</tr>
</tbody>
</table>
<p>One thing to remember. If you plan to use the plugin multiple times in a page, you need to change the &#8220;instance&#8221; value to a random number prefixed with alphanumeric characters. You also need to make this the &#8220;Object&#8221; name of your html (where it says var <strong>spBL233565656</strong> = new spBootstrapList &#8230;). This will make the instances of your plugin unique in every page and will not clash with each other.</p>
<h3>Under the hood:</h3>
<p>For those of you who want to know more about how the plugin works, I will show you some of it&#8217;s main functionality. Note that this was originally built for a FAQ list for SharePoint, hence the FAQ names such as &#8220;Question&#8221; and &#8220;Answer&#8221;. Though the plugin will work with any kind of SP list.</p>
<h6>Requirements</h6>
<p>We will need some help with Mark Anderson&#8217;s <a href="https://github.com/sympmarc/SPServices">SPServices</a> &#8211; a jQuery plugin that basically does the web service calls for us. This allows us to work with the lists&#8217; data and convert it to our desired markup. Of course we will need <a href="http://getbootstrap.com/">Bootstrap </a>&#8211; which will take care of the responsive and overall styles, as well as <a href="https://jquery.com/">jQuery</a> for DOM manipulation.<br />
Notice the required scripts and styles. You can link to them externally or download them to a document library in SharePoint. Our constructor spBootstrapList() calls our Javascript class, passing along our required arguments. Again, the variable name &#8220;spBL233565656&#8221; and the value for &#8220;instance&#8221; has to be identical. This is for using it multiple of these in a single page.<br />
So going into the Javascript plugin: <strong>sp-bootstrap-list-viewer.js</strong>, you will see that on first load there&#8217;s a public method that gets called to set things up &#8220;init()&#8221;. This makes the initial call to the plugin &#8220;SPServices&#8221;. As I&#8217;ve mentioned, the plugin does the AJAX call to the SharePoint web service. It uses SOAP as its protocol so the request / response will be in XML format. Our init() method also checks for valid fields:</p>
<pre class="">var init = function(){
var ajax = methods.callSPServices(_settings.rowLimit, true);
$(ajax.responseXML).SPFilterNode("z:row").each(function(e) {
    var errors = Array();
    if(typeof $(this).attr('ows_' + _settings.question) == 'undefined'){
        errors.push("Invalid field name for Question: " + _settings.question);
    }
    if(typeof $(this).attr('ows_' + _settings.answer) == 'undefined'){
        errors.push("Invalid field name for Answer: " + _settings.answer);
    }
    if(_settings.filterBy !== '') {
        if(typeof $(this).attr('ows_' + _settings.filterBy) == 'undefined'){
            errors.push("Invalid field name for FilterBy: " + _settings.filterBy);
        }
    }
    _errors = errors;
    methods.parseResponse($(this));
});
</pre>
<p>Then we parse our response and we fill our internal object &#8220;_faqs&#8221;: Notice the SharePoint internal list names that start with &#8220;ows_&#8221; &#8211; these are how it comes out of the <a href="https://developers.google.com/doubleclick-publishers/docs/soap_xml">SOAP response</a>. We parse it to our own object with more meaningful keys and it&#8217;s values:</p>
<pre>parseResponse : function(resp){
    var record = {};
    record.question = resp.attr('ows_' + _settings.question);
    record.answer = resp.attr('ows_' + _settings.answer);
    record.created = resp.attr('ows_' + 'Created');
    if(_settings.filterBy !== ''){
        record.filterBy = resp.attr('ows_' + _settings.filterBy);
    }
    _faqs.push(record);
},
</pre>
<p>By the way, in order for us to make public methods from within our plugin is that we assign it to the &#8220;this&#8221; keyword. This way it becomes accessible from the outside. Here is an example of all the public methods of our plugin:</p>
<pre>this.paginate = paginate;
this.filter = filter;
this.showHideFilters = showHideFilters;
this.clearFilter = clearFilter;
this.showHideAnswer = showHideAnswer;
</pre>
<p>Continuing with our process, note that we need the &#8220;_faq&#8221; array to be filled for our next method to fire. This method is called <strong>methods.buildFaqs()</strong>, So we built another object called &#8220;methods&#8221; and inside this is a series of functions. This will be our &#8220;internal&#8221; methods.<br />
If you look at the buildFaqs() method, the code looks like below:</p>
<pre class="">buildFaqs : function(){
  var output = '';
  for(var i=0; i&lt;_faqs.length; i++){
    var hr = (i != (_faqs.length-1)) ? '</pre>
<p><em>[code above has been truncated &#8211; due to syntax highlighting error]</em><br />
So you see, we have our SharePoint list data in our array, and we build the HTML from it.</p>
<h6>Pagination</h6>
<p>Our pagination logic is quite interesting. We use the Bootstrap pagination HTML, and for each page, we need to do a <a href="http://www.w3schools.com/ajax/">AJAX</a> call in order to get the next page ID. This is how SharePoint determines where to offset the record when a query is made by using &#8220;<a href="https://social.msdn.microsoft.com/Forums/en-US/02663336-3185-498a-904f-86845106b624/getlistitems-with-jquery-using-listitemcollectionpositionnext-as-a-query-option?forum=sharepointdevelopmentprevious">ListItemCollectionPositionNext</a>&#8221; as a parameter.</p>
<pre>var buildPagination = function(){
  if(_errors.length &gt; 0) {
      $('#'+ _settings.instance+' .spBLPagerContainer').html('');
      console.log('Exiting buildPagination()');
      return false;
  }
  methods.getTotal(); //inside here we build filters
  if(parseInt(_totalRecords) &gt; parseInt(_settings.rowLimit)) { //yes paginate
      var pages = Math.ceil(_totalRecords / _settings.rowLimit);
      var positions = Array('');
      for (var i=0; i &lt; (pages - 1); i++) { //builds the values required 'ListItemCollectionPositionNext'
          var pos = '';
          if(i == 0){ //skips first to not pass pos
              positions.push(methods.getNextPos(pos));
          }else{
              var offset = i;
              positions.push(methods.getNextPos(positions[offset]));
          }
      }
  // console.log(positions);
  methods.buildPagination(positions);
  }else{ // no need to paginate
      $('#'+ _settings.instance+' .spBLPagerContainer').html('');
  }
}
</pre>
<p>We then proceed to build the actual markup with our internal method: methods.buildPagination():</p>
<pre class="">buildPagination : function(items){
  if(_firstLoad == false){
      $('#'+ _settings.instance+' .spBLPagerContainer').html(pager);
  }
  _positions = items;
  var pager = '</pre>
<p>&nbsp;<br />
<em>[code above has been truncated &#8211; due to syntax highlighting error]</em><br />
Each button has an inline &#8220;onclick&#8221; that calls our public &#8220;paginate()&#8221; method. Each of them also has the attribute &#8220;data-position&#8221; which holds the offset &#8220;ID&#8221; that we talked about:</p>
<pre>var paginate = function(clicked){
  if($(clicked).parents('li').hasClass('active') || $(clicked).parents('li').hasClass('disabled')){
      return false;
  }
  $('#'+ _settings.instance+' .pagination li').removeClass('active');
  var pos = $(clicked).attr('data-position') != '' ? $(clicked).attr('data-position') : '';
  if($(clicked).hasClass('NextButton')){
      $('#'+ _settings.instance+' a[data-position="'+pos+'"]').not('.NextButton').parents('li').addClass('active');
  }else if($(clicked).hasClass('PrevBtn')){
      $('#'+ _settings.instance+' a[data-position="'+pos+'"]').not('.PrevBtn').parents('li').addClass('active');
  }else{
      $(clicked).parents('li').addClass('active');
  }
  methods.updateNextPrevBtns(pos);
  methods.clearFaqs();
  var options = {
      CAMLViewFields: methods.fieldsToRead(true),
      CAMLQuery: methods.query(),
      CAMLRowLimit : _settings.rowLimit,
      CAMLQueryOptions: ""
  }
  var ajax = $().SPServices(methods.mergeSPCommon(options));
  $(ajax.responseXML).SPFilterNode("z:row").each(function(e) {
      methods.parseResponse($(this));
  });
  methods.buildFaqs();
}
</pre>
<p>You noticed too from the code above that we use SPServices on each click. That&#8217;s because we are only grabbing a predefined set of records &#8211; which is the limit that we pass into our plugin.</p>
<h6>Filters</h6>
<p>So the plugin supports filtering. This means that you can choose a field from your list and make that a &#8220;filter&#8221; column.<br />
First, assuming you&#8217;ve selected a valid field, we have to build it. Take a look at the code below:</p>
<pre>getFilterByValues : function(resp){
  var filterList = Array();
  var filterListUnique = Array();
  $(resp.responseXML).SPFilterNode("z:row").each(function(e) {
      var filterValue = $(this).attr('ows_' + _settings.filterBy);
          if(typeof filterValue !== 'undefined'){ //only add to array if theres a value
              filterList.push(filterValue);
          }
  });
  $.each(filterList,function(i,el){ //get rid of duplicates
      if($.inArray(el, filterListUnique) === -1){
          filterListUnique.push(el);
      }
  });
  _filterByListUnique = filterListUnique;
  _filterByList = filterList;
}
</pre>
<p>The above simply grabs the data. And since there are duplicate entries &#8211; we will have to make them unique and fill up our &#8220;_filterByListUnique&#8221; array. This is next:</p>
<pre class="">buildFilters : function(){
  var count = 0;
  var out = '';
  out +=</pre>
<p><em>[code above has been truncated &#8211; due to syntax highlighting error]</em><br />
The above builds out the checkboxes we need for our filtering capabilities. Now when each item is clicked, it triggers our filter() method:</p>
<pre>var filter = function(clicked){
  _firstLoad = false;
  _filterByValues = [];
  $('#'+ _settings.instance+'-filtersArray :checkbox:checked').each(function(e){
      _filterByValues.push($(this).val());
  })
  methods.filter();
}
</pre>
<p>Which actually calls our internal methods.filter() method:</p>
<pre>filter : function(){
  $('#'+ _settings.instance+'-filtersArray input').attr('disabled', 'disabled');
  $('#'+ _settings.instance+' .clearFilter').attr('disabled', 'disabled');
  methods.clearFaqs();
  var ajax = methods.callSPServices(_settings.rowLimit, true);
  $(ajax.responseXML).SPFilterNode("z:row").each(function(e) {
      methods.parseResponse($(this));
  });
  setTimeout(function(){
      methods.buildFaqs();
      buildPagination();
      $('#'+ _settings.instance+'-filtersArray input').removeAttr('disabled');
      $('#'+ _settings.instance+' .clearFilter').removeAttr('disabled');
  },10);
  if(_filterByValues.length &gt; 0){
      $('#'+ _settings.instance + ' .clearFilter').show();
  }else{
      $('#'+ _settings.instance + ' .clearFilter').hide();
  }
}
</pre>
<p>A lot going on here. You see that we make a new AJAX call through SPServices, passing our filter value. We parse the response, rebuild our items through &#8220;methods.buildFaqs()&#8221; and rebuild our pagination through buildPagination().<br />
There&#8217;s much more logic in the plugin such as the creating the <a href="https://msdn.microsoft.com/en-us/library/dd588092%28v=office.11%29.aspx">CAML</a> queries &#8211; but I decided not to go through them. If you&#8217;re well versed with .NET and SharePoint &#8211; you will be able to decipher these easy.</p>
<h3>Console Debugging</h3>
<p>Once you have the code in place &#8211; save and refresh your page. If by any reason some of the options are passed incorrectly, error handling is added and will be displayed in the HTML. You also need to turn on the console for more messages of what&#8217;s going on. I&#8217;ve added a couple of helpers that will get you information about your list such as getAllColumns(). This will display all of the fields that belong to the list you&#8217;re working with. Simply use the instance name + getAllColumns().</p>
<h3>Conclusion</h3>
<p>As you can see, we can make SharePoint internal lists work responsively. We may have to redo the markup entirely &#8211; but there are web services in place to grab the data. Thanks to open source code such as SPServices, jQuery and Bootstrap &#8211; our task becomes somewhat easier.<br />
Feel free to comment below.</p>
<p>The post <a href="https://michaelsoriano.com/sharepoint-bootstrap-using-spservices-and-jquery/">How to make a SharePoint List work with Bootstrap &#8211; using SPServices and jQuery</a> appeared first on <a href="https://michaelsoriano.com">Michael Soriano</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://michaelsoriano.com/sharepoint-bootstrap-using-spservices-and-jquery/feed/</wfw:commentRss>
			<slash:comments>56</slash:comments>
		
		
			</item>
	</channel>
</rss>
