The List View Web Part has great capability for filtering displayed data using the web parts header controls..
If you’re building out a UI which incorporates a list view web part, you can also perform the filtering using Javascript in response to your UI events, hooking into the LVWP filtering code.
As an example, what I want to build is a UI which allows me to filter a LVWP using Year and Month controls shown below..
So when I click on a Year, the LVWP is filtered by that year..
And likewise for the Month..you get the picture
In the screenshots above, the columns I’m filtering by are present in the view, but they don’t have to be I’ve only shown them there for demonstration purposes.
This can be done in both 2010 and 2013, though the code is different; in 2013 we call the RefreshPaging function and in 2010 we call the page postback function __doPostBack using the control id of the LVWP with correctly formed event arguments – if you’ve ever done any work with the SPGridView control this will sound familiar.
INPLVIEW.JS
Both versions make use of the javascript ContextInfo ‘objects’ which SharePoint whips up for list view web parts and in particular relies on the InitAllClvps function found in this file.
In my implementation I’m using Knockout to manage the filter UI and subscriptions to the Knockout Year/Month observables to perform the actual filtering.
HTML (2013)
<link rel="stylesheet" type="text/css" href="../SiteAssets/sgm-meetings/css/fontello.css" runat="server" /> | |
<!--[if IE 7]> | |
<link rel="stylesheet" type="text/css" href="../SiteAssets/sgm-meetings/css/fontello-ie7.css" runat="server" /> | |
<![endif]--> | |
<!-- App CSS --> | |
<link rel="stylesheet" type="text/css" href="../SiteAssets/sgm-meetings/css/cdtm-main.css" runat="server" /> | |
<!-- Lib JS --> | |
<script type="text/javascript" src="../SiteAssets/sgm-meetings/scripts/knockout-3.1.0.js"></script> | |
<!-- App JS --> | |
<script type="text/javascript" src="../SiteAssets/sgm-meetings/app/cdtm-app.js"></script> | |
<div id="cdtm" class="cdtm"> | |
<div class="cdtm-wrapper"> | |
<div class='box-5'> | |
<div class='header'> | |
<h1> | |
<span data-bind='text: LibraryTitle'></span><span data-bind='visible: LibraryTitle && LibrarySubTitle' style='display:none;'> <i class='icon-angle-right' style=''></i> </span><span data-bind='text: LibrarySubTitle'></span> | |
<img data-bind='visible: Working' src='/_layouts/15/images/hig_progcircle_loading24.gif' border='0' /> | |
</h1> | |
</div> | |
<div class="nav"> | |
<div class="row"> | |
<div class='left'> | |
<ul class='years' data-bind='foreach: FilterYears' title='filter the meetings by year'> | |
<li data-bind='css: { selected: $parent.isSelectedYear($data) }'> | |
<a href='#' | |
data-bind=' | |
text: $data, | |
click: $parent.filterByYear'> | |
</a></li> | |
</ul> | |
<ul class='months' data-bind='foreach: FilterMonths' title='filter the meetings by month'> | |
<li data-bind='css: { selected: $parent.isSelectedMonth($data) }'> | |
<a href='#' | |
data-bind=' | |
text: $data, | |
click: $parent.filterByMonth'> | |
</a></li> | |
</ul> | |
</div> | |
<div class='right'> | |
<div class='search-wrapper'> | |
<input class='searchfield' type='text' value='' data-bind='value: FilterTextIm, valueUpdate: "afterkeydown"' title='filter the meetings by name' /> | |
<i class='icon-search'></i> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="maincontent"> | |
Content Goes Here | |
</div> | |
</div> | |
</div> | |
</div> |
JAVASCRIPT (2013)
(function(module,$) { | |
"use strict"; | |
window.pd = window.pd || {}; | |
pd.FilterLibraryViewModel = function() { | |
/* observable state */ | |
this.FilterTextIm = ko.observable(''), | |
this.FilterText = ko.computed(this.FilterTextIm) | |
.extend({ throttle: 400 }), | |
this.LibraryTitle = ko.observable(null), | |
this.LibrarySubTitle = ko.observable(), | |
this.FilterYears = new ko.observableArray([]), | |
this.FilterMonths = new ko.observableArray(['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']), | |
this.SelectedYear = ko.observable(''), | |
this.SelectedMonth = ko.observable(''); | |
this.Working = ko.observable(true); | |
/* populate years filter as Current Year..-=4 */ | |
for(var cy = parseInt(new Date().getFullYear()), y = cy; y > (cy-4); y--) { this.FilterYears.push(y.toString()); } | |
/* model actions */ | |
this.filterByYear = function(year) { | |
this.FilterTextIm(''); // zap the filter text | |
year = (year === this.SelectedYear()) ? '' : year; | |
this.SelectedYear(year); | |
}.bind(this); | |
this.filterByMonth = function(month) { | |
this.FilterTextIm(''); // zap the filter text | |
month = (month === this.SelectedMonth()) ? '' : month; | |
this.SelectedMonth(month); | |
}.bind(this); | |
/* model behaviour */ | |
this.isSelectedYear = function (year) { | |
var y = this.SelectedYear(); | |
return (y == year); | |
}.bind(this); | |
this.isSelectedMonth = function (month) { | |
var m = this.SelectedMonth(); | |
return (m == month); | |
}.bind(this); | |
} | |
pd.FilterLibraryApp = function() { | |
var | |
viewModel = new pd.FilterLibraryViewModel(), | |
debug = true, | |
wpCtx = null, | |
wpEvent = null, | |
_module = { | |
start: start | |
}; | |
return _module; | |
function locateListView() { | |
EnsureScript("inplview", typeof InitAllClvps, null, true); | |
InitAllClvps(); | |
for(var k in g_ctxDict) { | |
if (debug && window.console) console.log(g_ctxDict[k]); | |
if (g_ctxDict[k].ListTitle === 'Documents') { | |
wpCtx = window['ctx'+g_ctxDict[k].ctxId]; | |
break; | |
} | |
} | |
if (debug && window.console) { | |
console.log(wpCtx.clvp); | |
console.log(wpCtx.clvp.ctx); | |
console.log(wpCtx.clvp.ctx.view); | |
console.log(wpCtx.clvp.ctx.ListSchema.Filter); | |
console.log(wpCtx.clvp.ctx.ListData.FilterLink); | |
} | |
var clvp = (wpCtx && wpCtx.clvp) ? wpCtx.clvp : null; | |
wpEvent = { clvp: clvp, event: { currentCtx: { clvp: clvp } } }; | |
viewModel.LibraryTitle(wpCtx.ListTitle); | |
viewModel.LibrarySubTitle('All'); | |
viewModel.Working(false); | |
//$('#' + wpCtx.wpq + '_ListTitleViewSelectorMenu_Container').hide(); | |
} | |
function filterByText() { | |
if (!wpCtx) { | |
alert('Unable to filter: the list view webpart could not be located!'); | |
return; | |
} | |
var filterText = this.FilterText(); | |
if (debug && window.console) { console.log(">>FilterByText: ["+filterText+"]"); } | |
var $webpart = $("#WebPart" + wpCtx.wpq); | |
$webpart.find("tr.ms-itmhover").each(function() { | |
var $tr = $(this); | |
if (filterText && filterText.length) { | |
var | |
r = new RegExp(filterText,'gi'), | |
matched = false, | |
$tds = $tr.find(".ms-vb-title,.ms-vb-2").first(); | |
$tr.find(".ms-vb-title,.ms-vb2").each(function() { | |
var td = $(this).text(); | |
if(!r.test(td)) return true; | |
matched = true; | |
return false; | |
}); | |
if(matched) $tr.fadeIn(250); | |
else $tr.fadeOut(250); | |
} else { | |
$tr.fadeIn(250); | |
} | |
}); | |
} | |
function onFilterChange(filterType) { | |
if (!wpCtx) { | |
alert('Unable to filter: the list view webpart could not be located!'); | |
return; | |
} | |
var | |
filterColumns = [], | |
filterValues = [], | |
fyv = this.SelectedYear(), | |
fym = this.SelectedMonth(); | |
if (fyv && fyv.length) { | |
filterColumns.push('catYear'); | |
filterValues.push(fyv); | |
} | |
if (fym && fym.length) { | |
filterColumns.push('catMonth'); | |
filterValues.push(fym); | |
} | |
var filterUrl; | |
if (!filterColumns.length) { | |
/* clear filter */ | |
filterUrl = _spPageContextInfo.webAbsoluteUrl + window.location.pathname + '?View=' + wpCtx.clvp.ctx.view + '&FilterClear=1'; | |
} else { | |
filterUrl = _spPageContextInfo.webAbsoluteUrl + window.location.pathname + '?View=' + wpCtx.clvp.ctx.view; | |
for(var i = 0; i < filterColumns.length; i++) { | |
if (debug && window.console) { console.log(">>FilterBy: ["+filterColumns[i]+"] = ["+filterValues[i]+"]"); } | |
filterUrl += '&FilterField' | |
+ (i+1) + '=' + filterColumns[i] | |
+ '&FilterValue' | |
+ (i+1) + '=' + filterValues[i]; | |
} | |
if (debug && window.console) { console.log(">>FilterUrl: "+filterUrl); } | |
} | |
if (wpEvent.clvp == null || wpEvent.clvp.ctx == null || !wpEvent.clvp.ctx.IsClientRendering) { | |
HandleFilter(wpEvent.event, filterUrl); | |
} else { | |
wpEvent.clvp.RefreshPaging(filterUrl); | |
wpEvent.clvp.ctx.queryString = filterUrl; | |
} | |
} | |
function start() { | |
ko.applyBindings(viewModel, $('#cdtm').get(0)); | |
viewModel.FilterText.subscribe(filterByText, viewModel, "change"); | |
viewModel.SelectedYear.subscribe(onFilterChange.bind(viewModel, 'year')); | |
viewModel.SelectedMonth.subscribe(onFilterChange.bind(viewModel, 'month')); | |
locateListView(); | |
} | |
}(); | |
$(pd.FilterLibraryApp.start); | |
})({ Name: 'Module' },jQuery); | |
Looking into the implementation the code in pd.FilterLibraryViewModel
is just the knockout viewmodel, the code in pd.FilterLibraryApp
is whats sets things up and performs the filtering.
The locateListView
function ensures that the inplview.js
file is loaded (via SOD) and calls InitAllClvps()
. It then looks through the list of ContextInfo
objects looking for the one for our list view web part. It then performs some object mangling to get things right for the filtering to work – you’ll have to delve deep into inplview.js
to see why, but this is just what SharePoint requires.
Having got everything setup, when clicking on a Year or Month the onFilterChange
function is called in response to a change in the viewmodel observables, this works out the filter url required based on the viewmodel observable state and calls the RefreshPaging
(inplview.js)
function – the list view web part then gets filtered in place without requiring a post back.
Views with Person/Group Columns
For some reason I’ve yet to fathom, when you include an Person/Group column in the view, in-place filtering does not work and a full page postback occurs.
Although not the subject of this post, the sample code included here also includes a text box which filters the table rows of the list view web part, but only the rows currently displayed.
How to Use.
Simply drop a list view web part on to a page and configure the view as you’d like it, then drop a content editor web part onto the page and configure it to point to the HTML file – which includes the script and CSS files.
Links to Resources
CSS (2013)
/* CLEARFIX */ | |
.cdtm-wrapper .row:before, | |
.cdtm-wrapper .row:after { | |
content: ""; | |
display: table; | |
} | |
.cdtm-wrapper .row:after { | |
clear: both; | |
} | |
.cdtm-wrapper .row { | |
zoom: 1; /* For IE 6/7 (trigger hasLayout) */ | |
} | |
.cdtm-wrapper .row .left { | |
float: left; | |
} | |
.cdtm-wrapper .row .right { | |
float: left; | |
} | |
/* LAYOUT */ | |
.cdtm { | |
display: inline-block; | |
width: 100%; | |
/*border: 1px dotted #aaa;*/ | |
} | |
.cdtm-wrapper { | |
font-family: 'Open Sans',Calibri,Verdana,Arial,'Sans Serif'; | |
width: 100%; | |
font-size: 12px; | |
} | |
/* PADDING/MARGINS */ | |
.cdtm-wrapper > .box-5 { | |
padding: 5px 10px 10px 0; | |
} | |
.cdtm-wrapper .header { | |
margin: 0; | |
padding: 0; | |
} | |
.cdtm-wrapper .nav { | |
margin: 0; | |
padding: 0; | |
} | |
.cdtm-wrapper article { | |
margin: 0; | |
padding: 0; | |
} | |
/* HEADER & NAVIGATION */ | |
.cdtm-wrapper .header h1, .cdtm-wrapper .header h2, .cdtm-wrapper .header h3, .cdtm-wrapper .header h4, .cdtm-wrapper .header h5 { | |
color: #415A6C; | |
margin: 0 0 8px 0; | |
padding: 0; | |
font-size: 18pt; | |
line-height: 18pt; | |
font-weight: 700; | |
} | |
.cdtm-wrapper .header h2 { | |
font-size: 16pt; | |
} | |
.cdtm-wrapper .header h3 { | |
font-size: 14pt; | |
} | |
.cdtm-wrapper .header h4 { | |
font-size: 12pt; | |
} | |
.cdtm-wrapper .header h5 { | |
font-size: 12pt; | |
} | |
.cdtm-wrapper .header h1 *[class^='icon-'] { | |
font-size: 20px; | |
line-height: 20px; | |
} | |
.cdtm-wrapper .nav .left { | |
display: inline-block; | |
width: 60%; | |
min-width: 290px; | |
max-width: 410px; | |
} | |
.cdtm-wrapper .nav .right { | |
display: inline-block; | |
width: 40%; | |
max-width: 250px; | |
} | |
.cdtm-wrapper .nav ul { | |
list-style: none outside none; | |
margin: 0 0 5px; | |
padding: 0; | |
} | |
.cdtm-wrapper .nav ul.years { | |
font-size: 1.5em; | |
} | |
.cdtm-wrapper .nav ul.months { | |
font-size: 1.2em; | |
} | |
.cdtm-wrapper .nav li { | |
display: inline-block; | |
margin: 0; | |
padding: 0; | |
} | |
.cdtm-wrapper .nav li a, .cdtm-wrapper .nav li a:link { | |
color: #E24912; | |
display: inline-block; | |
padding: 2px 3px; | |
margin-right: 2px; | |
} | |
.cdtm-wrapper .nav li a:hover { | |
color: #002596; | |
cursor: pointer; | |
background-color: rgb(0,180,240); | |
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; /* IE 8/9 */ | |
filter: alpha(opacity=50); /* IE 5.5 - 7 */ | |
background-color: rgba(0,180,240,0.50); | |
} | |
.cdtm-wrapper .nav li.selected a, | |
.cdtm-wrapper .nav li.selected a:link { | |
background-color: rgb(0,180,240); | |
background-color: rgba(0,180,240,1); | |
color: whitesmoke; | |
} | |
/* SEARCH FIELD */ | |
.cdtm-wrapper .nav .right .search-wrapper { | |
border: 1px solid #c8c8c8; | |
border-radius: 5px; | |
padding: 5px; | |
position: relative; | |
} | |
.cdtm-wrapper .nav .right .search-wrapper .searchfield { | |
background-color: #fff; | |
border: medium none; | |
border-radius: 3px; | |
color: #aeaeae; | |
display: inline-block; | |
font-size: 1.5em; | |
font-weight: normal; | |
margin: 0; | |
transition: all 0.2s linear 0s; | |
width: 88%; | |
} | |
.cdtm-wrapper .nav .right .search-wrapper .searchfield:focus { | |
color: #858585; | |
} | |
.cdtm-wrapper .nav .right .search-wrapper .icon-search { | |
color: #d5d8de; | |
display: inline-block; | |
font-size: 2em; | |
line-height: 10px; | |
position: absolute; | |
right: 6px; | |
text-indent: -3px; | |
width: 26px; | |
} | |
.cdtm-wrapper .nav .right .search-wrapper .icon-search:hover { | |
color: #415A6C; | |
cursor: pointer; | |
} | |
/* MAIN CONTENT */ | |
.cdtm-wrapper .maincontent { | |
display: none; | |
margin-top: 10px; | |
border-top: 1px solid #DDE0E4; | |
padding: 10px 2px 0 2px; | |
} | |
.ms-webpartzone-cell { | |
margin: auto auto 0; | |
} | |
/* fixup SP display */ | |
#MSOZoneCell_WebPartWPQ2 .ms-PartSpacingVertical { | |
margin-top: 0; | |
} | |
div.welcome { | |
padding-top: 0; | |
} | |
[class]#pageStatusBar { | |
margin: 10px 0 -10px 0; | |
} |
HTML (2010)
<link rel="stylesheet" type="text/css" href="../SiteAssets/sgm-meetings/css/fontello.css" runat="server" /> | |
<!--[if IE 7]> | |
<link rel="stylesheet" type="text/css" href="../SiteAssets/sgm-meetings/css/fontello-ie7.css" runat="server" /> | |
<![endif]--> | |
<!-- Lib CSS --> | |
<link rel="stylesheet" type="text/css" href="../SiteAssets/sgm-meetings/css/cdtm-main.css" runat="server" /> | |
<!-- Lib JS --> | |
<script type="text/javascript" src="../SiteAssets/sgm-meetings/scripts/knockout-3.1.0.js"></script> | |
<!-- App JS --> | |
<script type="text/javascript" src="../SiteAssets/sgm-meetings/app/cdtm-app.js"></script> | |
<div id="cdtm" class="cdtm"> | |
<div class="cdtm-wrapper"> | |
<div class='box-5'> | |
<div class='header'> | |
<h1> | |
<span data-bind='text: LibraryTitle'></span><span data-bind='visible: LibraryTitle && LibrarySubTitle' style='display:none;'> <i class='icon-angle-right' style=''></i> </span><span data-bind='text: LibrarySubTitle'></span> | |
<img data-bind='visible: Working' src='/_layouts/images/hig_progcircle_loading24.gif' border='0' /> | |
</h1> | |
</div> | |
<div class="nav"> | |
<div class="row"> | |
<div class='left'> | |
<ul class='years' data-bind='foreach: FilterYears' title='filter the meetings by year'> | |
<li data-bind='css: { selected: $parent.isSelectedYear($data) }'> | |
<a href='#' | |
data-bind=' | |
text: $data, | |
click: $parent.filterByYear'> | |
</a></li> | |
</ul> | |
<ul class='months' data-bind='foreach: FilterMonths' title='filter the meetings by month'> | |
<li data-bind='css: { selected: $parent.isSelectedMonth($data) }'> | |
<a href='#' | |
data-bind=' | |
text: $data, | |
click: $parent.filterByMonth'> | |
</a></li> | |
</ul> | |
</div> | |
<div class='right'> | |
<div class='search-wrapper'> | |
<input class='searchfield' type='text' value='' data-bind='value: FilterTextIm, valueUpdate: "afterkeydown"' title='filter the meetings by name' /> | |
<i class='icon-search'></i> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> |
JAVASCRIPT (2010)
(function(module,$) { | |
"use strict"; | |
window.pd = window.pd || {}; | |
pd.MeetingsViewModel = function() { | |
/* observable state */ | |
this.FilterTextIm = ko.observable(''); | |
this.FilterText = ko.computed(this.FilterTextIm) | |
.extend({ throttle: 400 }); | |
this.LibraryTitle = ko.observable(''); | |
this.LibrarySubTitle = ko.observable(''); | |
this.FilterYears = new ko.observableArray([]); | |
this.FilterMonths = new ko.observableArray(['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']); | |
this.SelectedYear = ko.observable(''); | |
this.SelectedMonth = ko.observable(''); | |
this.Working = ko.observable(true); | |
/* populate years filter as Current Year..-=4 */ | |
for(var cy = parseInt(new Date().getFullYear()), y = cy; y > (cy-4); y--) { this.FilterYears.push(y.toString()); } | |
/* model actions */ | |
this.filterByYear = function(year) { | |
this.FilterTextIm(''); // zap the filter text | |
year = (year === this.SelectedYear()) ? '' : year; | |
this.SelectedYear(year); | |
}.bind(this); | |
this.filterByMonth = function(month) { | |
this.FilterTextIm(''); // zap the filter text | |
month = (month === this.SelectedMonth()) ? '' : month; | |
this.SelectedMonth(month); | |
}.bind(this); | |
/* model behaviour */ | |
this.isSelectedYear = function (year) { | |
var y = this.SelectedYear(); | |
return (y == year); | |
}.bind(this); | |
this.isSelectedMonth = function (month) { | |
var m = this.SelectedMonth(); | |
return (m == month); | |
}.bind(this); | |
} | |
pd.FilterLibraryApp = function() { | |
var | |
viewModel = new pd.MeetingsViewModel(), | |
debug = true, | |
wpCtx = null, | |
wpEvent = null, | |
filterControlId = null, | |
listViewTableId = null, | |
_module = { | |
start: start | |
}; | |
return _module; | |
function locateListView() { | |
var fn = function() { | |
InitAllClvps(); | |
// find the CTX for the LVWP | |
for(var k in g_ctxDict) { | |
if (debug && window.console) console.log(g_ctxDict[k]); | |
if (g_ctxDict[k].ListTitle === module.LibraryTitle) { | |
wpCtx = window['ctx'+g_ctxDict[k].ctxId]; | |
break; | |
} | |
} | |
if (debug && window.console) { | |
console.log(wpCtx.clvp); | |
console.log(wpCtx.clvp.ctx); | |
console.log(wpCtx.clvp.ctx.view); | |
} | |
/* this isn't used in the 2010 version, not figured out if/how to use it yet | |
var clvp = (wpCtx && wpCtx.clvp) ? wpCtx.clvp : null; | |
wpEvent = { clvp: clvp, event: { currentCtx: { clvp: clvp } } }; | |
*/ | |
// find the LVWP and table containing the rows | |
var | |
$wpnode = $('#'+wpCtx.clvp.wpq).first(), | |
$wpTableNode = $wpnode.find("table[id^='onetidDoclibViewTbl']"); | |
listViewTableId = $wpTableNode.attr('id'); | |
// find the postback control id; e.g. ctl00$m$g_109629d4_d78b_4c9e_8ec0_90078b6e444e$ctl02 | |
var | |
$wpinput = $wpnode.find('> :first-child > input:first-child'), | |
ctrlid = $wpinput.attr('id'); | |
ctrlid = ctrlid && ctrlid.length | |
? ctrlid.replace(/^(ctl\d\d)_/gi,'$1$') | |
.replace(/_g_/g,'$g_') | |
.replace(/_(ctl\d\d)$/gi,'$$$1') | |
: ''; | |
if (debug && window.console) { console.log(">>locateListView: controlid="+ctrlid); } | |
filterControlId = ctrlid; | |
viewModel.LibraryTitle(wpCtx.ListTitle); | |
viewModel.LibrarySubTitle('All'); | |
viewModel.Working(false); | |
}; | |
EnsureScript("inplview", typeof InitAllClvps, fn); | |
} | |
function filterByText() { | |
if (!filterControlId) { | |
alert('Unable to filter: the list view webpart could not be located or the list view contains a Person/Group column!'); | |
return; | |
} | |
var filterText = this.FilterText(); | |
if (debug && window.console) { console.log(">>FilterByText: ["+filterText+"]"); } | |
var $wpTableNode = $("#"+listViewTableId); | |
$wpTableNode.find("tr.ms-itmhover").each(function() { | |
var $tr = $(this); | |
if (filterText && filterText.length) { | |
var | |
r = new RegExp(filterText,'gi'), | |
matched = false; | |
$tr.find(".ms-vb-title,.ms-vb2").each(function() { | |
var td = $(this).text(); | |
if(!r.test(td)) return true; | |
matched = true; | |
return false; | |
}); | |
if(matched) $tr.fadeIn(250); | |
else $tr.fadeOut(250); | |
} else { | |
$tr.fadeIn(250); | |
} | |
}); | |
} | |
function onFilterChange(filterType) { | |
if (!filterControlId) { | |
alert('Unable to filter: the list view webpart could not be located or the list view contains a Person/Group column!'); | |
return; | |
} | |
var | |
filterColumn = null, | |
filterValue = ''; | |
if (filterType.match(/year/gi)) { | |
filterValue = this.SelectedYear(); | |
filterColumn = 'catYear'; | |
} else if (filterType.match(/month/gi)) { | |
filterValue = this.SelectedMonth(); | |
filterColumn = 'catMonth'; | |
} | |
if (!filterValue) filterValue = '##dvt_all##'; | |
var filterCall = "__doPostBack('"+filterControlId+"','NotUTF8;__filter={"+filterColumn+"="+filterValue+"}')" | |
if (debug && window.console) { | |
console.log(">>Filter by " + filterType + " on ["+filterColumn+"] = ["+filterValue+"]"); | |
console.log(" >> " + filterCall); | |
} | |
eval(filterCall); | |
} | |
function start() { | |
ko.applyBindings(viewModel, $('#cdtm').get(0)); | |
viewModel.FilterText.subscribe(filterByText, viewModel); | |
viewModel.SelectedYear.subscribe(onFilterChange.bind(viewModel, 'year')); | |
viewModel.SelectedMonth.subscribe(onFilterChange.bind(viewModel, 'month')); | |
locateListView(); | |
} | |
}(); | |
$(pd.FilterLibraryApp.start); | |
})({ Name: 'Module', LibraryTitle: 'CDLT Meetings' },jQuery); | |
CSS (2010)
/* CLEARFIX */ | |
.cdtm-wrapper .row:before, | |
.cdtm-wrapper .row:after { | |
content: ""; | |
display: table; | |
} | |
.cdtm-wrapper .row:after { | |
clear: both; | |
} | |
.cdtm-wrapper .row { | |
zoom: 1; /* For IE 6/7 (trigger hasLayout) */ | |
} | |
.cdtm-wrapper .row .left { | |
float: left; | |
} | |
.cdtm-wrapper .row .right { | |
float: left; | |
} | |
/* LAYOUT */ | |
.cdtm { | |
display: inline-block; | |
width: 100%; | |
} | |
.cdtm-wrapper { | |
font-family: 'Open Sans',Calibri,Verdana,Arial,'Sans Serif'; | |
width: 100%; | |
font-size: 12px; | |
} | |
/* PADDING/MARGINS */ | |
.cdtm-wrapper > .box-5 { | |
padding: 5px 10px 10px 0; | |
} | |
.cdtm-wrapper .header { | |
margin: 0; | |
padding: 0; | |
} | |
.cdtm-wrapper .nav { | |
margin: 0; | |
padding: 0; | |
} | |
.cdtm-wrapper article { | |
margin: 0; | |
padding: 0; | |
} | |
/* HEADER & NAVIGATION */ | |
.cdtm-wrapper .header h1, .cdtm-wrapper .header h2, .cdtm-wrapper .header h3, .cdtm-wrapper .header h4, .cdtm-wrapper .header h5 { | |
color: #415A6C; | |
margin: 0 0 8px 0; | |
padding: 0; | |
font-size: 18pt; | |
line-height: 18pt; | |
font-weight: 700; | |
} | |
.cdtm-wrapper .header h2 { | |
font-size: 16pt; | |
} | |
.cdtm-wrapper .header h3 { | |
font-size: 14pt; | |
} | |
.cdtm-wrapper .header h4 { | |
font-size: 12pt; | |
} | |
.cdtm-wrapper .header h5 { | |
font-size: 12pt; | |
} | |
.cdtm-wrapper .header h1 *[class^='icon-'] { | |
font-size: 20px; | |
line-height: 20px; | |
} | |
.cdtm-wrapper .nav .left { | |
display: inline-block; | |
width: 60%; | |
min-width: 290px; | |
max-width: 410px; | |
} | |
.cdtm-wrapper .nav .right { | |
display: inline-block; | |
width: 40%; | |
max-width: 250px; | |
} | |
.cdtm-wrapper .nav ul { | |
list-style: none outside none; | |
margin: 0 0 5px; | |
padding: 0; | |
} | |
.cdtm-wrapper .nav ul.years { | |
font-size: 1.5em; | |
} | |
.cdtm-wrapper .nav ul.months { | |
font-size: 1.2em; | |
} | |
.cdtm-wrapper .nav li { | |
display: inline-block; | |
margin: 0; | |
padding: 0; | |
} | |
.cdtm-wrapper .nav li a, .cdtm-wrapper .nav li a:link { | |
color: #E24912; | |
display: inline-block; | |
padding: 2px 3px; | |
margin-right: 2px; | |
} | |
.cdtm-wrapper .nav li a:hover { | |
color: #002596; | |
cursor: pointer; | |
background-color: rgb(0,180,240); | |
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; /* IE 8/9 */ | |
filter: alpha(opacity=50); /* IE 5.5 - 7 */ | |
background-color: rgba(0,180,240,0.50); | |
} | |
.cdtm-wrapper .nav li.selected a, | |
.cdtm-wrapper .nav li.selected a:link { | |
background-color: rgb(0,180,240); | |
background-color: rgba(0,180,240,1); | |
color: whitesmoke; | |
} | |
/* SEARCH FIELD */ | |
.cdtm-wrapper .nav .right .search-wrapper { | |
border: 1px solid #c8c8c8; | |
border-radius: 5px; | |
padding: 5px; | |
position: relative; | |
} | |
.cdtm-wrapper .nav .right .search-wrapper .searchfield { | |
background-color: #fff; | |
border: medium none; | |
border-radius: 3px; | |
color: #aeaeae; | |
display: inline-block; | |
font-size: 1.5em; | |
font-weight: normal; | |
margin: 0; | |
transition: all 0.2s linear 0s; | |
width: 88%; | |
} | |
.cdtm-wrapper .nav .right .search-wrapper .searchfield:focus { | |
color: #858585; | |
} | |
.cdtm-wrapper .nav .right .search-wrapper .icon-search { | |
color: #d5d8de; | |
display: inline-block; | |
font-size: 2em; | |
line-height: 10px; | |
position: absolute; | |
right: 6px; | |
text-indent: -3px; | |
width: 26px; | |
} | |
.cdtm-wrapper .nav .right .search-wrapper .icon-search:hover { | |
color: #415A6C; | |
cursor: pointer; | |
} | |
Reblogged this on SharePoint : How to ?? and commented:
interesting component for filtering
Thanks for blogging about this solution! It works well to filter an OOTB List View Web Part in O365, but I could not get Ribbon integration on Item Selection working as-is. With the original solution, the Edit/View/Delete Ribbon Actions were always disabled when selecting filtered items. To address that issue, I changed the following line of code ” wpEvent = { clvp: clvp, event: { currentCtx: { clvp: clvp } } };” to ” wpEvent = { clvp: clvp, event: { currentCtx: clvp.ctx } };” and always call HandleFilter(wpEvent.event, filterUrl).
why don’t I see any link or code?
Any way you have made this work with managed metadata columns? They involve a weird lookup structure.
Dan/
Sorry I’ve not tried with MMD columns
I figured out how to do it and it was a chore of talking to the terms tore and hidden taxonomy list but it works great! Cool post by the way!
Thanks for the article. I’ve been waiting to integrate knockout with sharepoint. links to resources and or files do not seem to work though. i’m a litle confused at where the code to implement this is…
Is there a way to submit more than one filter via __doPostback()?
Dave/
Not that I’m aware of
@Phil: I found out. It is possible, but thanks anyway… Just do multiple postbacks in a row. Sharepoint saves the former filters and automatically combines them (with an AND-relationship)…
But now I need a way to sort via postback…
Dave/ good find 🙂
Does this work on library web parts? I saved all the files and adapted the html file for my environment URLs but after pointing the content editor at the html file it hangs at the progress loading .gif
Michael, yes it works on library list view web parts, you might check your browser javascript console for errors
Can I filter a discussion board using this?
mwen/
I haven’t tried but I doubt it would work on discussion boards
Hi and thanks for sharing. I made it work for SP2010 but I had to check the box “Show the actualize button” on the List View Webpart. Otherwise the controlid was always empty in the console.
I would like to reuse the filter – but unfortunately under resource it is not possible to get the reference files.
–> “HTML file – which includes the script and CSS files.”
I would be very happy if i could test the filter by myself
Great post. It would have been really helpful if someone could decouple this component from knockout (I am not familiar with knockout 😦 ). Do you have a plain JavaScript version of this? Thanks
There isn’t a plain Javascript version I’m afraid
he had before a regular plain javascript, for example:
…(alll options)
//Attach change event handler to filter dropdown
$(document.body).on(‘change’, ‘#filter’, function () {
CSWPFilterByField(‘owstaxIdSourceMMD’, $(‘#filter’).val(), ‘contain’));
});
function CSWPFilterByField(_fldName, _fldVal, _fldOperator)
{
// Loop through Query Groups
var groups = Srch.ScriptApplicationManager.get_current().queryGroups;
$.each(groups, function () {
// Look for query groups associated with a CSWP
if (this.displays != null && this.displays.length > 0) {
if (this.displays[0] instanceof Srch.ContentBySearch) {
// Update and execute query – change length according to specific ToolPart query!!
if (_fldVal == “All”) {
var newQuery = this.dataProvider.get_queryTemplate().substring(0, 42);
}
else {
var operator = (_fldOperator == ‘equal’) ? ‘=’ : ‘:’;
var newQuery = this.dataProvider.get_queryTemplate().substring(0, 42) + _fldName + operator + _fldVal;
}
this.dataProvider.set_queryTemplate(newQuery);
this.dataProvider.issueQuery();
//Saving value globally for next filtering
window.option = $(‘#filter’).find(‘option:selected’).val();
}
}
});
}
good luck!
a programmer
Hey, thank you very much for sharing this cool gimmick! Is it possible to have multiselect here? e. g. month Jan + Feb + Mar This would be great 🙂