I am integrating Algolia autocomplete and do not like the look of the autocomplete suggestions.
Specifically, I don't want the administrative municipalities and districts to appear in the suggestions, only address, city, country.
How can I omit the administrative query?
For example, if I type in "Sarajevo" the suggestions appear as "Sarajevo, Kanton of Sarajevo, Bosnia and Herzegovina" - I want it to appear as simply "Sarajevo, Bosnia and Herzegovina".
You should use the templates option of the Places constructor.
Here's a simple example:
const placesInstance = places({
...
templates: {
suggestion: function(s) {
return [s.highlight.name, s.highlight.city, s.highlight.country].join(', ');
}
}
});
Have a look at the function that is used by default for a more elaborate sample:
https://github.com/algolia/places/blob/master/src/formatDropdownValue.js
To solve the problem of once you select a 'place', the administrative level gets displayed in the search bar., you can leverage on jquery's focusout event.
Example
var cityCountry ,searchInput;
searchInput = $('#search-input'); //our search field
//Initialize
var placesAutocomplete = places({
// appId: 'YOUR_PLACES_APP_ID',
// apiKey: 'YOUR_PLACES_API_KEY',
container: document.querySelector('#search-input'),
});
//Attach required data to cityCountry
placesAutocomplete.on('change', function(e){
let suggestion,city,country;
suggestion = e.suggestion;
city = suggestion.name;
country= suggestion.country;
cityCountry = city+', '+country;
});
//Manipulate the search field on focusout
searchInput.on('focusout', function(){
setTimeout(function () {
searchInput.val(cityCountry);
},1)
});
Note, it won't work without the setTimeout().
Related
I have a problem in preserving the values in the textbox after dropdownlist selected index changed in asp.net mvc. Below is the code for initiating the dropdown onchange event.
#Html.DropDownList("BranchId",null,"Select Branch", new { onchange = "location.href='/User/GetRoles?BranchId='+this.options[this.selectedIndex].value" })
Аnd the roles dropdown binded with the values, but what i typed in the textboxes just above the branch dropdown get lost.
Please help me.
Regards,
Azeem
This is because you are changing your URL location by using location.href. What exactly do you want to achieve? If you need to load certain logic, you might as well bind change event to jQuery function that could load data from the server, and then you do something with that data.
$(document).ready(function() {
$("#BranchId").change(function() {
$.getJSON(YourUrlThatReturnsValues, {data: yourparameter}, function() {
// do some processing here.
});
});
});
The other option is to use Ajax Helpers instead of Html Helpers. Instead of Html.DropDownList you would use Ajax.DropDownList.
You may try something like that:
$(function () {
$('#BranchId').change(function () {
var id = $("#BranchId option:selected").val();
var data = {BranchId: id };
$.get("#Url.Action("User", "GetRoles")", data).done(function(d){
$('.somediv').val(d.rolename)
});
});
In my app I have to add Search field and some select fields.
when I add any character in search field corresponding content from store should be display.
Currently I am using search field on which I am handling keyup event.
Please let me know the flow and guide line to do this.
Thanks
I suppose you want a search feature for your search field..showing the results as you type. You can achieve this by using regular expressions and compare them with entries in the store.
Here's the code I used on a project:
//referencing my searchfield
Search: '#searchfield';
//attaching an event
Search: {
keyup: "OnFocus"
}
//the actual function
OnFocus: function (searchField, e) {
var query = searchField.getValue(); //get the value entered in the search field
var ContactsContainer = this.getContactsContainer(); //the container that holds my contacts
var store = Ext.getStore('Contacts'); // the store where I have the info
store.clearFilter(); //assure there aren't any filters set
if (query) { //if the current value in the search field
var thisRegEx = new RegExp(query, 'i'); //new regular expression with our value
store.filterBy(function (record) { //filter the store
if (thisRegEx.test(record.get('name')) //the fields in the store
thisRegEx.test(record.get('surname'))
thisRegEx.test(record.get('phone'))) {
return true; //must include this
};
return false;
});
}
Good Luck!
I need help in dynamically adding/removing route in Durandal Router. What I want is after user is logged in then I would be able to add or remove specific route depending upon logged in user's type.
I tried to add/remove route from visibleRoutes/allRoutes array ... but get binding exception from knockout library...
I was hoping it would be common scenario... but still couldn't find any solution ... please help me in fixing this issue.
Thanks.
Wasim
POST COMMENTS:
I tried this function to dynamically hide/show route... and similary tried to add/remove route from allRoutes[] ... but then get exception on knockout bidning
showHideRoute: function (url,show) {
var routeFounded = false;
var theRoute = null;
$(allRoutes()).each(function (route) {
if (url === this.url) {
routeFounded = true;
var rt = this;
theRoute = rt;
return false;
}
});
if (routeFounded)
{
if (show)
{
visibleRoutes.push(theRoute);
}
else
{
visibleRoutes.remove(theRoute);
}
}
}
In Durandal 2.0.
You can enumerate the routes to find the one you wish to show/hide.
Then change the value of: nav property
Then run buildNavigationModel();
here is an example:
// see if we need to show/hide 'flickr' in the routes
for (var index in router.routes) {
var route = router.routes[index];
if (route.route == 'flickr') {
if (vm.UserDetail().ShowFlickr) { // got from ajax call
// show the route
route.nav = true; // or 1 or 2 or 3 or 4; to have it at a specific order
} else if (route.nav != false) {
route.nav = false;
}
router.buildNavigationModel();
break;
}
}
Durandal 2.0 no longer has the method visibleRoutes. I found that the following works for me.
router.reset();
router.map([
{ route: 'home', moduleId: 'home/index', title: 'Welcome', nav: true },
{ route: 'flickr', moduleId: 'flickr/index', title: '', nav: true }
])
.buildNavigationModel()
.mapUnknownRoutes('home/index', 'not-found');
This removes all previous routes, if you want to maintain current routes you could try using the router.routes property to rebuild the array of routes.
I had a similar requirement. If I were you, I would take another approach. Rather than adding/removing routes when application loads - get the right routes to begin with per user type.
Two options, (I use both)
1) have a json service provide the proper routes per user type, this approach would be good if you need to 'protect/obscure' routes... i.e. I don't want the route referenced on any client resource.
2) A simpler solution see Durandal.js: change navigation options per area
You can have a settings property identify the user type.
I hope this helps.
I had a similar problem: First, router.visibleRoutes() is an observable array. In other words, when you change its value, the routes automatically update. However, the items in this array are not observable, so to make a change you need to replace the entire array and not just make a change to a single item in it.
So, all you have to do is find which item in this array you want to remove, and then create a new array without this item, and set router.visibleRoutes() to this new array.
If, for example, you find out the it is the 3rd item, then one way of doing it is:
router.visibleRoutes(router.visibleRoutes().splice(2, 1))
Note that splice() returns a new array where an item is removed. This new array is put into router.visibleRoutes.
We have an address field we want to provide typeahead for. It sits behind a login, although if we needed to we could get crafty and make that one page public for licensing compliance.
The Google Maps API is getting locked down. We used to use the "reverse geocode" portion of it to perform partial address search / typeahead for addresses - so for example if the user typed:
1600 Penn
I could hit the service and get back several suggestions, like:
1600 Pennsylvania Avenue, Washington, DC
There are several other partial address searches out there I've come across but they each have problems.
Google: $10000/yr minimum for just 7500 requests/day - ridiculous
Yahoo: Shutdown this year
Bing: Requires the page to be public / not behind login. This isn't a hard stop for us, but it would be a challenging redesign of how the page works.
Mapquest OpenStreetMap API: Searches for the exact string rather than a leading string - so it returns Penn Station instead of 1600 Pennsylvania Ave.
Mapquest OpenStreetMap data: We could download all of this and implement our own, but the CPU and data requirements would likely be too much to bite off for the time being.
We're fine with paying for usage, we'd just seek a solution closer to Amazon's $0.01/10000 requests than Google's.
UPDATE: My original answer, intact below, was given before SmartyStreets had an address autocomplete offering, which is free with a LiveAddress API subscription.
It's simple, really. The USPS has approved certain vendors for address verification/standardization services. I work for one such company, SmartyStreets, and in my experience, what you are trying to do is probably easier than you think with a good API that has a REST endpoint. Look into the LiveAddress API.
Submit a street address and at least a city/state, or zip, or both, and you'll get a list of suggested results.
NOTICE, however, that non-CASS providers such as Google Maps do address approximation, not address validation. Google - and others - make a "best guess" which is what Google and them are expert at. If you want actual existing addresses, make sure you find a service that does just that. I'll add that LiveAddress is free now and offers better performance than, for example, the USPS API.
How comes that doing it yourself isn't an option? IMO a partial search or a typeahead isn't so hard to do with a ternary trie on the address, street, city etc.pp field.
Google has released Google Places Autocomplete which resolves exactly this problem.
At the bottom of a page throw this in there:
<script defer async src="//maps.googleapis.com/maps/api/js?libraries=places&key=(key)&sensor=false&callback=googPlacesInit"></script>
Where (key) is your API key.
We've set our code up so you mark some fields to handle typeahead and get filled in by that typeahead, like:
<input name=Address placeholder=Address />
<input name=Zip placeholder=Zip />
etc
Then you initialize it (before the Google Places API has loaded typically, since that's going to land async) with:
GoogleAddress.init('#billing input', '#shipping input');
Or whatever. In this case it's tying the address typeahead to whatever input has name=Address in the #billing tag and #shipping tag, and it will fill in the related fields inside those tags for City, State, Zip etc when an address is chosen.
Setup the class:
var GoogleAddress = {
AddressFields: [],
//ZipFields: [], // Not in use and the support code is commented out, for now
OnSelect: [],
/**
* #param {String} field Pass as many arguments as you like, each a selector to a set of inputs that should use Google
* Address Typeahead via the Google Places API.
*
* Mark the inputs with name=Address, name=City, name=State, name=Zip, name=Country
* All fields are optional; you can for example leave Country out and everything else will still work.
*
* The Address field will be used as the typeahead field. When an address is picked, the 5 fields will be filled in.
*/
init: function (field) {
var args = $.makeArray(arguments);
GoogleAddress.AddressFields = $.map(args, function (selector) {
return $(selector);
});
}
};
The script snippet above is going to async call into a function named googPlacesInit, so everything else is wrapped in a function by that name:
function googPlacesInit() {
var fields = GoogleAddress.AddressFields;
if (
// If Google Places fails to load, we need to skip running these or the whole script file will fail
typeof (google) == 'undefined' ||
// If there's no input there's no typeahead so don't bother initializing
fields.length == 0 || fields[0].length == 0
)
return;
Setup the autocomplete event, and deal with the fact that we always use multiple address fields, but Google wants to dump the entire address into a single input. There's surely a way to prevent this properly, but I'm yet to find it.
$.each(fields, function (i, inputs) {
var jqInput = inputs.filter('[name=Address]');
var addressLookup = new google.maps.places.Autocomplete(jqInput[0], {
types: ['address']
});
google.maps.event.addListener(addressLookup, 'place_changed', function () {
var place = addressLookup.getPlace();
// Sometimes getPlace() freaks out and fails - if so do nothing but blank out everything after comma here.
if (!place || !place.address_components) {
setTimeout(function () {
jqInput.val(/^([^,]+),/.exec(jqInput.val())[1]);
}, 1);
return;
}
var a = parsePlacesResult(place);
// HACK! Not sure how to tell Google Places not to set the typeahead field's value, so, we just wait it out
// then overwrite it
setTimeout(function () {
jqInput.val(a.address);
}, 1);
// For the rest, assign by lookup
inputs.each(function (i, input) {
var val = getAddressPart(input, a);
if (val)
input.value = val;
});
onGoogPlacesSelected();
});
// Deal with Places API blur replacing value we set with theirs
var removeGoogBlur = function () {
var googBlur = jqInput.data('googBlur');
if (googBlur) {
jqInput.off('blur', googBlur).removeData('googBlur');
}
};
removeGoogBlur();
var googBlur = jqInput.blur(function () {
removeGoogBlur();
var val = this.value;
var _this = this;
setTimeout(function () {
_this.value = val;
}, 1);
});
jqInput.data('googBlur', googBlur);
});
// Global goog address selected event handling
function onGoogPlacesSelected() {
$.each(GoogleAddress.OnSelect, function (i, fn) {
fn();
});
}
Parsing a result into the canonical street1, street2, city, state/province, zip/postal code is not trivial. Google differentiates these localities with varying tags depending on where in the world you are, and as a warning, if you're used to US addresses, there are places for example in Africa that meet none of your expectations of what an address looks like. You can break addresses in the world into 3 categories:
US-identical - the entire US and several countries that use a similar addressing system
Formal addresses - UK, Australia, China, basically developed countries - but the way their address parts are broken up/locally written has a fair amount of variance
No formal addresses - in undeveloped areas there are no street names let alone street numbers, sometimes not even a town/city name, and certainly no zip. In these locations what you really want is a GPS location, which is not handled by this code.
This code only attempts to deal with the first 2 cases.
function parsePlacesResult(place) {
var a = place.address_components;
var p = {};
var d = {};
for (var i = 0; i < a.length; i++) {
var ai = a[i];
switch (ai.types[0]) {
case 'street_number':
p.num = ai.long_name;
break;
case 'route':
p.rd = ai.long_name;
break;
case 'locality':
case 'sublocality_level_1':
case 'sublocality':
d.city = ai.long_name;
break;
case 'administrative_area_level_1':
d.state = ai.short_name;
break;
case 'country':
d.country = ai.short_name;
break;
case 'postal_code':
d.zip = ai.long_name;
}
}
var addr = [];
if (p.num)
addr.push(p.num);
if (p.rd)
addr.push(p.rd);
d.address = addr.join(' ');
return d;
}
/**
* #param input An Input tag, the DOM element not a jQuery object
* #paran a A Google Places Address object, with props like .city, .state, .country...
*/
var getAddressPart = function(input, a) {
switch(input.name) {
case 'City': return a.city;
case 'State': return a.state;
case 'Zip': return a.zip;
case 'Country': return a.country;
}
return null;
}
Old Answer
ArcGis/ESRI has a limited typeahead solution that's functional but returns limited results only after quite a bit of input. There's a demo here:
http://www.esri.com/services/disaster-response/wildlandfire/latest-news-map.html
For example you might type 1600 Pennsylvania Ave hoping to get the white house by the time you type "1600 Penn", but have to get as far as "1600 pennsylvania ave, washington dc" before it responds with that address. Still, it could have a small benefit to users in time savings.
dojo newbie - giving it a shot.
After submitting a form, If an error is returned from the server I would like to show that message on the dijit.form.ValidationTextBox
var user_email = dijit.byId("login_user_email");
user_email.set("invalidMessage", data["result"]["user_email"]);
//need to force show the tooltip but how???
Any help much appreciated.
See it in action at jsFiddle.
Just show tooltip:
var textBox = bijit.byId("validationTextBox");
dijit.showTooltip(
textBox.get("invalidMessage"),
textBox.domNode,
textBox.get("tooltipPosition"),
!textBox.isLeftToRight()
);
Temporarily switch textBox validator, force validation, restore the original validator:
var originalValidator = textBox.validator;
textBox.validator = function() {return false;}
textBox.validate();
textBox.validator = originalValidator;
Or do both at once.
I think you can show the tooltip via myVTB.displayMessage('this is coming from back end validation'); method
you need to do the validation in the validator-method. like here http://docs.dojocampus.org/dijit/form/ValidationTextBox-tricks
you also need to focus the widget to show up the message! dijit.byId("whatever").focus()
#arber solution is the best when using the new dojo. Just remember to set the focus to the TextBox before calling the "displayMessage" method.
I am using dojo 1.10 which works create as follows:
function showCustomMessage(textBox, message){
textBox.focus();
textBox.set("state", "Error");
textBox.displayMessage(message);
}
Dojo reference guid for ValidationTextBox: https://dojotoolkit.org/reference-guide/1.10/dijit/form/ValidationTextBox.html
I know this question is ancient, but hopefully this'll help someone. Yes, you should use validators, but if you have a reason not to, this will display the message and invalidate the field:
function(textbox, state /*"Error", "Incomplete", ""*/, message) {
textbox.focus();
textbox.set("state", state);
textbox.set("message", message);
}
You can call directly the "private" function:
textBox._set('state', 'Error');
You get the same result as #phusick suggested but with less code and arguably in a more direct and clean way.
Notes:
_set is available to ValidationTextBox as declared on its base class dijit/_WidgetBase.
Live demo:
http://jsfiddle.net/gibbok/kas7aopq/
dojo.require("dijit.form.Button");
dojo.require("dijit.form.ValidationTextBox");
dojo.require("dijit.Tooltip");
dojo.ready(function() {
var textBox = dijit.byId("validationTextBox");
dojo.connect(dijit.byId("tooltipBtn"), "onClick", function() {
dijit.showTooltip(
textBox.get('invalidMessage'),
textBox.domNode,
textBox.get('tooltipPosition'), !textBox.isLeftToRight()
);
});
dojo.connect(dijit.byId("validatorBtn"), "onClick", function() {
// call the internal function which set the widget as in error state
textBox._set('state', 'Error');
/*
code not necessary
var originalValidator = textBox.validator;
textBox.validator = function() {return false;}
textBox.validate();
textBox.validator = originalValidator;
*/
});
});