Get Twitter Bloodhound Typeahead suggestions without opening dropdown - twitter-typeahead

I am able to get all the Twitter Typeahead suggestions with the code below after user enters an input and the typeahead:render is called. I would like to hide the dropdown all the time and get the suggestions only in an array. Is there a way to achieve this since typeahead:render would probably require the dropdown be opened.
var bloodhoundData = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.whitespace,
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: localData
});
$('filter .typeahead').typeahead({
hint: true,
highlight: true,
minLength: 1
},
{
source: bloodhoundData,
limit: 99999
}).on('typeahead:render', getSuggestions);
function getSuggestions() {
var suggestions = Array.prototype.slice.call(arguments, 1);
}

Since Bloodhound.js is a standalone library, you don't have to use typeahead with it. You could tie the input for bloodhound to an ordinary text input and examine the result of the get method.
Something like this might work, with q being the text from the input (borrowed from the NFL Teams example):
var myBloodhound = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
identify: function(obj) { return obj.name; },
local: localData
});
function getMyData(q, sync) {
myBloodhound.search(q, sync);
}
You can check out the bloodhound documentation here and the examples here.

Related

Pnotify and fullcalendar

I am using pnotify and loading callback function to show a notification when the fullcalendar plugin has loaded all events.
loading:function(isLoading, view){
if (isLoading === false){
new PNotify({
title:"Finished loading events",
type:'success',
delay: 1000
});
My problems is that when ever I move to different dates it calls loading again so I am left with so many notifications shown on my screen that it becomes very unusable. How can I bypass this? Is there a way to check if a notification is active and just change the text and title of it?
You can add that logic based on the template you're using (check the template docs).
Your code would be something like
loading:function(isLoading, view){
var exists = false;
$(".ui-pnotify-title").each(function() {
if ($(this).html() == 'Finished loading events')
exists = true;
});
if (!exists) {
new PNotify({
title:"Finished loading events",
type:'success',
delay: 1000
});
}
}
It would be better if you could use a specific id or class to detect if the notification is already shown, but this works.
Take a look at the working jsfiddle.
You can just store it in a variable, do your necessary code (like nullable/undefined checks, etc) and call "update()" (here: http://sciactive.com/pnotify/ - for example, find for 'Click Notice' and see the source)
var p = new PNotify({
title: 'Some title',
text: 'Check me out! I\'m a error.',
type: 'error',
icon: 'fa fa-times-circle'
});
// ... code ...
p.update({title: 'My new title'});

Fully populated ko.observableArray will not show in Template using foreach

I am trying to separate a list of JSON data into segments ("sliders") and have succeeded in creating a data object in the format I want, however the foreach binding is not working as expected.
HTML Template:
<div class="slide" data-bind="foreach: actionSliders">
Stuff
</div>
Here is my relevant Knockout code:
function Slider() {
this.actions = ko.observableArray([]);
}
var viewModel = {
actionSliders: ko.observableArray([])
};
viewModel.loadData = function() {
//LOAD Actions from API
jQuery.ajax({
type: 'GET',
url: 'http://'+window.location.hostname+'/api/actions/get_author_posts/',
dataType: 'json',
success: function (ActionData) {
console.log('getJSON data - Actions',ActionData.posts);
var actionSlidersCount = 0;
viewModel.actionSliders([]);
//create the first slider array
viewModel.actionSliders().push(new Slider());
viewModel.actionSliders()[0].actions([]);
jQuery.each(ActionData.posts, function(index) {
// add each action to the current slider
viewModel.actionSliders()[actionSlidersCount].actions().push(new Action(this));
//add a new slider every 5 records
var calc = (parseInt(index)+1)%5;
if(calc ==0 ){
//new slider
actionSlidersCount++;
viewModel.actionSliders().push(new Slider());
viewModel.actionSliders()[actionSlidersCount].actions([]);
}
});
console.log('ActionSliders',viewModel.actionSliders());
},
data: { },
async: true
});
};
This is what my data looks like in the console:
ActionSliders
[Slider, Slider, Slider, Slider, Slider, Slider, Slider, sortNum: function, random: function, sum: function, max: function, min: function…]
0: Slider
actions: Object[0]
__proto__: Slider
1: Slider
2: Slider
3: Slider
4: Slider
5: Slider
6: Slider
length: 7
__proto__: Array[0]
* I can access all the data with console commands:
> viewModel.actionSliders()[0].actions()[0]
Action {id: 197, title: "Turned off the tap while brushing my teeth"…}
> viewModel.actionSliders()
[ Slider, Slider, Slider, Slider, Slider, Slider, Slider]
So, as you can see, in the working model (no errors in console, no data-bind errors), the object is fully populated with data, and in the template, "stuff" should repeat 6 times - once for each Slider, but the loop isn't even working. Is there a problem with having observable arrays inside of others? Am I missing something in the way I am creating the Slider objects? Any advice is most welcome, please.
Since you pushed new Slider object into your actionSliders observableArray I guessing the structure might be like this:
actionSliders = [
{
actions = {}
},
{
actions = {}
}
];
I'm sorry if this doesn't work for your, but how if you try to bind it like this ? :
<div class="slide" data-bind="foreach: actionSliders().actions">
Stuff
</div>
I found the answer, it was a two part issue.
The reason why foreach wasn't working was because I was pushing to the function, instead of the array. I needed to use:
viewModel.actionSliders.push(new Slider());
instead of:
viewModel.actionSliders().push(new Slider());
Once I did that, the foreach worked for the main object
Then, I realized I needed to inject the data inside the class, instead of from outside. To remedy that, I modified the code like this:
var tempActionArray = [];
jQuery.each(ActionData.posts, function(index) {
//add an action to the current slider
var tempAction = new Action(this);
tempActionArray.push(tempAction);
var calc = (parseInt(index)+1)%5;
if(calc ==0 ){
//add a new slider
actionSlidersCount++;
viewModel.actionSliders.push(new Slider(tempActionArray));
//reset temp array
tempActionArray = [];
//viewModel.actionSliders[actionSlidersCount].actions([]);
}
});
function Slider(data) {
var data = data || [];
this.actions = ko.observableArray([]);
var Actions = [];
//console.log("slider data",data)
jQuery.each(data, function(index) {
//console.log("action index",data[index])
Actions.push(data[index]);
});
this.actions = Actions;
}
and now all is good in the world! :) Moving on...

How to correctly waitFor() a saveScreenShot() end of execution

Here is my full first working test:
var expect = require('chai').expect;
var assert = require('assert');
var webdriverjs = require('webdriverjs');
var client = {};
var webdriverOptions = {
desiredCapabilities: {
browserName: 'phantomjs'
},
logLevel: 'verbose'
};
describe('Test mysite', function(){
before(function() {
client = webdriverjs.remote( webdriverOptions );
client.init();
});
var selector = "#mybodybody";
it('should see the correct title', function(done) {
client.url('http://localhost/mysite/')
.getTitle( function(err, title){
expect(err).to.be.null;
assert.strictEqual(title, 'My title page' );
})
.waitFor( selector, 2000, function(){
client.saveScreenshot( "./ExtractScreen.png" );
})
.waitFor( selector, 7000, function(){ })
.call(done);
});
after(function(done) {
client.end(done);
});
});
Ok, it does not do much, but after working many hours to get the environement correctly setup, it passed. Now, the only way I got it working is by playing with the waitFor() method and adjust the delays. It works, but I still do not understand how to surely wait for a png file to be saved on disk. As I will deal with tests orders, I will eventually get hung up from the test script before securely save the file.
Now, How can I improve this screen save sequence and avoid loosing my screenshot ?
Since you are using chaning feature of webdriverjs it is wrong to use callback in waitFor function.
Function saveScreenshot is also chained. So the proper way would be the following:
it('should see the correct title', function(done) {
client.url('http://localhost/mysite/')
.getTitle( function(err, title){
expect(err).to.be.null;
assert.strictEqual(title, 'My title page' );
})
.waitFor( selector, 2000)
.saveScreenshot( "./ExtractScreen.png" )
.call(done);
});

jQuery autocomplete: how to split the string result?

I have the following situation with an autocomplete plugin on an .aspx page. It is working fine. The result from the autocomplete search yields a product id and a product description is concatenated with it (i.e. 2099 -- A Product). I know that I need to use split() with this but where do I put it? I'm still rather new to jQuery and javascript.
$(document).ready(function() {
$('.divAutoComplete').autocomplete("LookupCodes.aspx?type=FC", {
mustMatch: true
});
});
If it's the same autocomplete I've used (by Tomas Kirda) you should be able to add an onSelected event like so:
$(document).ready(function() {
$('.divAutoComplete').autocomplete("LookupCodes.aspx?type=FC", {
mustMatch: true,
onSelect: function(value, data) { autoCompleteSelected(value, data); }
});
});
function autoCompleteSelected(value, data) {
var parts = data.split("--");
... do something with parts
}
Obviously, if it's not the then it will have different events
In JavaScript, any string can be split using the split function like so:
"Pandas enjoy tasty bamboo".split(' ')
The above splits the string on spaces returning the following array:
["Pandas", "enjoy", "tasty", "bamboo"]
Any string can be fed to the split function and it'll cope with multi-character strings just fine.
Now as for your question with the jQuery autocomplete plugin, you'll need to have your .aspx page return a JS array of options in order for it to work. Alternatively, you can load the data some other way and then pass an array to autocomplete. If the server returns an array like the following then you can pass it directly:
["1234 -- Chicken", "4321 -- Noodle", "1432 -- Irrational Monkeys"]
The point is that autocomplete uses an array for matching.
The docs for the autocomplete plugin seem decent enough.
do this code for splitting
<script type="text/javascript">
$(function() {
var availableTags = ["c++", "java", "php", "coldfusion", "javascript", "asp", "ruby", "python", "c", "scala", "groovy", "haskell", "perl"];
function split(val) {
return val.split(/,\s*/);
}
function extractLast(term) {
return split(term).pop();
}
$("#tags").autocomplete({
minLength: 0,
source: function(request, response) {
// delegate back to autocomplete, but extract the last term
response($.ui.autocomplete.filter(availableTags, extractLast(request.term)));
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function(event, ui) {
var terms = split( this.value );
// remove the current input
terms.pop();
// add the selected item
terms.push( ui.item.value );
// add placeholder to get the comma-and-space at the end
terms.push("");
this.value = terms.join(", ");
return false;
}
});
});
</script>

How to update a Dojo Grid cell value using a TooltipDialog (and DropDownButton)

I have a dojo grid which is using some editable dijit form fields. All is well, until I try ot implement an country (multi) select cell as an Tooltip Dialog; i.e., show a drop down button which opens the tooltip dialog populated with a checkbox array to select one or more country. Once checked and clicked OK, the cell should update with a list of selected countries. Obviously I'll take care of updating the server via the store later on.
I've implemented a country select tooltip dialog which works fine like so:
dojo.provide("CountrySelector");
dojo.declare(
"CountrySelector",
[dijit.form.DropDownButton],
{
label: 'Countries',
dropDown: new dijit.TooltipDialog({ execute: function() {
console.log("EXECUTE : ", arguments[0]);
this.value = arguments[0].country;
}, href:'/cm/ui/countries' }),
postCreate: function() {
this.inherited(arguments);
this.label = this.value;
dojo.connect(this.dropDown, 'onClose', function() { console.log('close'); });
console.log("CountrySelect post create", this);
},
}
);
And the grid cell is typed as:
{ name: 'Countries', field: 'targeting.countries', editable: true, hidden: false, type:dojox.grid.cells._Widget, widgetClass: CountrySelector },
All is working fine but I can't figure out how to update cell's content and store once the widget is executed. As well, I don't seem to have the row id of the updated row.
Any ideas?
Thanks,
Harel
//Layout:
gridLayout: {rows: [{name: 'Coll Name',field: 'colField', type: dojox.grid.cells.ComboBox, editable:'true', width:'8%',options: [], alwaysEditing:false}]}
//Grid Store:
this.gridStore = new dojo.data.ItemFileReadStore({data: {items: data}});
//
var setOptions = function(items, request){
this.gridLayout.rows[0].options.push('Val 1','Val 2');
this.gridLayout.rows[0].values.push('1','2');
dojo.connect(this.gridLayout.rows[0].type.prototype.widgetClass.prototype, "onChange",this, "_onComboChange");
}
this.gridStore.fetch({onComplete: dojo.hitch(this,setOptions)});
_onComboChange: function (selectedOption) {
console.info("_onComboChange: ",selectedOption);
},
// If you need to populate combos with different values you can use onItem
var getArray = function(item, request){
// populate one by one
// attach an event to each combo
}
this.gridStore.fetch({onItem: dojo.hitch(this,getArray)});
This is what i used to update my grid
var idx = yourGrid.getItemIndex(item);
if (idx >- 1) {
yourGrid.updateRow(idx);
}
More detail
every row is identified by its identifier
yourGrid.store.fetchItemByIdentity({
identity: <yourIdentity>,
onItem: function(item){
// Update your attributes in the store depending on the server response
// yourGrid.store.setValue(item, <attribute>,<value>);
var idx = yourGrid.getItemIndex(item);
if (idx >- 1) {
yourGrid.updateRow(idx);
}
}
});
I didn't set up a test with your code but you should be able to do it by just creating a method named getValue in your widget that returns the value.
Take a look at the other examples (like dojox.grid.cells.ComboBox) to get an idea of what getValue should look like.

Resources