Render a headline when search results are displayed - algolia

I didn’t find a way to output a headline (Search results:) in front of the search results that is only visible when results are displayed.
It wouldn’t be a problem to have it every time (part of the hit item template) but I don’t need one if the empty results template is displayed.
What would be a nice solution?
Here is my current hits widget configuration:
this.search.addWidget(
this.algoliaInstantSearch.widgets.hits({
container: '.js-algolia-hits',
escapeHits: true,
templates: {
item: document.querySelector('.js-algolia-hit-item-template').innerHTML,
empty: window.algoliaEmptyResultsText
},
cssClasses: {
root: 'c-search__hits-list',
empty: 'c-search__hits-empty',
item: 'c-search__hits-list-item'
}
})
);

You can use the allItems template option to have the total control of the rendering of the hits.
search.addWidget(
instantsearch.widgets.hits({
container: document.querySelector("#products"),
templates: {
allItems: function({ hits }) {
// No results message
if (hits.length === 0) return "";
const hitsMarkup = hits
.map(
hit =>
`<div class="ais-hits--item">${
hit._highlightResult.name.value
}</div>`
)
.join("");
return `
<div>
<h1>Search Results:</h1>
<div class="ais-hits--container">${hitsMarkup}</div>
</div>
`;
}
}
})
);
You can see the result here: https://codesandbox.io/s/8zxxvo84wl

Related

EmberJS: Observer Not Being Triggered on Computed Property

