A collection of code snips using Lucidworks App Studio – a modular code framework for developing bespoke, data-anywhere search and discovery web applications.

Contents

  • Angular Statements
  • ng-class
  • ng-if
  • Comments Form
  • Add icon to POST button
  • Change text of POST button
  • Facets / Filters
  • Facet naming
  • Images
  • Images missing from app deployed to server
  • Modal Dialog
  • Two-color dialog header
  • Modal timeout
  • Search Result
  • Currency fields

Angular Statements

ng-class

Add a class called “displayresult” to a tag if the search result’s foo field is not empty.

<search:result ng-class="{'displayresult': result.fields.foo.val[0] !== undefined}">
  <div> ... stuff ... </div>
</search:result>

Add a class called “clear-border” to layout block if a “more” button click condition in the controller has been met.

<layout:block ng-class="{'clear-border': moreon}">
  <div> ... stuff ... </div>
</layout:block>

ng-if

Show image if the result’s hyperlink field is not empty.

<img ng-if="(result.fields.hyperlink.val[0] !== undefined)">
  ng-src="{{result|field:'image'|actual}}?v={{$root.version}}"
  title="{{result|field:'title'|display}}" />

Show image if search result’s image field is not empty and environment field contains the string “Tree”

<span ng-if="(result.fields.image.val[0] === undefined && result.fields.environment.val[0].indexOf('Tree') > -1)">
  <img class="static" ng-src="{{$root.contextPath}}assets/icons/tree.png?v={{$root.version}}"/>
</span>

Show block if a whole bunch of conditions are met

<layout:block ng-if="(result.fields.image.val[0] !== undefined && result.fields.tab.val[0].indexOf('Apps') < 0
  || (result.fields.tab.val[0].indexOf('Reports') > -1 && result.fields.environment.val[0].indexOf('Hyperspace') > -1)
  || (result.fields.tab.val[0].indexOf('Reports') > -1 && result.fields.environment.val[0].indexOf('InfoView') > -1)
  || (result.fields.tab.val[0].indexOf('Reports') > -1 && result.fields.environment.val[0].indexOf('Insights') > -1)
  || (result.fields.tab.val[0].indexOf('Reports') > -1 && result.fields.environment.val[0].indexOf('Logic') > -1)
  || (result.fields.tab.val[0].indexOf('Reports') > -1 && result.fields.environment.val[0].indexOf('Vantage') > -1)
  ) 
  && !((result.fields.tab.val[0].indexOf('Reports') > -1 && result.fields.environment.val[0].indexOf('Tableau') > -1))
  && result.fields._lw_data_source_s.val[0].indexOf('Galleries') === -1"
  >
  <div> ... stuff ... </div>
</layout:block>

Comments Form

Add icon to POST button

Add a styling or class to the <social:comment-form> tag that refers to the form like myCommentForm or myButton.

<social:comment-form 
  target="result.id"
  styling="myCommentForm" 
  class="myButton"
></social:comment-form>

Then in CSS, specify the button’s background, width and height as needed.

