Upgrading a template with a customized sessions and history widget
This upgrade guide documents the steps required to upgrade a template for an existing built search that used a customized session search and click history or customised session cart tools widgets.
This guide provides the steps required to update a template that includes a customized sessions and history widget. If you are using a template that includes a default sessions and history widget please see: upgrading a template with a default sessions and history widget. |
Upgrade path
The upgrade process involves two steps: replacing JavaScript libraries and updating Freemarker templates.
The specifics of each step depend on the upgrade path, however. Follow the procedure for the upgrade path being taken.
Upgrade from Funnelback 15 to Funnelback 16
Replace JavaScript libraries
Prior to Funnelback 15.24, the widget was delivered using an angularJS based session library. As of Funnelback 15.24, this was split into two independent widgets; one to handle search and click history and another to handle cart functionality.
Because of this the funnelback-session-1.0.0.js
library uses AngularJS. It has been superseded by newer libraries using Handlebars. funnelback-session-1.0.0.js
should be removed.
Regarding upgrading from Funnelback 15
|
Replace:
<script src="${GlobalResourcesPrefix}thirdparty/angular-1.0.7/angular.js"></script>
<script src="${GlobalResourcesPrefix}thirdparty/angular-1.0.7/angular-resource.js"></script>
<script src="${GlobalResourcesPrefix}js/funnelback-session-1.0.0.js"></script>
with:
<script nomodule type="text/javascript" src="${GlobalResourcesPrefix}thirdparty/es6-promise-4.2.5/es6-promise.auto.min.js"></script>
<script type="text/javascript" src="${GlobalResourcesPrefix}thirdparty/handlebars-4.7/handlebars.min.js"></script>
<script type="text/javascript" src="${GlobalResourcesPrefix}js/funnelback.session-cart-0.1.min.js"></script>
<script type="text/javascript" src="${GlobalResourcesPrefix}js/funnelback.session-history-0.1.min.js"></script>
<script type="text/javascript">
var flbSessionCart = new Funnelback.SessionCart({collection: '${question.collection.id}'});
var flbSessionHistory = new Funnelback.SessionHistory({collection: '${question.collection.id}'});
</script>
Update Freemarker templates
Remove angular styles from <head>
section
From the <style>
tag located in the <head>
section, remove the following AngularJS specific classes:
-
[ng\:cloak]
-
[ng-cloak]
-
[data-ng-cloak]
-
[x-ng-cloak]
-
.ng-cloak
-
.x-ng-cloak { display: none !important; }
Remove angular attribute data-ng-app
The new session history and cart Javascript libraries don’t require any apps defined. Remove the following AngularJS apps:
-
data-ng-app="Funnelback"
Remove angular attribute data-ng-controller
The new session history and cart Javascript libraries don’t require any controllers defined. Remove the following AngularJS controllers:
-
data-ng-controller="DefaultCtrl"
-
data-ng-controller="SearchHistoryCtrl"
-
data-ng-controller="ClickHistoryCtrl"
-
data-ng-controller="CartCtrl"
Removing the above may lead to empty HTML elements which can be removed. |
Remove angular attribute data-ng-class
The new session history and cart Javascript libraries control the HTML elements which used to be managed by the AngularJS attribute data-ng-class
. Remove the data-ng-class
attributes from HTML elements.
Remove angular attribute ng-cloak
and data-ng-cloak
The new session history and cart Javascript libraries don’t require ng-cloak
class or the data-ng-cloak
and should be removed.
Removing the above may lead to HTML elements with no classes or attributes which can be removed if they are not required by the css styling or to manage other HTML elements. |
Replace angular attribute data-ng-show
The new session history and cart Javascript libraries use classes and Javascript to show, toggle and hide HTML elements which used to be managed by the AngularJS attribute data-ng-show
. How these attributes are replaced depends on what functionality the attribute provides. These are outlined below:
-
If the attribute displays the 'zero for an empty cart' then it and the associated HTML can be removed as this is now done by the session cart Javascript. As seen in the example below. Remove:
<span data-ng-show>0</span>
. -
If the attribute indicates where to show 'search results' then remove the attribute and add the class
search-results-content
, as seen in the example below:<section class="search-results pt-3" data-ng-show="isDisplayed('results')" aria-label="Search Results">
becomes
<section class="search-results pt-3 search-results-content" aria-label="Search Results">
-
If the attribute indicates where to show the history breadcrumbs then remove the attribute and add the class
session-history-breadcrumb
, as seen in the example below:<div class="breadcrumb" data-ng-controller="SearchHistoryCtrl" data-ng-show="!searchHistoryEmpty">
becomes
<div class="breadcrumb session-history-breadcrumb">
-
If the attribute indicates where to show the list of clicks from history then remove the attribute and add the class
session-history-click-results
. As this is now done by the session history Javascript the Freemarker list element must be an ancestor of the element with classsession-history-click-results
. Use the Freemarker item element around the repeated HTML, as seen in the example below:<div data-ng-show="!clickHistoryEmpty && <@fb.HasClickHistory />"> ... <ul> <#list session.clickHistory as h> <li> ... </li> </#list> </ul> </div>
becomes
<#list session.clickHistory> <div class="session-history-click-results"> ... <ul> <#items as h> <li> ... </li> </#items> </ul> </div> </#list>
-
If the attribute indicates where to show information when there is no click history then remove the attribute and add the class
session-history-click-empty
, as seen in the example below:<div class="card" data-ng-show="clickHistoryEmpty || !<@fb.HasClickHistory />"> ... </div>
becomes
<div class="card session-history-click-empty"> ... </div>
-
If the attribute indicates where to show the list of searches from history then remove the attribute and add the class
session-history-search-results
. As this is now done by the session history Javascript. Also rearrange the Freemarker list element must be an ancestor of the element with classsession-history-search-results
. Use the Freemarker item element around the repeated HTML, as seen in the example below:<div data-ng-show="!searchHistoryEmpty && <@fb.HasSearchHistory />"> ... <ul> <#list session.searchHistory as h> <li> ... </li> </#list> </ul> </div>
becomes
<#list session.searchHistory> <div class="session-history-search-results"> ... <ul> <#items as h> <li> ... </li> </#items> </ul> </div> </#list>
-
If the attribute indicates where to show information when there is no search history then remove the attribute add the class
session-history-search-empty
, as seen in the example below:<div class="card" data-ng-show="searchHistoryEmpty || !<@fb.HasSearchHistory />"> ... </div>
becomes
<div class="card session-history-search-empty"> ... </div>
-
If the attribute determines that it should only show information when the data model element is not empty then remove the attribute and wrap the element in a Handlebars if statement, as seen in the example below:
<img data-ng-show="item.metaData.I" class="img-fluid float-right ng-cloak" alt="Thumbnail for {{result.title}}" data-ng-src="{{item.listMetadata.I[0]}}">
becomes
{{#if metaData.I}} <img class="img-fluid float-right" alt="Thumbnail for {{result.title}}" src="{{#byIndex 0 "|"}}{{metaData.I}}{{/byIndex}}"/> {{/if}}
Replace angular attributes data-ng-switch
and data-ng-switch-when
.
The new session history and cart Javascript libraries use Handlebars in the template section and therefore the attributes data-ng-switch
and data-ng-switch-when
are removed and replaced with Handlebars notation. To achieve the same result as the AngularJS a Handlebars helper is added using the Javascript below. The Javascript goes in the script element after the configuration of Funnelback.SessionCart
, as seen in the example below:
<script type="text/Javascript">
var flbSessionCart = new Funnelback.SessionCart({
...
});
flbSessionCart.Handlebars.registerHelper({
// usage: {{#if (eq collection "test")}}
eq: function (v1, v2) {
return v1 === v2;
}
});
</script>
As well as adding the above Javascript the details data-ng-switch
and data-ng-switch-when
attributes are removed and used to create the handle bars notation element using data-ng-switch=<value1>
and data-ng-switch-when=<value2>
and adding {{ #if (eq <value1> <value2>) }}
, as seen in the example below:
<div data-ng-switch="item.collection">
<#-- Output templates for all results depending on their source collection, per configured in collection.cfg -->
<#list question.collection.configuration.valueKeys() as key>
<#if key?starts_with("stencils.template.shortlist.")>
<#assign itemCollection = key?substring("stencils.template.shortlist."?length)>
<#assign itemNamespace = question.collection.configuration.value(key)>
<#if .main[itemNamespace]??>
<div data-ng-switch-when="${itemCollection}">
<@.main[itemNamespace].ShortListTemplate />
</div>
</#if>
</#if>
</#list>
...
</div>
becomes
{{#if collection}}
<#list question.collection.configuration.valueKeys() as key>
<#if key?starts_with("stencils.template.shortlist.")>
<#assign itemCollection = key?substring("stencils.template.shortlist."?length)>
<#assign itemNamespace = question.collection.configuration.value(key)>
<#if .main[itemNamespace]??>
{{#if (eq collection "${itemCollection}")}}
<@.main[itemNamespace].ShortListTemplate />
{{/if}}
</#if>
</#if>
</#list>
{{/if}}
Replace angular attribute data-ng-repeat
:
The cart Javascript will add the ul
element as well as a li
element for each item to display so any classes for these elements will need to be added using the Javascript below. The Javascript goes in the script element after the configuration of Funnelback.SessionCart
:
<script type="text/Javascript">
var flbSessionCart = new Funnelback.SessionCart({
...
});
function styleItems() {
var boxItems = document.getElementsByClassName('flb-cart-box-item');
for (var i = 0; i < boxItems.length; i++) {
boxItems.item(i).classList.add('mb-3');
}
}
styleItems();
</script>
As well as adding the above Javascript the details of the HTML element which has the data-ng-repeat
attribute and it’s children are used to configure the 'item' section of the Funnelback.SessionCart
which creates the individual cart result item HTML elements. The child HTML elements of the li
will form the template input for Funnelback.SessionCart
item object. Since the template has to be a single line the Freemarker <@compress single_line=true>
can be used if HTML formatting is retained. If display is defined in a macro object then it can be separated to a new macro which the template
JSON object can point to the new macro. Alternatively the HTML can be added to template
JSON object directly. The example below uses a new macro and assigns this to the template.
<#macro Cart>
<#if question.collection.configuration.valueAsBoolean("ui.modern.session")>
<section id="search-cart" class="search-cart pt-3" data-ng-cloak data-ng-show="isDisplayed('cart')" data-ng-controller="CartCtrl" aria-label="Search shortlist">
<div class="container">
<div class="row">
<div class="col-md-12">
...
<div class="row search-results mt-3">
<div class="col-md-12">
<ul class="list-unstyled">
<li data-ng-repeat="item in cart" class="mb-3">
<div data-ng-switch="item.collection">
<#-- Output templates for all results depending on their source collection, per configured in collection.cfg -->
<#list question.collection.configuration.valueKeys() as key>
<#if key?starts_with("stencils.template.shortlist.")>
<#assign itemCollection = key?substring("stencils.template.shortlist."?length)>
<#assign itemNamespace = question.collection.configuration.value(key)>
<#if .main[itemNamespace]??>
<div data-ng-switch-when="${itemCollection}">
<@.main[itemNamespace].ShortListTemplate />
</div>
</#if>
</#if>
</#list>
...
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
</#if>
</#macro>
becomes
<#macro Cart>
<#if question.collection.configuration.valueAsBoolean("ui.modern.session")>
<section class="search-cart" aria-label="Search shortlist">
<div class="container">
<div class="row search-results mt-3">
<div id="search-cart" class="col-md-12 pt-3"></div>
</div>
</div>
</section>
</#if>
</#macro>
<#macro CartItem>
<@compress single_line=true>
{{#if collection}}
<#list question.collection.configuration.valueKeys() as key>
<#if key?starts_with("stencils.template.shortlist.")>
<#assign itemCollection = key?substring("stencils.template.shortlist."?length)>
<#assign itemNamespace = question.collection.configuration.value(key)>
<#if .main[itemNamespace]??>
{{#if (eq collection "${itemCollection}")}}
<@.main[itemNamespace].ShortListTemplate />
{{/if}}
</#if>
</#if>
</#list>
{{/if}}
</@compress>
</#macro>
with
var flbSessionCart = new Funnelback.SessionCart({
...
item: {
selector: '#search-results',
template: '<@history_cart.CartItem />',
}
});
Replace angular attribute data-ng-click
:
The new session history and cart Javascript libraries use classes and Javascript to show, toggle and hide HTML elements which used to be managed by the AngularJS attribute data-ng-click
. How these attributes are replaced depends on what functionality the attribute provides. These are outlined below
-
If the attribute toggles the session history then remove the attribute and add the class
session-history-toggle
, as seen in the example below:<a data-ng-class="{active: isDisplayed('history')}" data-ng-click="toggleHistory()" href="#" aria-label="Search History"> <span class="fal fa-history"></span> History </a>
becomes
<a href="#" class="session-history-toggle" aria-label="Search History"> <span class="fal fa-history"></span> History </a>
-
If the attribute displays the session history then remove the attribute and add the class
session-history-show
. If you want the link hidden if the history is empty you can add the classsession-history-link
, as seen in the example below<a title="Click history" href="#" class="${class}" data-ng-click="toggleHistory()"> Last visited ${prettyTime(session.getClickHistory(result.indexUrl).clickDate)} </a>
becomes
<a title="Click history" href="#" class="${class} session-history-show session-history-link"> Last visited ${prettyTime(session.getClickHistory(result.indexUrl).clickDate)} </a>
-
If the attribute hides the session history then remove the attribute and add the class
session-history-hide
, as seen in the example below:<a href="#" data-ng-click="hideHistory()"><span class="fal fa-arrow-left"></span> Back to results</a>
becomes
<a href="#" class="session-history-hide"><span class="fal fa-arrow-left"></span> Back to results</a>
-
If the attribute clears the session click history then remove the attribute and add the class
session-history-clear-click
, as seen in the example below:<button class="btn btn-danger btn-sm float-right" title="Clear click history" data-ng-click="clear('Your history will be cleared')"> <span class="fal fa-times"></span> Clear </button>
becomes
<button class="btn btn-danger btn-sm float-right session-history-clear-click" title="Clear click history"> <span class="fal fa-times"></span> Clear </button>
-
If the attribute clears the session search history then remove the attribute and add the class
session-history-clear-search
, as seen in the example below:<button class="btn btn-danger btn-sm float-right" title="Clear click history" data-ng-click="clear('Your history will be cleared')"> <span class="fal fa-times"></span> Clear </button>
becomes
<button class="btn btn-danger btn-sm float-right session-history-clear-click" title="Clear click history"> <span class="fal fa-times"></span> Clear </button>
-
If the attribute displays the cart count then any classes will need to be added using the Javascript in the example below. The Javascript goes in the script element after the configuration of
Funnelback.SessionCart
.<script type="text/Javascript"> var flbSessionCart = new Funnelback.SessionCart({ ... }); function styleCartCountItems() { var cartCountItems = document.getElementsByClassName('.flb-cart-count'); for (var i = 0; i < cartCountItems.length; i++) { cartCountItems.item(i).classList.add(<comma seperated classes>); } } styleCartCountItems(); </script>
As well as adding the above Javascript the details of the HTML element which has the
data-ng-click
attribute and it’s children are used to configure thecartCount
section of theFunnelback.SessionCart
which creates the cart count HTML elements. To ensure the Javascript works add the classflb-cart-count
to the parent of the HTML element which has thedata-ng-click
attribute. Icons and labels are moved to thecartCount
section as seen below.<small> <a data-ng-class="{active: isDisplayed('cart'), disabled: cart.length < 1}" data-ng-click="toggleCart()" title="{{cart.length}} item(s) in your shortlist" href="#" > <span class="fal fa-star"></span> Shortlist (<span class="ng-cloak">{{cart.length}}</span>) </a> </small>
becomes
<small class="flb-cart-count" ></small>
with
var flbSessionCart = new Funnelback.SessionCart({ ... cartCount: { selector: '.flb-cart-count', icon: 'fal fa-star', isLabel: true, label: 'Shortlist', template: '{{>icon-block}} {{>label-block}} (<span>{{count}}</span>)' } });
Additional HTML elements may need to be added to the template depending on the original HTML. -
If the attribute is used to add and remove search result items from the cart then any classes will need to be added using the Javascript below. The Javascript also includes adding an event listener to the
click
event of the element which displays the cart results screen. The Javascript goes in the script element after the configuration ofFunnelback.SessionCart
.<script type="text/Javascript"> var flbSessionCart = new Funnelback.SessionCart({ ... }); function styleCartClickItems() { var triggerItems = document.getElementsByClassName('.flb-cart-item-trigger'); for (var i = 0; i < triggerItems.length; i++) { triggerItems.item(i).classList.add(<comma seperated classes>); } } styleCartClickItems(); var countTriggers = document.getElementsByClassName('flb-cart-count-trigger'); for (var i = 0; i < countTriggers.length; i++) { countTriggers.item(i).addEventListener("click", styleCartCountItems); } </script>
As well as adding the above Javascript the details of the HTML element which has the
data-ng-click
attribute and it’s children are used to configure theitemTrigger
section of theFunnelback.SessionCart
which manages items using the cart Javascript. To ensure the Javascript works add a class from the parent of the html element which has thedata-ng-click
attribute to theitemTrigger
selector. Icons and labels are moved to theitemTrigger
section as seen below.<div class="card-header"> <#if question.collection.configuration.valueAsBoolean("ui.modern.session")> <a href="Javascript:;" class="btn btn-light border border-secondary float-right ng-cloak shortlist-button" data-ng-click="toggle()" data-cart-link data-css="far fa-star|fas fa-star" data-labels="Add to shortlist|Remove" title="{{label}}"> <span class="{{css}}"></span> <span class="ng-cloak">{{label}}</span> </a> </#if> ... </div>
becomes
<div class="card-header"> ... </div>
with
var flbSessionCart = new Funnelback.SessionCart({ ... itemTrigger: { // Set location of item trigger to add to / delete from cart selector: '.card-header', position: 'afterbegin', // Set display of item trigger to add to / delete from cart iconAdd: 'far fa-star', iconDelete: 'fas fa-star', isLabel: true, labelAdd: 'Add to shortlist', labelDelete: 'Remove', template: '{{>icon-block}} {{>label-block}}' } });
The
position
is set tobeforebegin
,afterbegin
,beforeend
,afterend
relative position toselector
element as follows:-
beforebegin
: HTML is added before theselector
element -
afterbegin
: HTML is added before the first child inside theselector
element -
beforeend
: HTML is added after the last child inside theselector
element -
afterend
: HTML is added after theselector
element itself
This process may result in empty HTML elements which can be removed
-
-
If the attribute changes the display from the cart results to the search results then any classes on this element and child elements will need to be added using the Javascript below. The Javascript goes in the script element after the configuration of
Funnelback.SessionCart
.<script type="text/Javascript"> var flbSessionCart = new Funnelback.SessionCart({ ... }); var boxHeaders = document.getElementsByClassName('flb-cart-box-header'); for (var i = 0; i < boxHeaders.length; i++) { boxHeaders.item(i).classList.add('text-center'); } </script>
As well as adding the above Javascript the details of the HTML element which has the
data-ng-click
attribute and it’s children are used to configure thecart
section of theFunnelback.SessionCart
which displays the header for the cart results page. To ensure the Javascript works add the idsearch-cart
to the parent of the html element which has thedata-ng-click
attribute. Icons and labels are moved to thecart
section as seen below.<div class="col-md-12"> <a href="#" data-ng-click="hideCart()"><span class="fal fa-arrow-left"></span> Back to results</a> <h2 class="text-center"> <span class="fal fa-star"></span> Shortlist <button class="btn btn-danger btn-sm" title="Clear selection" data-ng-click="clear('Your selection will be cleared')"><span class="fal fa-times"></span> Clear</button> </h2> ... </div>
becomes
<div id="search-cart" class="col-md-12"></div>
with
var flbSessionCart = new Funnelback.SessionCart({ ... cart: { selector: '#search-cart', pageSelector: ['#search-results-content', '#search-history'], icon: 'fal fa-star', label: 'Shortlist', backIcon: 'fal fa-arrow-left', backLabel: ' Back to results', clearClasses: 'btn btn-danger btn-sm', clearIcon: 'fal fa-times', clearLabel: 'Clear' } });
The HTML used to display the individual items needs to be moved as per instructions in the data-ng-repeat
section for showing cart results -
If the attribute is used on the cart results page to remove items from the cart then any classes will need to be added using the Javascript below. The Javascript goes in the
script
element after the configuration ofFunnelback.SessionCart
.<script type="text/Javascript"> var flbSessionCart = new Funnelback.SessionCart({ ... }); function styleItems() { var triggerItems = document.getElementsByClassName('flb-cart-item-trigger'); for (var i = 0; i < triggerItems.length; i++) { triggerItems.item(i).classList.add(<comma seperated copied classes); } } styleItems(); </script>
As well as adding the above Javascript the details of the HTML element which has the
data-ng-click
attribute and it’s children are used to configure thecartItemTrigger
section of theFunnelback.SessionCart
which manages items using the cart Javascript. To ensure the Javascript works add a class from the parent of the html element which has thedata-ng-click
attribute to thecartItemTrigger
selector. Icons and labels are moved to thecartItemTrigger
section as seen below.<div class="card-header"> ... <a href="Javascript:;" class="btn btn-light border border-secondary float-right ng-cloak shortlist-button" data-ng-click="remove(item.indexUrl)"> <i class="fal fa-times"></i> <span class="ng-cloak">Remove</span> </a> </div>
becomes
<div class="card-header"> ... </div>
with
var flbSessionCart = new Funnelback.SessionCart({ ... cartItemTrigger: { selector: '.card-header, position: 'beforeend', // Set display of item trigger to add to / delete from cart iconAdd: '', // Never seen iconDelete: 'fal fa-times', isLabel: true, labelAdd: '', // Never seen labelDelete: 'Remove', template: '{{>icon-block}} {{>label-block}}' } });
The
position
is set tobeforebegin
,afterbegin
,beforeend
,afterend
relative position toselector
element as follows-
beforebegin
: HTML is added before theselector
element -
afterbegin
: HTML is added before the first child inside theselector
element -
beforeend
: HTML is added after the last child inside theselector
element -
afterend
: HTML is added after theselector
element itself
This process may result in empty HTML elements which can be removed.
-
Replace angular attribute data-ng-disabled
We can no longer use the angular notation data-ng-disabled
. These attributes should be removed.
Replace angular attribute data-ng-href
We can no longer use the angular notation data-ng-href
. These attributes should be replaced with standard HTML href
attributes, as seen in the example below:
<a data-ng-href="{{item.indexUrl}}">{{item.title}}</a>
becomes
<a href="{{indexUrl}}">{{title}}</a>
Replace angular attribute data-ng-src
We can no longer use the angular notation data-ng-src
. These attributes should be replaced with standard HTML src
attributes, as seen in the example below.
<img class="img-fluid float-left ng-cloak" alt="Thumbnail for {{item.title}}" data-ng-src="{{item.listMetadata.image[0]}}">
becomes
<img class="img-fluid float-left" alt="Thumbnail for {{title}}" src="{{#byIndex 0 "|"}}{{metaData.image}}{{/byIndex}}">
Replace icon prefix
The cart Javascript allows a prefix to be set for icons to simply development. If your icons don’t have a common prefix this can be set to nothing by using empty single quotes (''
). As seen in the example below.
var flbSessionCart = new Funnelback.SessionCart({
collection: <value>,
iconPrefix: ''
}
Remove the item.
Prefix
The cart Javascript will break when the item.
prefix is used on data model items. For userId
, collection
, indexURL
, title
, summary
and addedDate
the item
prefix can be removed. When displaying metadata items you may need an item from a specific index. This can be achieved by adding the Javascript shown below. The Javascript goes in the script
element after the configuration of Funnelback.SessionCart
.
<script type="text/Javascript">
var flbSessionCart = new Funnelback.SessionCart({
...
});
flbSessionCart.Handlebars.registerHelper({
// usage: {{#byIndex "0" "|"}}{{image}}{{/byIndex}}
byIndex: function (index, delimiter, options) {
const str = options.fn(this);
const arr = str.split(delimiter);
if (index < arr.length) return arr[index];
return "";
}
});
</script>
As well as adding the above Javascript the HTML element which has the item.
prefix is modified to use the byIndex
helper. i.e. {{#byIndex "0" "|"}}{{metaData.<name>}}{{/byIndex}}
, as shown in the example below:
<a class="text-muted" href="mailto:{{item.listMetadata.peopleEmail[0]}}">{{item.listMetadata.peopleEmail[0]}}</a>
becomes
<a class="text-muted" href="mailto:{{#byIndex 0 "|"}}{{metaData.peopleEmail}}{{/byIndex}}">{{#byIndex 0 "|"}}{{metaData.peopleEmail}}{{/byIndex}}</a>
Replace the AngularJS |
notation
The cart Javascript templating uses Handlebars notation and will break if the used to manipulate data source items and need to be replaced as per the following:
-
When used to truncate an object remove
|truncate:<value>
and wrap with{{#truncate <value>}}{{<data item>}}{{/truncate}}
, as shown in the example below:{{item.summary|truncate:255}}
becomes
{{#truncate 255}}{{summary}}{{/truncate}}
-
When used to truncate an object remove
|cut:<value>
and wrap with{{#cut <value>}}{{<data item>}}{{/cut}}
, as shown in the example below:{{item.indexUrl|cut:'https://'}}
becomes
{{#cut "https://"}}{{indexUrl}}{{/cut}}
-
When used to replace characters in the data model item
if
would depend on the regex. For example,|replace:'\\|.*$'
is the same as getting everything before the first pipe and therefore can be replaced by the Handlebars helper 'byIndex' as shown in the example below:<h5>{{item.listMetadata.peoplePosition[0]|replace:'\\|.*$'}}</h5>
becomes
<h5>{{#byIndex 0 "|"}}{{metaData.peoplePosition}}{{/byIndex}}</h5>
Replace the AngularJS ||
notation
The cart Javascript templating uses Handlebars notation and will break if used to display a data model or default value or a default value. It needs to be replaced, as shown in the example below:
<span>{{item.listMetadata.stencilsCourseCredit[0] || '-'}}</span>
becomes
<span>
{{#if metaData.stencilsCourseCredit}}
{{#byIndex 0 "|"}}{{metaData.stencilsCourseCredit}}{{/byIndex}}
{{else}}
-
{{/if}}
</span>
Upgrade from Funnelback 15 to Funnelback DXP
Replace JavaScript libraries
Prior to Funnelback 15.24, the widget was delivered using an angularJS based session library. As of Funnelback 15.24, this was split into two independent widgets; one to handle search and click history and another to handle cart functionality.
Because of this the funnelback-session-1.0.0.js
library uses AngularJS. It has been superseded by newer libraries using Handlebars. funnelback-session-1.0.0.js
should be removed.
Regarding upgrading from Funnelback 15
|
Replace JavaScript Libraries
Replace:
<script src="${GlobalResourcesPrefix}thirdparty/angular-1.0.7/angular.js"></script>
<script src="${GlobalResourcesPrefix}thirdparty/angular-1.0.7/angular-resource.js"></script>
<script src="${GlobalResourcesPrefix}js/funnelback-session-1.0.0.js"></script>
with:
<script nomodule type="text/javascript" src="${GlobalResourcesPrefix}thirdparty/es6-promise-4.2.5/es6-promise.auto.min.js"></script>
<script type="text/javascript" src="${GlobalResourcesPrefix}thirdparty/handlebars-4.7/handlebars.min.js"></script>
<script type="text/javascript" src="${GlobalResourcesPrefix}js/funnelback.session-cart-1.0.min.js"></script>
<script type="text/javascript" src="${GlobalResourcesPrefix}js/funnelback.session-history-1.0.min.js"></script>
<script type="text/javascript">
var flbSessionCart = new Funnelback.SessionCart({collection: '${question.collection.id}'});
var flbSessionHistory = new Funnelback.SessionHistory({collection: '${question.collection.id}'});
</script>
Update Freemarker templates
Make the following changes to simple.ftl
.
-
Replace the recent Session history block with a
<div id="search-history-recent">
div.That is replace
<#-- Build list of previous queries --> <#assign qsSignature = computeQueryStringSignature(QueryString) /> <#if session.searchHistory?? && (session.searchHistory?size gt 1 || session.searchHistory[0].searchParamsSignature != qsSignature)> <div class="breadcrumb session-history-breadcrumb"> <button class="btn btn-link pull-right session-history-show"><small class="text-muted"><span class="glyphicon glyphicon-plus"></span> More…</small></button> <ol class="list-inline" > <li class="text-muted">Recent:</li> <#list session.searchHistory as h> <#if h.searchParamsSignature != qsSignature> <#outputformat "plainText"> <#assign facetDescription><#compress> <#list h.searchParams?matches("f\\.([^=]+)=([^&]+)") as f> ${urlDecode(f?groups[1])?split("|")[0]} = ${urlDecode(f?groups[2])}<#if f_has_next><br></#if> </#list> </#compress></#assign> </#outputformat> <li> <a <#if facetDescription != ""> data-toggle="tooltip" data-placement="bottom" title="${facetDescription}"</#if> title="${prettyTime(h.searchDate)}" href="${question.currentProfileConfig.get("ui.modern.search_link")}?${h.searchParams}">${h.originalQuery!} <small>(${h.totalMatching})</small></a> <#if facetDescription != ""><i class="glyphicon glyphicon-filter"></i></a></#if> </li> </#if> </#list> </ol> </div> </#if>
with
<div id="search-history-recent"></div>
-
Replace the Session history block with a
<div id="search-history">
div.That is replace
<div id="search-history"> <div class="row"> <div class="col-md-12"> <a href="#" class="session-history-hide"><span class="glyphicon glyphicon-arrow-left"></span> Back to results</a> <h2><span class="glyphicon glyphicon-time"></span> History</h2> <div class="row"> <div class="col-md-6"> <h3> <span class="glyphicon glyphicon-heart"></span> Recently clicked results <button class="btn btn-danger btn-xs session-history-clear-click" title="Clear click history"><span class="glyphicon glyphicon-remove"></span> Clear</button> </h3> <#list session.clickHistory> <ul class="session-history-click-results"> <#items as h> <li><a href="${h.indexUrl}">${h.title}</a> · <span class="text-warning">${prettyTime(h.clickDate)}</span><#if h.query??><span class="text-muted"> for "${h.query!}"</#if></span></li> </#items> </ul> </#list> <p class="session-history-click-empty text-muted">Your click history is empty.</p> </div> <div class="col-md-6"> <h3> <span class="glyphicon glyphicon-search"></span> Recent searches <button class="btn btn-danger btn-xs session-history-clear-search" title="Clear search history"><span class="glyphicon glyphicon-remove"></span> Clear</button> </h3> <#list session.searchHistory> <ul class="session-history-search-results list-unstyled"> <#items as h> <li><a href="?${h.searchParams}">${h.originalQuery!} <small>(${h.totalMatching})</small></a> · <span class="text-warning">${prettyTime(h.searchDate)}</span></li> </#items> </ul> </#list> <p class="session-history-search-empty text-muted">Your search history is empty.</p> </div> </div> </div> </div> </div>
with
<div id="search-history"></div>
-
Finally, in the Last visited time display block, remove this line:
<#if question.currentProfileConfig.get("ui.modern.session")?boolean && session?? && session.getClickHistory(s.result.indexUrl)??><small class="text-warning session-history-link"><span class="glyphicon glyphicon-time"></span> <a title="Click history" href="#" class="text-warning session-history-show">Last visited ${prettyTime(session.getClickHistory(s.result.indexUrl).clickDate)}</a></small></#if>
Replace JavaScript Libraries
Replace:
<script nomodule type="text/javascript" src="${GlobalResourcesPrefix}thirdparty/es6-promise-4.2.5/es6-promise.auto.min.js"></script>
<script type="text/javascript" src="${GlobalResourcesPrefix}thirdparty/handlebars-4.7/handlebars.min.js"></script>
<script type="text/javascript" src="${GlobalResourcesPrefix}js/funnelback.session-cart-0.1.min.js"></script>
<script type="text/javascript" src="${GlobalResourcesPrefix}js/funnelback.session-history-0.1.min.js"></script>
<script type="text/javascript">
var flbSessionCart = new Funnelback.SessionCart({collection: '${question.collection.id}'});
var flbSessionHistory = new Funnelback.SessionHistory({collection: '${question.collection.id}'});
</script>
with:
<script nomodule type="text/javascript" src="${GlobalResourcesPrefix}thirdparty/es6-promise-4.2.5/es6-promise.auto.min.js"></script>
<script type="text/javascript" src="${GlobalResourcesPrefix}thirdparty/handlebars-4.7/handlebars.min.js"></script>
<script type="text/javascript" src="${GlobalResourcesPrefix}js/funnelback.session-cart-1.0.min.js"></script>
<script type="text/javascript" src="${GlobalResourcesPrefix}js/funnelback.session-history-1.0.min.js"></script>
<script type="text/javascript">
var flbSessionCart = new Funnelback.SessionCart({collection: '${question.collection.id}'});
var flbSessionHistory = new Funnelback.SessionHistory({collection: '${question.collection.id}'});
</script>
Update Freemarker templates
Make the following changes to simple.ftl
.
-
Replace the recent Session history block with a
<div id="search-history-recent">
div.That is replace
<#-- Build list of previous queries --> <#assign qsSignature = computeQueryStringSignature(QueryString) /> <#if session.searchHistory?? && (session.searchHistory?size gt 1 || session.searchHistory[0].searchParamsSignature != qsSignature)> <div class="breadcrumb session-history-breadcrumb"> <button class="btn btn-link pull-right session-history-show"><small class="text-muted"><span class="glyphicon glyphicon-plus"></span> More…</small></button> <ol class="list-inline" > <li class="text-muted">Recent:</li> <#list session.searchHistory as h> <#if h.searchParamsSignature != qsSignature> <#outputformat "plainText"> <#assign facetDescription><#compress> <#list h.searchParams?matches("f\\.([^=]+)=([^&]+)") as f> ${urlDecode(f?groups[1])?split("|")[0]} = ${urlDecode(f?groups[2])}<#if f_has_next><br></#if> </#list> </#compress></#assign> </#outputformat> <li> <a <#if facetDescription != ""> data-toggle="tooltip" data-placement="bottom" title="${facetDescription}"</#if> title="${prettyTime(h.searchDate)}" href="${question.currentProfileConfig.get("ui.modern.search_link")}?${h.searchParams}">${h.originalQuery!} <small>(${h.totalMatching})</small></a> <#if facetDescription != ""><i class="glyphicon glyphicon-filter"></i></a></#if> </li> </#if> </#list> </ol> </div> </#if>
with
<div id="search-history-recent"></div>
-
Replace the Session history block with a
<div id="search-history">
div.That is replace
<div id="search-history"> <div class="row"> <div class="col-md-12"> <a href="#" class="session-history-hide"><span class="glyphicon glyphicon-arrow-left"></span> Back to results</a> <h2><span class="glyphicon glyphicon-time"></span> History</h2> <div class="row"> <div class="col-md-6"> <h3> <span class="glyphicon glyphicon-heart"></span> Recently clicked results <button class="btn btn-danger btn-xs session-history-clear-click" title="Clear click history"><span class="glyphicon glyphicon-remove"></span> Clear</button> </h3> <#list session.clickHistory> <ul class="session-history-click-results"> <#items as h> <li><a href="${h.indexUrl}">${h.title}</a> · <span class="text-warning">${prettyTime(h.clickDate)}</span><#if h.query??><span class="text-muted"> for "${h.query!}"</#if></span></li> </#items> </ul> </#list> <p class="session-history-click-empty text-muted">Your click history is empty.</p> </div> <div class="col-md-6"> <h3> <span class="glyphicon glyphicon-search"></span> Recent searches <button class="btn btn-danger btn-xs session-history-clear-search" title="Clear search history"><span class="glyphicon glyphicon-remove"></span> Clear</button> </h3> <#list session.searchHistory> <ul class="session-history-search-results list-unstyled"> <#items as h> <li><a href="?${h.searchParams}">${h.originalQuery!} <small>(${h.totalMatching})</small></a> · <span class="text-warning">${prettyTime(h.searchDate)}</span></li> </#items> </ul> </#list> <p class="session-history-search-empty text-muted">Your search history is empty.</p> </div> </div> </div> </div> </div>
with
<div id="search-history"></div>
-
Finally, in the Last visited time display block, remove this line:
<#if question.currentProfileConfig.get("ui.modern.session")?boolean && session?? && session.getClickHistory(s.result.indexUrl)??><small class="text-warning session-history-link"><span class="glyphicon glyphicon-time"></span> <a title="Click history" href="#" class="text-warning session-history-show">Last visited ${prettyTime(session.getClickHistory(s.result.indexUrl).clickDate)}</a></small></#if>