I am building a handelbars helper that renders a checkbox group. My goal is to display a checkbox group with something like this and get two-way binding on selectedOptions:
{{form-checkboxGroup options=allOptions selectedOptions=selectedOptions}}
I've used this pattern successfully with other form components and it's a big win. I'm able to render my allOptions and selectedOptions values as a checkbox group, but it's the two-way binding that's tripping me up. Any idea what I'm missing?
By the way, I'm using ember-cli, but that doesn't affect anything relating to this issue.
Here's my setup:
Handlebars Helper: helpers/form-checkbox-group.js
The sole purpose of this file is to link the Handelbars expression {{form-checkboxGroup}} to the view and template below.
import FormCheckboxGroupView from 'my-app/views/util/form/form-checkbox-group';
export default Ember.Handlebars.makeBoundHelper(function( options ) {
return Ember.Handlebars.helpers.view.call(this, FormCheckboxGroupView, options);
});
CheckboxGroup Handlebars Template: templates/util/form/form-checkbox-group.hbs
...
{{#each user in view.combinedOptions}}
{{input type="checkbox" name="view.fieldName" checked=user.checked }} {{user.name}}
{{/each}}
...
CheckboxGroup View: views/util/form/form-checkbox-group.js
...
export default FormCheckboxGroupView = Ember.View.extend( FormFieldMixin, {
templateName: 'util/form/form-checkbox-group',
selectedOptions: function() {
console.log("When triggered this could update view.selectedOptions");
}.observes('view.combinedOptions.#each.checked'),
// combines the "options" and "selected options" into a single array of "combinedOptions" explicitly indicating what's checked
combinedOptions: function() {
...
// sample result of combinedOptions:
// { name: "Johnny Five", id: "12", checked: true }
return combinedOptions;
}.property('view.options', 'view.selectedOptions')
});
And finally, to actually use my Handlebars helper, here's the consuming page's template and corresponding controller:
Consuming Page: templates/my-page.hbs
{{form-checkboxGroup options=allUsersArray selectedOptions=selectedUsersArray fieldName="selectedProvidersArray" }}
Backing Controller for Consuming Page: controllers/my-page.js
export default MyPageController = Ember.Controller.extend( FormMixin, {
allUsersArray: [
{ name: 'Bill Huxtable', id: 'billy' },
{ name: 'Samantha Jones', id: 'jones' },
{ name: 'Tony Pepperoni', id: 'tonyp' },
{ name: 'Ridonk Youliss', id: 'silly' }
],
selectedUsersArray: [
{ name: 'Tony Pepperoni', id: 'tonyp' },
{ name: 'Ridonk Youliss', id: 'silly' }
],
...
});
So, all of this successfully renders the checkbox group nicely, but my efforts to capture the fact that a checkbox has been newly selected by using observes("view.combinedOptions.#each.checked') is not working.
Any idea on how I can this up for two-way binding? Thanks in advance for assistance!
No jsbin so I'm flying blind, but try this:
selectedOptions: function() {
console.log("When triggered this could update view.selectedOptions");
}.observes('combinedOptions.#each.checked')
view.property is how you access view from template. You don't need that from the view itself (unless you have view property on your view).

How to remove certain elements before taking screenshot?

I am able to take screenshot of the page using the example code below:
html2canvas(document.body, {
onrendered: function(canvas) {
document.body.appendChild(canvas);
}
});
Now there are certain div's i dont want to be part of the page when I take the screenshot?
How can i prevent them from being part of the screenshot.
One way I thought was to clone the element and then remove the elements, but taking a screenshot of the clone gives a white screen. Here is the code I used:
html2canvas($(document.body).clone()[0], {
onrendered: function(canvas) {
document.body.appendChild(canvas);
}
});
Add this attribute: data-html2canvas-ignore to any element you don't want to be taken when the screenshot is processed.
Hopefully this will help the next guy.
When I used this library I faced a problem that the lib download all the images in my application, that cause the application to run slowly. I resolved the problem using the ignoreElements option.
This is my code:
var DropAreaElement= document.getElementById("123");
var config= {
useCORS: true,
ignoreElements: function (element) {
if (element.contains(DropAreaElement) || element.parentElement.nodeName =="HTML" || element == DropAreaElement || element.parentNode == DropAreaElement) {
console.log("elements that should be taken: ", element)
return false;
}else {
return true;
}
}
};
html2canvas(DropAreaElement, config).then(function (canvas){
var imgBase64 = canvas.toDataURL('image/jpeg', 0.1);
console.log("imgBase64:", imgBase64);
var imgURL = "data:image/" + imgBase64;
var triggerDownload = $("<a>").attr("href", imgURL).attr("download", "layout_" + new Date().getTime() + ".jpeg").appendTo("body");
triggerDownload[0].click();
triggerDownload.remove();
}).catch(Delegate.create(this, function (e){
console.error("getLayoutImageBase64 Exception:", e);
});
If you don't want to use an attribute, html2canvas does provide a method to remove elements. For example:
html2canvas( document.body, {
ignoreElements: function( element ) {
/* Remove element with id="MyElementIdHere" */
if( 'MyElementIdHere' == element.id ) {
return true;
}
/* Remove all elements with class="MyClassNameHere" */
if( element.classList.contains( 'MyClassNameHere' ) ) {
return true;
}
}
} ).then( function( canvas ) {
document.body.appendChild( canvas );
} );
For more information, see html2canvas options.
You can create HOC for <Printable/> and <NonPrintable/> , you can wrap your component with <NonPrintable><YourCoolComponent/></NonPrintable>
those children components would be excluded.
import React from "react"
interface INonPrintable {
children: React.ReactChildren
}
/*
HOC - Printable which injects the printId to the React component
which gets us Printable Context to html2canvas => jsPDF
eg:
<Printable printId="about-you-print">
<PersonalInfo badEmail={badEmail} />
<IdentityInfo />
<AdditonalInfo />
<AddressInfo
serviceAddress={serviceAddress}
billingAddress={this.state.billingAddress}
setBillingAddress={this.setBillingAddress}
/>
</Printable>
*/
export default function Printable({ printId = "", children, ...restProps }) {
return <div print-id={printId} {...restProps}>{children}</div>
}
/*
HOC - NONPrintable which injects the data-html2canvas-ignore to the React component
which gets us Printable Context to html2canvas => jsPDF
eg:
<NonPrintable style={{display:"flex",justifyContent:'space-around'}}>
<Button
text="Print PDF using Own utility"
onClick={this.handlePrintPdf}
/>
<Button
text="Print PDF using html2canvas + jsPDF"
onClick={this.handlePrintwithPDFjs}
/>
</NonPrintable>
*/
export const NonPrintable = ({ children, ...restProps }) => {
return <div data-html2canvas-ignore {...restProps}>{children}</div>
}

Angular app wont load (JSFiddle)

I have a simple angular app here
<div ng-app="WhereToMeet" ng-controller="MapCtrl">
<leaflet shape="shape"></leaflet>
<button ng-click="clicked()">Clicked</button>
</div>
app = angular.module("WhereToMeet", [])
app.directive "leaflet", ->
restrict: "E"
replace: true
transclude: true
template: "<div id=\"map\"></div>"
scope:
shape: "=shape"
link: (scope, element, attrs, ctrl) ->
scope.$watch attrs.shape,( (newValue, oldValue) ->
watched newValue
), true
watched = (newValue) ->
alert newValue
#MapCtrl = ($scope) ->
clicked = (clicked) ->
$scope.shape = "Clicked"
alert "clicked"
I have it in a JSFiddle http://jsfiddle.net/charliedavi/bezFB/22/ but it wont run. Really odd. I think its an error with my coffee script but I can not see it
error:
Uncaught SyntaxError: Unexpected string fiddle.jshell.net:22
Uncaught Error: No module: WhereToMeet
in pure JS
var app;
app = angular.module("WhereToMeet", []);
app.directive("leaflet", function() {
var watched;
({
restrict: "E",
replace: true,
transclude: true,
template: "<div id=\"map\"></div>",
scope: {
shape: "=shape"
},
link: function(scope, element, attrs, ctrl) {
return scope.$watch(attrs.shape, (function(newValue, oldValue) {
return watched(newValue);
}), true);
}
});
return watched = function(newValue) {
return alert(newValue);
};
});
this.MapCtrl = function($scope) {
var clicked;
return clicked = function(clicked) {
$scope.shape = "Clicked";
return alert("clicked");
};
};
http://jsfiddle.net/charliedavi/gsPx3/2/
i dont know coffee script but angular. i just tried to solve it. ;-)
Select no-wrap body, under select framework
Select no-library(pure-js)
Add angular js as resources
Manually initialize angular using this angular bootstrap
angular.bootstrap document, ['WhereToMeet']
The generated javascript code is in another scope. You have to solve this
by either adding the -b parameter to the coffeescript compiler or export your function
explicitly via
root = exports ? this
root.clicked = ->
alert "clicked"
$scope.shape = "Clicked"
It is working now Fiddle Here
I had a similar issue with jsfiddle and angular yesterday. I had to do a couple of things to make it work:
Jsfiddle is adding the tags for html and body, so just write the markup that should end up inside the body tag.
Add a wrapping div with ng-app="myApp" instead of trying to specify another html-tag
Select no-wrap body, under select framework
I don't know what your "leaflet" is doing but I have updated your fiddle so that the click will trigger an alert
I've had to change the how the controller is instantiated to get the onclick to work.
http://jsfiddle.net/t9nsY/2/
app.controller("MapCtrl", function ($scope) {
$scope.clicked = function (clicked) {
console.log("clicked");
$scope.shape = "Clicked";
return alert("clicked");
};
});

Dynamic Carousel Content does not show

I have been working on this for a number of days now, but my limited JS knowledge seems to hurt me.
I am creating a dynamic Ext.Carousel component in my ST2 application, which is based on the contents of a Store file.
That all works fine, but I will show the code anyway, so that nothing is left to imagination:
Ext.getStore('DeviceStore').load(
function(i) {
Ext.each(i, function(i) {
if (i._data.name == 'Audio Ring') {
var carousel = Ext.ComponentManager.get('speakerCarousel');
var items = [];
Ext.each(i.raw.speakers, function(speaker) {
items.push({
sci: Ext.create('SmartCore.view.SpeakerCarouselItem', {
speakerId: speaker.speakerid,
speakerName: speaker.speakername,
speakerEnabled: speaker.speakerenabled
})
});
});
carousel.setItems(items);
}
});
})
Now, this adds me the appropriate number of items to the carousel. They display, but without the content I specified:
This is the Carousel itself:
Ext.define('SmartCore.view.SpeakerCarousel', {
extend: 'Ext.Carousel',
xtype: 'speakerCarousel',
config: {
id: 'speakerCarousel',
layout: 'fit',
listeners: {
activeitemchange: function(carousel, item) {
console.log(item);
}
}
}
});
This is the item class, that I want to fill the data from the store into:
Ext.define("SmartCore.view.SpeakerCarouselItem", {
extend: Ext.Panel,
xtype: 'speakerCarouselItem',
config: {
title:'SpeakerCarouselItem',
styleHtmlContent: true,
layout: 'fit'
},
constructor : function(param) {
this.callParent(param);
this.add(
{
layout: 'panel',
style: 'background-color: #759E60;',
html: 'hello'
}
)
}
});
Again, the right number of items shows in the carousel (11), but the content is not visible, nor is the background colour changed.
When I check the console.log(item) in the browser, the items show as innerItems inside the carousel object.
Any help is greatly appreciated!!
Well, I fixed it myself, or better, I found a workaround that seems to be what I want.
I ended up ditching the constructor all together.
Instead I overwrote the apply method for the 'speakerName' key-value pair.
From there, I can use:
this._items.items[0]._items.items[i].setWhatever(...)
to set the content inside the item.
If anyone knows the "real" way to do this, I would still greatly appreciate input!

Handle selected event in autocomplete textbox using bootstrap Typeahead?

I want to run JavaScript function just after user select a value using autocomplete textbox bootstrap Typeahead.
I'm searching for something like selected event.
$('.typeahead').on('typeahead:selected', function(evt, item) {
// do what you want with the item here
})
$('.typeahead').typeahead({
updater: function(item) {
// do what you want with the item here
return item;
}
})
For an explanation of the way typeahead works for what you want to do here, taking the following code example:
HTML input field:
<input type="text" id="my-input-field" value="" />
JavaScript code block:
$('#my-input-field').typeahead({
source: function (query, process) {
return $.get('json-page.json', { query: query }, function (data) {
return process(data.options);
});
},
updater: function(item) {
myOwnFunction(item);
var $fld = $('#my-input-field');
return item;
}
})
Explanation:
Your input field is set as a typeahead field with the first line: $('#my-input-field').typeahead(
When text is entered, it fires the source: option to fetch the JSON list and display it to the user.
If a user clicks an item (or selects it with the cursor keys and enter), it then runs the updater: option. Note that it hasn't yet updated the text field with the selected value.
You can grab the selected item using the item variable and do what you want with it, e.g. myOwnFunction(item).
I've included an example of creating a reference to the input field itself $fld, in case you want to do something with it. Note that you can't reference the field using $(this).
You must then include the line return item; within the updater: option so the input field is actually updated with the item variable.
first time i've posted an answer on here (plenty of times I've found an answer here though), so here's my contribution, hope it helps. You should be able to detect a change - try this:
function bob(result) {
alert('hi bob, you typed: '+ result);
}
$('#myTypeAhead').change(function(){
var result = $(this).val()
//call your function here
bob(result);
});
According to their documentation, the proper way of handling selected event is by using this event handler:
$('#selector').on('typeahead:select', function(evt, item) {
console.log(evt)
console.log(item)
// Your Code Here
})
What worked for me is below:
$('#someinput').typeahead({
source: ['test1', 'test2'],
afterSelect: function (item) {
// do what is needed with item
//and then, for example ,focus on some other control
$("#someelementID").focus();
}
});
I created an extension that includes that feature.
https://github.com/tcrosen/twitter-bootstrap-typeahead
source: function (query, process) {
return $.get(
url,
{ query: query },
function (data) {
limit: 10,
data = $.parseJSON(data);
return process(data);
}
);
},
afterSelect: function(item) {
$("#divId").val(item.id);
$("#divId").val(item.name);
}
Fully working example with some tricks. Assuming you are searching for trademarks and you want to get the selected trademark Id.
In your view MVC,
#Html.TextBoxFor(model => model.TrademarkName, new { id = "txtTrademarkName", #class = "form-control",
autocomplete = "off", dataprovide = "typeahead" })
#Html.HiddenFor(model => model.TrademarkId, new { id = "hdnTrademarkId" })
Html
<input type="text" id="txtTrademarkName" autocomplete="off" dataprovide="typeahead" class="form-control" value="" maxlength="100" />
<input type="hidden" id="hdnTrademarkId" />
In your JQuery,
$(document).ready(function () {
var trademarksHashMap = {};
var lastTrademarkNameChosen = "";
$("#txtTrademarkName").typeahead({
source: function (queryValue, process) {
// Although you receive queryValue,
// but the value is not accurate in case of cutting (Ctrl + X) the text from the text box.
// So, get the value from the input itself.
queryValue = $("#txtTrademarkName").val();
queryValue = queryValue.trim();// Trim to ignore spaces.
// If no text is entered, set the hidden value of TrademarkId to null and return.
if (queryValue.length === 0) {
$("#hdnTrademarkId").val(null);
return 0;
}
// If the entered text is the last chosen text, no need to search again.
if (lastTrademarkNameChosen === queryValue) {
return 0;
}
// Set the trademarkId to null as the entered text, doesn't match anything.
$("#hdnTrademarkId").val(null);
var url = "/areaname/controllername/SearchTrademarks";
var params = { trademarkName: queryValue };
// Your get method should return a limited set (for example: 10 records) that starts with {{queryValue}}.
// Return a list (of length 10) of object {id, text}.
return $.get(url, params, function (data) {
// Keeps the current displayed items in popup.
var trademarks = [];
// Loop through and push to the array.
$.each(data, function (i, item) {
var itemToDisplay = item.text;
trademarksHashMap[itemToDisplay] = item;
trademarks.push(itemToDisplay);
});
// Process the details and the popup will be shown with the limited set of data returned.
process(trademarks);
});
},
updater: function (itemToDisplay) {
// The user selectes a value using the mouse, now get the trademark id by the selected text.
var selectedTrademarkId = parseInt(trademarksHashMap[itemToDisplay].value);
$("#hdnTrademarkId").val(selectedTrademarkId);
// Save the last chosen text to prevent searching if the text not changed.
lastTrademarkNameChosen = itemToDisplay;
// return the text to be displayed inside the textbox.
return itemToDisplay;
}
});
});