Show attribute in search result template only if contains highlighted matches - search

I have several attributes set as attributesToRetrieve. In my template though only some of these are displayed. Roughly something like this:
<div id="results">
<div>{{_highlightResult.attr1.value}}</div>
<div>{{_highlightResult.attr2.value}}</div>
<div>{{_highlightResult.attr3.value}}</div>
</div>
This way the attributes will be rendered in any case, and highlighted if they contain a matched word.
Now I'd like to add another section where all other attributes can be displayed but only if they contain a matched word to be highlighted, something like:
<div id="results">
<div>{{_highlightResult.attr_1.value}}</div>
<div>{{_highlightResult.attr_2.value}}</div>
<div>{{_highlightResult.attr_3.value}}</div>
<!--
The following div will be rendered and displayed only
if any of these special attributes contain an highlighted word.
Only an attribute containing a matched word will be displayed
-->
<div class="other-attr">
{{_highlightResult.Long_SpecialAttr_1.value}}
{{_highlightResult.SpecialAttr_2.value}}
{{_highlightResult.SpecialAttr_3.value}}
</div>
</div>
As mentioned in the comment, this section will be rendered and displayed only if any of these special attributes contain an highlighted word, also only an attribute containing a matched word will be displayed.
Plus as you can see there is a Long_SpecialAttr_1, it's a long text attribute, wich I'd like to have it displayed as a snippeted attribute.
To give a better idea (maybe) what I'm trying to achieve for this additional section is something like the one Google display below every search results, a sort of text blob text with ellipsis containing the marked words of these attributes.
Is this possible? I'm using algolia instasearch.js, thank you!
UPDATE
Thanks to #Jerska for his answer, unfortunately a small bit of code wasn't working in my case, specifically:
['highlight', 'snippet'].forEach(function (type) {
data['_' + type + 'Result'].forEach(function (elt) {
elt.display = elt.matchLevel !== 'none';
});
});
giving me an error in the console stating data._snippetResult.forEach() is undefined. So I modified that bit with this:
for(var el in d._snippetResult)
{
// create new property with bool value, true if not "none"
d._snippetResult[el].display = d._snippetResult[el].matchLevel !== 'none';
};

