I am currently working with the watson chatbot conversation application. i ran into a situation where i have to refresh a context, just at a certain stage depending on the user input, but i haven't been able to achieve this.
I tried setting the context = {}, if the user input isn't in the Array, but it seems to check the context at every stage of the Application.
if(context.city !== array[i]['city']){
context = {};
}
what i am asking the user is to input a list of cities, which is in an Array. If the user inputs a location that isn't in the Array first, it let's the user know that it isn't in the Array. If the user inputs a city that is not in the Array after inputing a city that is already in the array, it is returning the last context.
for example,
City = Boston.
You just input Boston, price is 20.
Then if i do City = Atlanta(Bearing in mind Atlanta is not in the Array)
it does city is Atlanta, price is 20.
Any idea how to fix this, on any steps i can follow to stop this from happening?
for(var i = 0; i < array.length; i++){
if (context.city !== array[i]['city']){
if(inputBox.value === context.city)
{
context = latestResponse.context;
context.rate = rate;
context.currency = currency;
}
else
{
context.city = "";
context.rate = "";
context.currency = "";
}
}
}
For someone else that might be going through the same problem now or in the future,
This was what solved it for me. The information needed was in an Array, so i looped over it, and checked if the context.city != array.
You can set context.city to empty string or null in your app or in advanced editor
and then in your app fix logic based on that
Related
I need to update a collection in values like this :
{
"email" : "x#gmail.com",
"fullName" : "Mehr",
"locations" : ["sss","dsds","adsdsd"]
}
Locations needs to be an array. in firebase how can I do that ... and also it should check duplicated.
I did like this :
const locations=[]
locations.push(id)
firebase.database().ref(`/users/ + ${userId}`).push({ locations })
Since you need to check for duplicates, you'll need to first read the value of the array, and then update it. In the Firebase Realtime Database that combination can is done through a transaction. You can run the transaction on the locations node itself here:
var locationsRef = firebase.database().ref(`/users/${userId}/locations`);
var newLocation = "xyz";
locationsRef.transaction(function(locations) {
if (locations) {
if (locations.indexOf(newLocation) === -1) {
locations.push(newLocation);
}
}
return locations;
});
As you can see, this loads the locations, ensures the new location is present once, and then writes it back to the database.
Note that Firebase recommends using arrays for set-like data structures such as this. Consider using the more direct mapping of a mathematical set to JavaScript:
"locations" : {
"sss": true,
"dsds": true,
"adsdsd": true
}
One advantage of this structure is that adding a new value is an idempotent operation. Say that we have a location "sss". We add that to the location with:
locations["sss"] = true;
Now there are two options:
"sss" was not yet in the node, in which case this operation adds it.
"sss" was already in the node, in which case this operation does nothing.
For more on this, see best practices for arrays in Firebase.
you can simply push the items in a loop:
if(locations.length > 0) {
var ref = firebase.database().ref(`/users/ + ${userId}`).child('locations');
for(i=0; i < locations.length; i++) {
ref.push(locations[i]);
}
}
this also creates unique keys for the items, instead of a numerical index (which tends to change).
You can use update rather than push method. It would much easier for you. Try it like below
var locationsObj={};
if(locations.length > 0) {
for(i=0; i < locations.length; i++) {
var key= firebase.database().ref(`/users/ + ${userId}`).child('locations').push().key;
locationsObj[`/users/ + ${userId}` +'/locations/' + key] =locations[i];
}
firebase.database().ref().update(locationsObj).then(function(){// which return the promise.
console.log("successfully updated");
})
}
Note : update method is used to update multiple paths at a same time. which will be helpful in this case, but if you use push in the loop then you have to wait for the all the push to return the promises. In the update method it will take care of the all promises and returns at once. Either you get success or error.
I'm sure I'm doing something wrong... but every time I query on a calculated datasource, I get the error "cannot handle returning cyclic object."
Here's the gist:
I have a calculated model that fetches a user's google contacts and places the full name field into a table on the UI. The goal is to have a separate text box that can be used to search the full name field and then repopulate the table on the same page with the results of the search, similar to how google contacts search behavior works. The on value change event of the text box sends the textbox value to this server script:
function searchContacts (sq) {
var ds = app.models.Contacts.newQuery();
ds.filters.FullName._contains = sq;
var results = ds.run();
return results;
}
But every time I get the cyclic object error when the values are returned from that function. The error actually fires when the query run command (ds.run) is executed.
I've tried querying the datasource as well, but I've read somewhere that you can't query the datasource of a calculated model because it doesn't exist, so you have to query the model.
Any help would be much appreciated.
From your question it is not 100% clear, what you are trying to do. In case you are actually using Calculated Model, then your Server Script Query should look like this:
var sq = query.parameters.SearchQuery;
var contactsQuery = app.models.Contacts.newQuery();
contactsQuery.filters.FullName._contains = sq;
var contacts = ds.run();
var results = contacts.map(function(contact) {
var calcRecord = app.MyCalcModel.newRecord();
calcRecord.Name = contact.FullName;
return calcRecord;
});
return results;
Note, that you cannot return objects of arbitrary type from Server Script Query, only of type of this particular Calculated Model.
But from some parts of your description and error text if feels like you are trying to load records with async serever call using google.scritp.run. In this case you cannot return App Maker records(App Script doesn't allow this) and you need to map them to simple JSON objects.
I don't think I was super-clear on my original post.
I have a calculated model that is all of the user's contacts from Google Contacts (full name, email, mobile, etc...) On the UI I have a list widget that's populated with all of the Full Name fields and above the list widget a text input that's used to search the list widget. So the search text box's on input change event sends a request to query the Full Names, similar to how Google Contact's search feature works.
Screen Shot
It appears that App Maker doesn't let you query calculated models, so I have this workaround - unless someone comes up with something better:
This is the onInputChange handler for the search text box:
sq = app.pages.SelectClient.descendants.TextBox1.value;
app.datasources.SearchContacts.query.parameters.Name = sq;
app.datasources.SearchContacts.load();
This is the Server Script Code (thanks to #Pavel Shkleinik for the heads up):
var sq = query.parameters.Name;
if (sq !== null) {
return getContactsbyName(sq);
} else {
return getContacts();
}
And the server code with no query:
function getContacts() {
var results = [];
var contacts = ContactsApp.getContacts();
contacts.forEach(function(item) {
var contact = app.models.Contacts.newRecord();
contact.FullName = item.getFullName();
var emails = item.getEmails(ContactsApp.Field.WORK_EMAIL);
if (emails.length > 0) {
contact.PrimaryEmail = emails[0].getAddress();
}
contact.LastName = item.getFamilyName();
contact.FirstName = item.getGivenName();
var phones = item.getPhones(ContactsApp.Field.MOBILE_PHONE);
if (phones.length > 0) {
contact.Mobile = phones[0].getPhoneNumber();
}
var addresses = item.getAddresses(ContactsApp.Field.WORK_ADDRESS);
if (addresses.length > 0) {
contact.Address = addresses[0].getAddress();
}
results.push(contact);
results.sort();
});
return results;
}
And with the query:
function getContactsbyName(sq) {
var results = [];
var contacts = ContactsApp.getContactsByName(sq);
contacts.forEach(function(item) {
var contact = app.models.Contacts.newRecord();
contact.FullName = item.getFullName();
var emails = item.getEmails(ContactsApp.Field.WORK_EMAIL);
if (emails.length > 0) {
contact.PrimaryEmail = emails[0].getAddress();
}
contact.LastName = item.getFamilyName();
contact.FirstName = item.getGivenName();
var phones = item.getPhones(ContactsApp.Field.MOBILE_PHONE);
if (phones.length > 0) {
contact.Mobile = phones[0].getPhoneNumber();
}
var addresses = item.getAddresses(ContactsApp.Field.WORK_ADDRESS);
if (addresses.length > 0) {
contact.Address = addresses[0].getAddress();
}
results.push(contact);
results.sort();
});
return results;
}
This way, the list populates with all of the names when there's no search query present, and then re-populates with the search query results as needed.
The only issue is that the call to the Google Contacts App to populate the Calculated Model is sometimes very slow.
I have an address search box using the google map autocomplete library:
var autocompleter = new google.maps.places.Autocomplete(item);
There is an odd occasion that an address only returns a name attribute:
Object {name: "138 Manukau Road, Pukekohe, New Zealand"}
But other addresses are giving more data, such as:
Object {address_components: Array[7], adr_address: "<span class="street-address">430 Queen St</span>, …n>, <span class="country-name">New Zealand</span>", formatted_address: "430 Queen St, Auckland, Auckland 1010, New Zealand", geometry: Object, icon: "http://maps.gstatic.com/mapfiles/place_api/icons/geocode-71.png"…}address_components: Array[7]adr_address: "<span class="street-address">430 Queen St</span>, <span class="extended-address">Auckland</span>, <span class="locality">Auckland</span> <span class="postal-code">1010</span>, <span class="country-name">New Zealand</span>"formatted_address: "430 Queen St, Auckland, Auckland 1010, New Zealand"geometry: Objecthtml_attributions: Array[0]icon: "http://maps.gstatic.com/mapfiles/place_api/icons/geocode-71.png"id: "00fce9b1c43ac960068949cbf32eecb587b0b020"name: "430 Queen St"place_id: "ChIJQfHW8OVHDW0RyHgQRLy8fKc"reference: "CqQBlgAAAIDnkWNQ4cmU624FV6l_bAxmI27czZoytmzrrEWVaXgR5LcZuFqt1cL3WIMzoWhmZNhftRzhLUVwpFjqmw3qwKIqugj02HrvU5x6PtUvepPNPV-08pin_PvRU-__mMMH3N2vILIOLM_AnYFMqNG5MArF4ChZXJxZj6vk7PI3ORJe1W6QjIXoPgesL379E4WUCjrZ0fjv3KgqzB-G4f-8A5MSEN5S47-QZqkY5sl37cIQFWQaFLg4InSVLpYGg8n1gGO958TcA4UK"scope: "GOOGLE"types: Array[1]url: "https://maps.google.com/maps/place?q=430+Queen+St,+Auckland,+Auckland+1010,+New+Zealand&ftid=0x6d0d47e5f0d6f141:0xa77cbcbc441078c8"vicinity: "Auckland"__proto__: Object
I have found a similar issue which raised by someone in 2012, and looks like it has not been attended.
This problem was happening intermittently for me and usually out of the blue.
Turns out that if you get a result from Autocomplete with only a name property, you can use the google.maps.places.AutocompleteService to finish the job.
For example, call this if you only get a name back (sending the input element in el)
function getPlace(result, el, callback) {
var autocompleteService = new google.maps.places.AutocompleteService();
if (result.name.length > 0) {
var d = { input: result.name, offset: result.name.length };
autocompleteService.getPlacePredictions(d, function (list, status) {
if (list == null || list.length == 0) callback(null);
var placesService = new google.maps.places.PlacesService(el);
var ref = { 'reference': list[0].reference }
placesService.getDetails(ref, function (detailsResult, placesServiceStatus) {
if (placesServiceStatus == google.maps.GeocoderStatus.OK) {
callback(detailsResult);
}
else {
callback(null);
}
});
});
}
}
This helped me a lot
http://plnkr.co/edit/GF3nM3XfYX9El2w11pGo?p=preview
Surprisingly the very same address now returning correct data, google must be watching these errors and fix them as soon as possible.
Maybe the google service is not able to geocode your address and only return to you the name of the address found. The reason could be that the service has no aditional data to give you for those adress.
You can just not show those incomplete address to the user or try to geocode that address by another service like OSM Geocoder
http://wiki.openstreetmap.org/wiki/Nominatim
From Google Maps Autocomplete Reference:
Returns the details of the Place selected by user if the details were
successfully retrieved. Otherwise returns a stub Place object, with
the name property set to the current value of the input field.
So the answer is that the getPlace method is just failing for specific places. Not sure how to solve this, but gets us one step closer to the answer.
EDIT: FIXED IT!
For my app, I'm loading multiple libraries for google maps (geometry and places). Switching the order that the libraries were loaded fixed the issue, and I don't know why.
Change:
maps.googleapis.com/maps/api/js?v=3&key=[KEY]&libraries=geometry,places
to
maps.googleapis.com/maps/api/js?v=3&key=[KEY]&libraries=places,geometry
I'm wondering if given example could meet racing condition on a real server, does work on localhost. Given situation- user deletes a few person from a form ( lets say studyForm and studyPersons ) Ive added personToDelete to an array an later on- Edit Action im deleting them in a loop
// A punch of persons chosen to be deleted
self.markedForDeleted.forEach(function(trashed){
trashed.destroyRecord();
});
this.get('person').save().then(personEditSuccess, personEditFailure);
Should i edit it like this ?
var onDeleteSuccess = function(wasAnyToDelete){
if (wasAnyToDelete){
deletedPersons ++;
}
if(deletedPersons === lengthOfAllPersons) {
this.get('person').save().then(personEditSuccess, personEditFailure);
}
};
// A punch of persons chosen to be deleted
self.markedForDeleted.forEach(function(trashed){
trashed.destroyRecord().then(onDeleteSuccess, onDeleteFailure);
});
Working and currently using
Ember.RSVP.all(personsToDelete.invoke('destroyRecord')).then(deletePersonSuccess, deletePersonFailure);
i have a question about using the waypoint feature ,i took the script from the User Geocodezip; very big thanks for that ,and it work well ,but
i can only route with waypoint ,i would like to have the waypoints as an option,but if the User let this field empty there is no funktion!?
does anyone have an idea???
if (waypts.options[i].selected == true) {
i insert this line, to Element "waypts" becoming not a mandatory field, but now whole scripts have no funktion.
for (var i = 0; i < waypointstring.length; i++) {
if (waypts.options[i].selected == true) {
waypts.push({location:waypointstring[i], stopover:true});
}
}
Finally found the solution :
var address = waypointstring[i];
if (address !== "") {
here is the whole source example