Filtering dynamic content in MVC custom widget designer
Hi there,
I have a custom MVC widget, with a custom MVC designer that allows the user to select one or more records from a dynamic module.
The dynamic module has a "page type" string property. Is it possible to limit the items in my custom MVC widget designers selector to only those where "page type" = [some value]?
The selector I'm using in the designer is as follows:
<div class="form-group">
<label class="control-label">Links</label>
<sf-list-selector sf-dynamic-items-selector
sf-multiselect="true"
sf-item-type="properties.ModuleType.PropertyValue"
sf-selected-ids="itemSelector.selectedItemsIds"
sf-master="true" />
</div>
Cheers
Hi Michael,
You can filter the Items you get by using filter expression. You can check the following sample:
http://docs.sitefinity.com/example-filter-dynamic-content-items-by-dynamic-field
More information about the filter expressions is available here:
http://docs.sitefinity.com/filter-expressions-for-content-items
I hope this helps.
Regards,
Svetoslav Manchev
Telerik
Hi Svetoslav,
I checked out the links you provided, however it's showing how to filter in code-behind, and not in the MVC widget designer.
Is there a way to add a filter to the designer definition? e.g. some kind of filter property in here (as far as I understand the custom MVC widget designer works as an angular app with no code behind that I can access):
<div class="form-group">
<label class="control-label">Links</label>
<sf-list-selector sf-dynamic-items-selector
sf-multiselect="true"
sf-item-type="properties.ModuleType.PropertyValue"
sf-selected-ids="itemSelector.selectedItemsIds"
sf-master="true" />
</div>
Thanks,
Michael
Hello Michael,
Unfortunately it is not possible to achieve your desired behavior with the out-of-the-box functionality of the dynamic content selector.
Fortunately, however, Feather is an open source project available on GitHub and it is easy to look at its source code and plug in in the right places to achieve the desired behavior.
Like most of the things in Feather it is quite easy to override the default scripts for the selectors and services.
Firstly override the sf-dynamic-items-selector directive and find out whether it is currently working with the desired dynamic module. After that pass on that information to the data service. To override the sf-dynamic-items-selector create the following folder structure on the root of the project:
client-components
--selectors
----dynamic-modules
In the dynamic-modules folder create a sf-dynamic-items-selector.js file and make the necessary changes to the source code like so:
(
function
($)
angular.module(
'sfSelectors'
)
.directive(
'sfDynamicItemsSelector'
, [
'sfDataService'
,
function
(dataService)
return
require:
'^sfListSelector'
,
restrict:
'A'
,
link:
pre:
function
(scope, element, attrs, ctrl)
var
master = attrs.sfMaster ===
'true'
|| attrs.sfMaster ===
'True'
;
ctrl.getItems =
function
(skip, take, search)
var
provider = ctrl.$scope.sfProvider;
// check if correct dynamic module and send the information along to the data service
var
isMyModule = ctrl.$scope.sfItemType ==
"Telerik.Sitefinity.DynamicTypes.Model.Pressreleases.PressRelease"
;
if
(master)
return
dataService.getItems(ctrl.$scope.sfItemType, provider, skip, take, search, ctrl.identifierField, isMyModule);
else
return
dataService.getLiveItems(ctrl.$scope.sfItemType, provider, skip, take, search, ctrl.identifierField, isMyModule);
;
ctrl.getSpecificItems =
function
(ids)
var
provider = ctrl.$scope.sfProvider;
if
(master)
return
dataService.getSpecificItems(ids, ctrl.$scope.sfItemType, provider);
else
return
dataService.getSpecificLiveItems(ids, ctrl.$scope.sfItemType, provider);
;
ctrl.selectorType =
'DynamicItemsSelector'
;
ctrl.dialogTemplateUrl =
'client-components/selectors/dynamic-modules/sf-dynamic-items-selector.sf-cshtml'
;
ctrl.$scope.dialogTemplateId =
'sf-dynamic-items-selector-template'
;
var
closedDialogTemplate = attrs.sfMultiselect ?
'client-components/selectors/common/sf-list-group-selection.sf-cshtml'
:
'client-components/selectors/common/sf-bubbles-selection.sf-cshtml'
;
ctrl.closedDialogTemplateUrl = closedDialogTemplate;
;
]);
)(jQuery);
(
function
()
angular.module(
'sfServices'
)
.factory(
'sfDataService'
, [
'serviceHelper'
,
'serverContext'
,
function
(serviceHelper, serverContext)
/* Private methods and variables */
var
serviceUrl = serverContext.getRootedUrl(
'Sitefinity/Services/DynamicModules/Data.svc/'
),
dataItemPromise;
var
getResource =
function
(itemId)
var
url = serviceUrl;
if
(itemId && itemId !== serviceHelper.emptyGuid())
url = url + itemId +
'/'
;
return
serviceHelper.getResource(url);
;
var
getLiveItems =
function
(itemType, provider, skip, take, search, searchField)
var
filter = serviceHelper.filterBuilder()
.searchFilter(search,
null
, searchField)
.and()
.cultureFilter()
.getFilter();
dataItemPromise = getResource(
'live'
).get(
itemType: itemType,
itemSurrogateType: itemType,
provider: provider,
skip: skip,
take: take,
filter: filter
)
.$promise;
return
dataItemPromise;
;
var
getItems =
function
(itemType, provider, skip, take, search, searchField, isMyModule)
var
filter = serviceHelper.filterBuilder()
// pass on the information to the filter
.searchFilter(search,
null
, searchField, isMyModule)
.and()
.cultureFilter()
.getFilter();
dataItemPromise = getResource().get(
itemType: itemType,
itemSurrogateType: itemType,
provider: provider,
skip: skip,
take: take,
filter: filter
)
.$promise;
return
dataItemPromise;
;
var
getSpecificLiveItems =
function
(ids, itemType, provider)
var
filter = serviceHelper.filterBuilder()
.specificItemsFilter(ids)
.and()
.cultureFilter()
.getFilter();
dataItemPromise = getResource(
'live'
).get(
itemType: itemType,
itemSurrogateType: itemType,
provider: provider,
skip: 0,
take: 100,
filter: filter
)
.$promise;
return
dataItemPromise;
;
var
getSpecificItems =
function
(ids, itemType, provider)
var
filter = serviceHelper.filterBuilder()
.specificItemsFilter(ids)
.and()
.cultureFilter()
.getFilter();
dataItemPromise = getResource().get(
itemType: itemType,
itemSurrogateType: itemType,
provider: provider,
skip: 0,
take: 100,
filter: filter
)
.$promise;
return
dataItemPromise;
;
var
getItem =
function
(itemId, itemType, provider)
dataItemPromise = getResource(itemId).get(
itemType: itemType,
provider: provider
)
.$promise;
return
dataItemPromise;
;
return
/* Returns the data items. */
getLiveItems: getLiveItems,
getSpecificLiveItems: getSpecificLiveItems,
getItems: getItems,
getSpecificItems: getSpecificItems,
getItem: getItem
;
]);
)();
(
function
()
var
module = angular.module(
'sfServices'
, [
'ngResource'
,
'serverDataModule'
]);
module.config([
'$httpProvider'
,
function
($httpProvider)
if
(!$httpProvider.defaults.headers.get)
$httpProvider.defaults.headers.get = ;
var
getHeaders = $httpProvider.defaults.headers.get;
//disable IE ajax request caching
//NOTE: This breaks angular logic for loading templates through XHR request leading to 400 - bad request.
//Only specific format is accepted for this header by the server.
//getHeaders['If-Modified-Since'] = 'Thu, 01 Feb 1900 00:00:00';
getHeaders[
'Cache-Control'
] =
'no-cache'
;
getHeaders.Pragma =
'no-cache'
;
]);
module.factory(
'serviceHelper'
, [
'$resource'
,
'serverContext'
,
function
($resource, serverContext)
/* Private methods and variables */
var
emptyGuid =
'00000000-0000-0000-0000-000000000000'
;
function
endsWith(str, suffix)
return
str.indexOf(suffix, str.length - suffix.length) >= 0;
function
trimRight(str, suffix)
return
str.substr(0, str.length - suffix.length);
var
getResource =
function
(url, options, headers, isArray)
var
headerData = headers || ;
var
resourceOption = options || stripTrailingSlashes:
false
;
var
culture = serverContext.getUICulture();
if
(culture && !headerData.SF_UI_CULTURE)
headerData.SF_UI_CULTURE = culture;
//headerData['Cache-Control'] = 'no-cache';
//headerData.Pragma = 'no-cache';
return
$resource(url, ,
get:
method:
'GET'
,
isArray: isArray,
headers: headerData
,
put:
method:
'PUT'
,
isArray: isArray,
headers: headerData
, resourceOption);
;
function
FilterBuilder(baseFilter)
this
.filter = baseFilter ||
''
;
this
.liveItemsFilter =
'Visible==true AND Status==live'
;
this
.andOperator =
' AND '
;
FilterBuilder.prototype =
constructor: FilterBuilder,
lifecycleFilter:
function
()
this
.filter +=
this
.liveItemsFilter;
return
this
;
,
cultureFilter:
function
()
var
culture = serverContext.getUICulture();
if
(culture)
this
.filter +=
'Culture=='
+ culture;
return
this
;
else
return
this
.trimOperator();
,
searchFilter:
function
(search, frontendLanguages, searchField, isMyModule)
var
customFieldName =
"Test"
,
searchString =
"var"
;
if
(!search)
if
(!isMyModule)
return
this
.trimOperator();
this
.filter +=
'('
+ customFieldName +
'.ToUpper().Contains("'
+ searchString +
'".ToUpper()))'
;
return
this
;
var
field = searchField ||
'Title'
;
var
searchFilter;
if
(isMyModule)
searchFilter =
'('
+ field +
'.ToUpper().Contains("'
+ search +
'".ToUpper()) AND '
+ customFieldName +
'.ToUpper().Contains("'
+ searchString +
'".ToUpper()))'
;
else
searchFilter =
'('
+ field +
'.ToUpper().Contains("'
+ search +
'".ToUpper()))'
;
if
(frontendLanguages && frontendLanguages.length > 1)
for
(
var
i = 0; i < frontendLanguages.length; i++)
var
localizedField = String.format(
"0[\"1\"]"
, field, frontendLanguages[i]);
searchFilter += String.format(
"OR 0.ToUpper().Contains(\"1\".ToUpper())"
, localizedField, search);
searchFilter =
'('
+ searchFilter +
')'
;
this
.filter += searchFilter;
return
this
;
,
differFilter:
function
(items, identifier)
var
itemsFilterArray = [];
if
(!items || items.length === 0)
return
this
.trimOperator();
itemsFilterArray.push(identifier +
'!="'
+ items[0] +
'"'
);
if
(items.length > 1)
for
(
var
i = 1; i < items.length; i++)
itemsFilterArray.push(
' AND '
+ identifier +
'!="'
+ items[i] +
'"'
);
this
.filter +=
'('
+ itemsFilterArray.join(
''
) +
')'
;
return
this
;
,
specificItemsFilter:
function
(ids)
var
itemsFilterArray = [];
if
(ids.length === 0)
return
this
.trimOperator();
itemsFilterArray.push(
'Id='
+ ids[0]);
if
(ids.length > 1)
for
(
var
i = 1; i < ids.length; i++)
if
(ids[i] === emptyGuid)
continue
;
itemsFilterArray.push(
' OR Id='
+ ids[i]);
this
.filter +=
'('
+ itemsFilterArray.join(
''
) +
')'
;
return
this
;
,
append:
function
(filter)
if
(filter)
this
.filter +=
'('
+ filter +
')'
;
return
this
;
,
and:
function
()
if
(
this
.filter)
this
.filter +=
this
.andOperator;
return
this
;
,
trimOperator:
function
()
if
(endsWith(
this
.filter,
this
.andOperator))
this
.filter = trimRight(
this
.filter,
this
.andOperator);
return
this
;
,
getFilter:
function
()
return
this
.filter;
;
/* Public interface */
return
filterBuilder:
function
(baseFilter)
return
new
FilterBuilder(baseFilter);
,
emptyGuid:
function
()
return
emptyGuid;
,
getResource: getResource
;
]);
module.provider(
'serverContext'
,
function
ServerContextProvider()
var
customContext = customContext || ;
var
constructContext =
function
($injector)
return
getRootedUrl: customContext.getRootedUrl || sitefinity.getRootedUrl,
getEmbeddedResourceUrl: customContext.getEmbeddedResourceUrl || sitefinity.getEmbeddedResourceUrl,
getFrontendLanguages: customContext.getFrontendLanguages || sitefinity.getFrontendLanguages,
getCurrentFrontendRootNodeId: customContext.getCurrentFrontendRootNodeId || sitefinity.getCurrentFrontendRootNodeId,
setCurrentFrontendRootNodeId: customContext.setCurrentFrontendRootNodeId || sitefinity.setCurrentFrontendRootNodeId,
getCurrentUserId: customContext.getCurrentUserId || sitefinity.getCurrentUserId,
getUICulture:
function
()
if
($injector.has(
'widgetContext'
))
return
$injector.get(
'widgetContext'
).culture;
return
customContext && customContext.uiCulture;
,
isMultisiteEnabled: customContext.isMultisiteEnabled || sitefinity.isMultisiteEnabled
;
;
/* The context should be object containing properties: 'appPath' and optionally 'currentPackage' and 'uiCulture'. */
this
.setServerContext =
function
(context)
customContext = context;
;
this
.$get = [
'$injector'
,
function
($injector)
return
constructContext($injector);
];
);
)();
Thanks very much for the detailed reply Velizar, much appreciated
Cheers,
Michael