First of all, just to clarify the settings of your index before going forward, Algolia also highlights the attributes in attributesToSnippet.
Also, to have an ellipsis on the attributes you snippet, you can set snippetEllipsisText.
So you might want to use these settings in your index:
attributesToHighlight: ['attr_1', 'attr_2', 'attr_3', 'SpecialAttr_2', 'SpecialAttr_3'],
attributesToSnippet: ['Long_SpecialAttr_1:3'], // Snippet should contain max 3 words
snippetEllipsisText: '…' // This is the utf-8 "Horizontal ellipsis" character
On the front-end side, in instantsearch.js you can use the transformData parameter on almost any widget to be able to access and/or modify the data passed to the template.
In this specific example, we'll want to have a look at transformData.item on the hits widget.
The first step would be to log the data:
search.addWidget(
instantsearch.widgets.hits({
transformData: {
item: function (data) {
console.log(data);
return data;
}
}
})
);
This will allow you to see that kind of response:
_highlightResult: {
attr_1: {
value: 'lorem <em>ipsum</em> dolor <em>sit</em>',
matchLevel: 'full',
matchedWords: ['ipsum', 'sit']
},
attr_2: {
value: 'lorem <em>ipsum</em> dolor',
matchLevel: 'partial',
matchedWords: ['ipsum']
},
attr_3: {
value: 'lorem',
matchLevel: 'none',
matchedWords: []
},
// ...
},
_snippetResult: {
Long_SpecialAttr_1: {
value: 'lorem <em>ipsum</em> dolor …', // Let's assume Long_SpecialAttr_1 was equal to 'lorem ipsum dolor sit'
matchLevel: 'full'
}
}
Unfortunately here, the API is a bit inconsistent since as you can see, snippeted attributes don't have the matchedWords attribute that highlighted attributes have. You can choose to set it both in attributesToSnippet and attributesToHighlight if you really want the info.
However, for your use-case, we just need matchLevel. What we want, is to display elements only if matchLevel !== 'none'. Unfortunately, Hogan.js, the underlying template engine of instantsearch.js doesn't allow for much flexibility, so you can't just put this comparison in your template.
A solution could be to precompute these conditions inside the transformData:
transformData: {
item: function (data) {
['highlight', 'snippet'].forEach(function (type) {
var group = data['_' + type + 'Result'];
for (var attr in group) {
if (!group.hasOwnProperty(attr)) continue;
var elt = group[attr];
elt.display = elt.matchLevel !== 'none';
};
});
data.displaySpecial = false ||
data._snippetResult.Long_SpecialAttr_1.display ||
data._highlightResult.SpecialAttr_2.display ||
data._highlightResult.SpecialAttr_3.display;
return data;
}
}
And then use these new attributes in your template:
<div id="results">
<div>{{{_highlightResult.attr_1.value}}}</div>
<div>{{{_highlightResult.attr_2.value}}}</div>
<div>{{{_highlightResult.attr_3.value}}}</div>
<!--
The following div will be rendered and displayed only
if any of these special attributes contain an highlighted word.
Only an attribute containing a matched word will be displayed
-->
{{#displaySpecial}}
<div class="other-attr">
{{#_snippetResult.Long_SpecialAttr_1.display}}
{{{_highlightResult.Long_SpecialAttr_1.value}}}
{{/_snippetResult.Long_SpecialAttr_1.display}}
{{#_highlightResult.SpecialAttr_2.display}}
{{{_highlightResult.SpecialAttr_2.value}}}
{{/_highlightResult.SpecialAttr_2.display}}
{{#_highlightResult.SpecialAttr_3.display}}
{{{_highlightResult.SpecialAttr_3.value}}}
{{/_highlightResult.SpecialAttr_3.display}}
</div>
{{#displaySpecial}}
</div>
(By the way, to render HTML, you should use {{{ ... }}} instead of {{...}}, I've replaced them here)

Related

Gravity Forms List Field Read Only column

Would anyone happen to know how to set readonly in a cell/column of gravity forms list field? I need one column for input and the others should not be edited by the user.
Gravity Perks has confirmed the list column is not supported by their plugin.
Add the following to an HTML field on your form:
**
You will need to know the list field id and the column you want to make read-only. If your list field is 32 for instance and you want the second column to be "read-only", you would replace the second line below with .gfield_list_32_cell2
<script>
jQuery(document).ready(function(){
jQuery( '.gfield_list_1_cell1' ).find( 'input, select, textarea' ).each(
function() {
if ( jQuery( this ).val() != '' ) {
jQuery( this ).prop( 'readonly', true );
}
});
});
</script>

Aurelia e2e: Check if span within a div contains certain text

I want to see if the text contained in a span which itself is contained in a div contains certain text.
This is the HTML:
<div class="funds">
<span>banking</span>
<span class="m-l-sm">
<b>EUR 1,000</b>
</span><!--anchor-->
</div>
I want to check if <span class="m-l-sm"> contains 'EUR'.
One way I tried to do this (among several others) is:
var checkFunds = element(by.id("m-l-sm"));
if (checkFunds.getText().toContain('EUR'&'GBP')) {
//do something
}
I get an error saying .toContain() is not a function.
I'm at a bit of a loss as to how to proceed.
.toContain() is a Jasmine matcher's method. It is usually used for assertions, e.g.:
expect(checkFunds.getText()).toContain('EUR');
Since it looks like you actually want to do something if you see a substring in a string - you need to get to the actual text of the element, which means you need to resolve the promise returned by .getText(). Then, you can check if substring EUR is inside the text:
var checkFunds = element(by.id("m-l-sm"));
checkFunds.getText().then(function (text) {
if (text.indexOf('EUR') >= 0) {
// do something
}
});

Getting element through attribute value in javaScript

I want to get the element through javascript based on attribute value, as per the example I want to get element through attribute "doc-cid" and the value of "doc-cid" is dynamic.
<p align="center" style="font-family:times;" doc-cid="11303">
<font size="2" doc-cid="11304">55</font></p>
<p id="demo">Click the button to change the text of a list item.</p>
<button onclick="myFunction()">Try it</button>
<script>
function myFunction()
{
var list = document.getElementsByTagName("doc-cid=\"11303\"")
alert(list.getAttribute("doc-cid"));
}
</script>
I'll start with a few pointers about your above code, and follow through with a solution.
POINTERS
doc-cid is not a "TagName", it is a custom attribute. hence, your function trying to getElementsByTagName will always fail for "doc-cid".
doc-cid can by dynamic (or not) it doesn't matter. A lookup function will always get the CURRENT DOM value of your element (unless you specifically make it do otherwise).
I suggest you use the new "data-*" attribute in html, it keeps your markup valid (if that is important to you). The use would be as follows:
your content
SOLUTION
function getElementByAttribute (attribute, value, start) {
var start = start || document,
cache, //store found element
i;
function lookup(start) {
// if element has been found and cached, stop
if (cache) {
return;
} else if (start.getAttribute(attribute) === value) { // check if current element is the one we're looking for
cache = start;
return;
} else if (start.childElementCount > 0) { // if the current element has children, loop through those
lookup(start.children[0]);
} else if (start.nextElementSibling) { // if the current element has a sibling, check it
lookup(start.nextElementSibling);
} else {
return;
}
}
lookup(start);
return cache;
}
You simply give the function the attribute name you are looking up, the value you need to match and the starting point of the lookup (if no starting point is specified it'll start at the very beginning of your page (much slower).
Below is an example for your markup:
// you have no easy to get starting point, so we'll traverse the DOM
getElementByAttribute('doc-cid', '11303');
If you want to start at a better node, you can add a wrapper div element and give it id="wrapper" then you could call the function as follows:
var start = document.getElementById('wrapper');
getElementByAttribute('doc-cid', '11303', start);
Hope this helps.

Wordpress Technical Terms

I'm working on designing a site in WP, but I'm at a loss for the right words to google. What I'm looking for is a "text container that can be toggled". I have a syntax highlighting plugin for posting code, but I don't want the code to be visible in large blocks considering it may be a little distracting. I was wondering if anyone could link me to a plugin or give me the technical term for what I'm thinking of, where you can put the text in a group and then be able to toggle whether it is visible or not within the page.
It sounds like what you are looking for is simply applying a CSS class to the element and then using jQuery or some other JS library to toggle its visibility. Example below (code is not optimized in order to explain some of the concepts. This can, read "should", be cleaned up):
// This is HTML/CSS
<body>
...
<p>Here is some normal text.</p>
Show/hide source code for displayText method
<div class="source_code" id="source_code_for_displayText_method">
// Groovy code
...
public void displayText(String message) {
outputStream.write(message)
}
...
</div>
...
Show/hide source code for download method
<div class="source_code" id="source_code_for_download_method">
// Groovy code
...
GParsPool.withPool(threads) {
sessionDownloadedFiles = localUrlQueue.collectParallel { URL url ->
downloadFileFromURL(url)
}
}
...
</div>
...
Show/hide all source code sections
...
</body>
You can default all source code sections to hidden:
// This is CSS
.source_code {
display: hidden;
}
Then you would use JS to provide the toggle ability:
// This is JavaScript
// This toggles a specific section by using an id ("#") selector
$('#source_code_displayText_method_toggle_link').onClick(function() {
$('#source_code_for_displayText_method').toggle();
});
// This toggles all source code sections by using a class (".") selector
$('#source_code_all_toggle_link').onClick(function() {
$('.source_code').toggle();
});
Some thoughts:
If you toggle all sections, you need to determine what the current state is -- if some are currently shown and others hidden, this will invert each. If you want "hide all" and "show all", then use .hide() and .show() respectively.
If you are manually adding the source code sections and want semantic selectors, the above is fine. If you are building some kind of automation/tool to allow you to repeat this, you'll probably want to use generated ids and helper links, in which case it would look like:
.
// This is HTML/CSS
<body>
...
<p>Here is some normal text.</p>
Show/hide source code for displayText method
<div class="source_code" id="source_code_1">
// Groovy code
...
public void displayText(String message) {
outputStream.write(message)
}
...
</div>
...
Show/hide source code for download method
<div class="source_code" id="source_code_2">
// Groovy code
...
GParsPool.withPool(threads) {
sessionDownloadedFiles = localUrlQueue.collectParallel { URL url ->
downloadFileFromURL(url)
}
}
...
</div>
...
Show/hide all source code sections
...
</body>
With the JavaScript to handle id parsing:
// This is JavaScript
// This toggles a specific section by using a dynamic id ("#") selector
$('.source_code_toggle_link').onClick(function(elem) {
var id = $(elem).attr("id");
// Split on the _ and take the last element in the resulting array
var idNumber = id.split("_")[-1];
var codeBlock = $('#source_code_' + idNumber);
codeBlock.toggle();
});

SimpleFlip View first image coming back from json doesn't show immediately in winjs

I have a simple WINJS flipview. There are 5 images loaded from an external json file. All the images load immediately except the first one, and second question is there a simple command to automatically set these to rotate?
So we are using a single page model app. This is a little promo slider I wanted to put in on one page and rotate. I've tried everything including just the demo, but the first item always comes back undefined.
I even tried removing the first image, but the first item always comes back undefined. I've spent a few days on this now with not much luck.
<div id="promoTemplate" data-win-control="WinJS.Binding.Template" style="display: none" >
<div class="overlaidItemTemplate">
<img class="image" data-win-bind="src: picture" />
<div class="overlay">
<h2 class="ItemTitle" data-win-bind="innerText: title"></h2>
</div>
</div>
</div>
<div id="promoFlipView" class="flipView" data-win-control="WinJS.UI.FlipView" data-win-options="{ itemDataSource: ActivityPromoData.bindingList.dataSource, itemTemplate: select('#promoTemplate') }">
</div>
This is connected to the demo example flipview Data
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved
(function () {
"use strict";
// This is an array that will be used to drive the FlipView in several
// scenarios. The array contains objects with the following attributes:
//
// type - There are two types that are used:
//
// item -
// The type for simple items. It informs the custom
// renderer that their is a title and picture that needs
// to be rendered.
//
// contentsArray -
// This is used for creating a table of contents. It
// informs the renderer that an array of data is present
// for use in constructing the Table of Contents.
//
// title - The title of a photo to be displayed.
//
// picture - The location of the photo to be displayed.
var array = [
{ type: "item", title: "Cliff", picture: "images/Cliff.jpg" },
{ type: "item", title: "Grapes", picture: "images/Grapes.jpg" },
{ type: "item", title: "Rainier", picture: "images/Rainier.jpg" },
{ type: "item", title: "Sunset", picture: "images/Sunset.jpg" },
{ type: "item", title: "Valley", picture: "images/Valley.jpg" }
];
var bindingList = new WinJS.Binding.List(array);
WinJS.Namespace.define("ActivityPromoData", {
bindingList: bindingList,
array: array
});
var e = ActivityPromoData.bindingList.dataSource;
})();
The original question above here is the FIRST IMAGE BUG FIX: adding this to the onready. This works providing there is no custom animations.
var proxyObject;
proxyObject = new WinJS.Binding.as({
itemTemplate: tutorialTemplate,
customAnimations: false
});
tutorialFlipView.winControl.itemTemplate = tutorialTemplate;
There is not builtin command to rotate. setInternval() can be used for this.
var timerId = setInternal(function()
{
if (flipview.winControl.count - 1 == flipview.winControl.currentPage)
flipview.winControl.currentPage = 0;
else
flipview.winControl.next();
}, slideshowInternal);
// to stop slideshow
clearInterval(timerId);
This assumes no complex transition between pages (for example: KENBURNS). if that is required, it is more involved problem, and it is good to consider using some existing javascript sdk on web and integrate in a custom winjs control. flipview control did not work well when integrating custom page transition animations.
regards image not loading - html/js code snippet will help answer it.
if <img> tag is used and bound it to the http image url, image is not guaranteed to be loaded when flipview shows the page. If number of images are handful, it might be better to download them using winjs.xhr to ensure that they are in cache and then, load the flipview.

Resources