.tk-stl-myCommentForm {
    .tk-stl-comment-form {
        margin: 0;
        .tk-stl-button {
                background: lighten(#1571bc, 10%);
                margin: 0.427rem 0 0 0;
                padding: 0 0.8em;
                height: 1.757rem;
                font-weight: bold;
                text-indent: 1.5rem;    
                line-height: 2.15;
                width: 4.5rem;
        }
        .tk-stl-button::after {
                content: url(../../assets/search/icon_post_14.svg);
                text-indent: -0.2rem;
                display: block;
                margin-top: -1.4rem;
                width: 1.125rem;
        }
    }
}

Change text of POST button

Add a class to the <social:comment-form> tag that collapses the original text line and adds the new text.

.myButton {
  .tk-stl-button.tk-stl-button-small {
    text-indent: -9999px;
    line-height: 0;
  }
  .tk-stl-button.tk-stl-button-small::after {
    content: "New button text";
    text-indent: 0;
    display: block;
    line-height: initial;
  }
}

Facets / Filters

Facet naming

Option 1 – add the facet name aliases in the markup through the facet-name parameter in a <search:facet> tag.

<search:facet-list 
    platform="platform" 
    query="query" 
    response="response" 
    styling="facet-list facet-list-wrappedheader"
    show-empty="false"
    >
    <search:facet
        facet-name="topics_hier=Topics" 
        show="5" 
        show-more="15"
        >
        <facet:hierarchical 
            facet-name="topics_hier" 
            platform="p_hierarchical" 
            query="query"
            select="multiselect"
            >                                                
        </facet:hierarchical>
    </search:facet>
    <search:facet 
        facet-name="environment=Environment"
        collapsible="true" 
        max-characters="40"
        select="multiselect"
        show="5" 
        show-more="15"
        ></search:facet>
</search:facet-list>

Option 2 – add the facet name aliases in the markup through the facet-name parameter in a <search:facet-list> tag.

<search:facet-list 
    platform="platform" 
    query="query" 
    response="response" 
    styling="facet-list facet-list-wrappedheader"
    show-empty="false"
    facet-name="environment=Environment, product_type=Product, entity=Business Entity">
   <search:facet  
        collapsible="true" 
        max-characters="40"
        select="multiselect"
        show="5" 
        show-more="15"
        ></search:facet>
</search:facet-list>

Option 3: add facet name aliases by injecting the TranslationService library into a controller along with a line for each facet field you want to give an alias to. Here’s an example using the high-level controller and function in /scripts/controllers/main.js:

angular.module('twigkitLightApp')
        .controller('MainCtrl', ['$rootScope', '$scope', '$stateParams', '$location', 'ModalService', '$twigkit', '$window', '$timeout', 'TranslationService',
            function ($rootScope, $scope, $stateParams, $location, ModalService, $twigkit, $window, $timeout, TranslationService) {
                // Transform Facet Name Aliases
                TranslationService.put('topics_hier', 'Topics');
                TranslationService.put('training_audience', 'Training Audience');
}]);

Continue to use the original field name in the markup and the translation will appear when the page is rendered, as the facet names no longer require the ad-hoc aliasing in the markup.

<search:facet-list 
    platform="platform" 
    query="query" 
    response="response" 
    styling="facet-list facet-list-wrappedheader"
    show-empty="false"
    >
    
    <search:facet 
        facet-name="topics_hier">
        <facet:hierarchical 
            facet-name="topics_hier" 
            platform="p_hierarchical" 
            query="query"
            select="multiselect"
        ></facet:hierarchical>
    </search:facet>
    <search:facet  
        facet-name="environment"
        collapsible="true" 
        max-characters="40"
        select="multiselect"
        show="5" 
        show-more="99"
        ></search:facet>
</search:facet-list>

Images

Images missing from app deployed to server

Sometimes image code and tags don’t work when an app is deployed to the company web server, even though everything works fine when viewed in the local desktop development environment running Jetty. Here are some of the basics to check when this happens. In main.js, set the context path to /. This will interpret to https://servername:8080/appname. The image service eventually needs to reach this path.

// Set the context path for assets and pages
$rootScope.contextPath = $twigkit.getContextPath('/');

Then in the <media:image> tag on the HTML page, set the image-service-url parameter value the context path in main.js. The image-service-url parameter in the following example points to both the context path and a custom image service in /src/main/resources/conf/services/images/tableau which calls a Tableau server API to retrieve thumbnails:

<!-- has hyperlink and image from Tableau -->
<media:image 
  ng-if="(result.fields.image.val[0] !== undefined && result.fields._lw_data_source_s.val[0].indexOf('Tableau') > -1)"
  src="{{tableauImageUrl(result, $root.version) | trusted}}"
  image-service-url="{{$root.contextPath}}twigkit/services/images/tableau/"
  use-headers="true"
></media:image>

Set the context path in Tomcat. If the images DO display when deployed as root.war to Tomcat, then check Reverse Proxy settings.

add <Context path=”” docBase=”myhiway” debug=”0″ reloadable=”true”></Context> to server.xml?

Modal Dialog

Two-color dialog header

This two-color modal header solution differentiates between a static prefix phrase like Gallery Item and a mutable string like Value Oriented Architecture.

In the <widget:modal> tag on the HTML page/view, enter the desired full text heading as plain text or variable to the title attribute, along with a class name through the modal-classes attribute.

<widget:modal id="viewdetails{{result.id}}" title="Value Oriented Architecture" modal-classes="psjh-gallery-modal">
    <layout:include file="views/modals/viewdetails.html" value="{{result.id}}"></layout:include>
</widget:modal>

Add this modal class name to LESS/SASS with a reference to the framework’s default header style which contains a :before pseudo-element that manages the desired phrase prefix. Adjust colors as desired.

.psjh-gallery-modal .tk-stl-modal-header {
    color: #000 !important;
    :before {
        content:"Gallery Item - ";
        color: #f1933c;
    }
}

The result should be a two-color modal dialog heading that came from a single attribute.

Modal timeout

When a modal widget built on the App Studio ModalService service works on only on a few desktops, a failure in event timing may be the cause.

<widget:modal style="z-index: 1000;" id="TimeoutModal">
  <div id="timeoutContainer">
  ...
  </div>
</widget:modal>

Try wrapping calls from the ModalService.show function in custom.js with a timeout function.

// Timeout Warning - show modal dialog
$scope.$on('IdleStart', function () {
  $scope.showModal('TimeoutModal');
});
// Timeout Warning - set modal services (note: needs timeout to work in all browsers)
$scope.showModal = function (id) {
  $timeout(function () {
    ModalService.show(id);
  }, 0);
};

Search Results

Currency fields

Add the currency field to Solr’s managed-schema.xml as either a string or an integer.

<field indexed="true" multiValued="false" name="cost_str" required="false" stored="true" type="string"/>
<field indexed="true" multiValued="false" name="cost_int" required="false" stored="true" type="ints"/>

Then in App Studio, add either field to a <search:field> tag with the number-format parameter.

<!-- Products, Parts, Accessories, and Supplies -->
<layout:block styling="psjh-result-table" 
  ng-if="((result.fields._lw_data_source_s.val[0].indexOf('Documents')) === 0)">
  <search:field label="Product ID:" name="id" styling="meta label-inline"></search:field>
  <search:field label="CostA:" name="cost_str" number-format="$#,###.##" styling="meta label-inline"></search:field>
  <search:field label="CostB:" name="cost_int" number-format="$#,###.##" styling="meta label-inline"></search:field>
</layout:block>