I am new to GeoFire, FireBase and Angular. I am trying to create a function that will take some coordinates and return some objects in vicinity of those coordinates.
I return a promise from the function which I assign to a scope variable used in the view hoping that when the promise is resolved by the ready event the array of objects in vicinity will be available.
obj.findGroupsInViscinity = function(pos){
var gFire = factoryAuth.geoFire;
var fbaseRef = factoryAuth.usersRef;
var groupsInQuery = {};
var groupsInQueryAr = [];
var deferred = $q.defer();
var geoQuery = gFire.query({
center: [pos.coords.latitude, pos.coords.longitude],
radius: 2
})
geoQuery.on("key_entered", function(groupId, groupLocation, distance) {
console.log("--> key_entered 1");
groupsInQuery[groupId] = true;
// Look up the vehicle's data in the Transit Open Data Set
fbaseRef.child("groups").child(groupId).once("value", function(dataSnapshot) {
console.log("--> key_entered 2");
// Get the vehicle data from the Open Data Set
group = dataSnapshot.val();
// If the vehicle has not already exited this query in the time it took to look up its data in the Open Data
// Set, add it to the map
if (group !== null && groupsInQuery[groupId] === true) {
console.log("Adding group", group);
// Add the group to the list of groups in the query
groupsInQuery[groupId] = group;
groupsInQueryAr.push({"name": group.name});
}
})
}) // end ke_entered monitoring
geoQuery.on("ready", function() {
console.log("GeoQuery ready event received. groupsInQueryAr = ", groupsInQueryAr);
deferred.resolve(groupsInQueryAr);
geoQuery.cancel();
console.log("GeoQuery canceled");
}) // Cacnel the geoQuery once we have gotten all the groups in viscinity
return deferred.promise; // Return a promise that will be resolved when ready event fires
}
Included below the console output from calling this function.
What I notice is that the key_entered part of the code is called twice in succession but before the code to process the key_entered event completes, the ready event is called because all key_entered events have fired. So while the key_entered part of the logic is building out the array I want to pass in resolving the promise, it is not ready at the time I resolve the promise in the ready event.
How can I ensure that I resolve the promise after all key_entered events have been processed and my array of objects has been built out properly?
Thanks,
Sanjay.
I would say that this is a bit of an XY problem and I would suggest you just load the restaurants into your view as you get them. This will probably be a better user experience in most cases.
That being said, if you want to do what you are asking about, you can make it work by using $.all(). Essentially, create and return your deferred promise. Start with an empty list and for every key_entered event, push a new promise onto the list. Then, in your ready event callback, do a $q.all() on the list of promises and once they complete (in the then() of the promise), do the deferred.resolve().
Related
Angular js function updating some record. After updating record i am calling search method to show data on view.
But record does not updated before that search method call that does not get data so show null on view.
I have separate button for search on its ng-click this search method call. After some second if i click that button it shows data on view.
my code is,
vm.Update = function (value)
{
var test = value;
searchCriteria = {
From: vm.From,
To: vm.To,
Region: vm.Region,
City: vm.SelectedCity
}
surveyService.UpdateVisit(searchCriteria,value).then(function (d) {
var Confrm = JSON.parse(d.data.data);
if (d.data.status) {
toastr.success(Updated, {
autoDismiss: false
});
}
else {
toastr.error(errorMsg);
}
});
vm.searchVisit(0);
}
This searchvisit call and service unable to update data in database so i do not get any record on view. When i call this searchvisit method from separate button for searching it shows record with updated data.
Hopes for your suggestions how to pause execution before calling searchvisit method or any alternative that it gets any response than move execution control to searchvisit method.
Thanks
This is due to the asynchronous nature in JS.
From your code, surveyService.UpdateVisit(searchCriteria,value) returns a promise. Thus, when vm.searchVisit(0); is called, surveyService.UpdateVisit(searchCriteria,value) has not been resolved yet, meaning updating is still in progress and have not been completed. There for vm.searchVisit(0); shows records that are not updated.
If your second function is dependent on the values of the first function call, please add it as shown below inside the success callback.
surveyService.UpdateVisit(searchCriteria,value).then(function (d) {
var Confrm = JSON.parse(d.data.data);
if (d.data.status) {
toastr.success(Updated, {
autoDismiss: false
});
}
else {
toastr.error(errorMsg);
}
//Add this here.
vm.searchVisit(0);
});
Simple question here.
I have this watch:
// Watch our model
$scope.$watch(function () {
// Watch our team name
return self.model.team.data.name;
}, function (name) {
console.log(name);
// if we have a name
if (name) {
// Store our model in the session
sessionStorage.designer = angular.toJson(self.model);
}
});
The team model is pull in from the database as a promise (hence the data) so when the watch first fires self.model.team has not been set so it is null.
How can I get my watch to either wait until it has been set or add a check into the return function of the watch?
Use a watch expression instead of a function. This will catch any errors with missing objects and return undefined.
// Watch our model
$scope.$watch('self.model.team.data.name', function (name) {
console.log(name);
// if we have a name
if (name) {
// Store our model in the session
sessionStorage.designer = angular.toJson(self.model);
}
});
There is no magic here - if one of the variables you are accessing could be null/undefined, then you cannot get its property if it's null/undefined. So, you have to guard against that:
$scope.$watch(
function(){
return (self.model.team && self.model.team.data.name) || undefined;
},
function(v){
// ...
});
The only "magic" is when you "$watch" for expressions, but the expressions need to be exposed on the scope. So, you could do:
$scope.model = self.model;
$scope.$watch("model.team.data.name", function(v){
// ...
});
But, really, you have to ask yourself why you need a $watch here to begin with. It seems to me that you are getting the team asynchronously once - it does not look like it will change except by maybe another async call. So, just handle that when you receive the data without the $watch:
someSvc.getTeam() // I made an assumption about a service that pulls the data from db
.then(function(team){
var name = team.data.name;
// if we have a name
if (name) {
// Store our model in the session
sessionStorage.designer = angular.toJson(self.model);
}
});
An unnecessary $watch is expensive - it is evaluated on every digest cycle, so, it's best to reduce the number of $watchers.
I tried to set up a simple app to show the latest message of each member.
At first, it loads the members array.Then I call a function refreshMsg to loop through the array.
Within the loop, I set an timer on it.
However it did not work at all.Could someone five me an hint?
Many thanks.
demo
//show the message for each member and set an timer to refesh
function refreshMsg(){
//loop through members array and set a timer
for(var i=0;i<$scope.members.length;i++){
var cur_member=$scope.members[i];
var name = cur_member.name;
var url_getMsg = "api/getMsg/".name;
var timer = $timeout( function refresh(){
$http.get(url_getMsg).success(function(data){
cur_member.msg=data;
}
)
timer = $timeout(refresh, 3000);
}, 3000);
}
}
})
One of the reasons it is not working correctly is that due to async nature of the call the cur_member would not match the response message as the loop would have moved ahead. The way to do this would be to create another method specifically for setting the $timeout which would create a closure. Better still use $interval
function refreshMsg(){
//loop through members array and set a timer
for(var i=0;i<$scope.members.length;i++){
var cur_member=$scope.members[i];
var name = cur_member.name;
setupRefresh(curr_member);
}
function setupRefresh(member) {
var url_getMsg = "api/getMsg/"+member.name;
var timer = $interval( function(){
$http.get(url_getMsg).success(function(data){
member.msg=data;
}
);
}, 3000);
}
Also remember to dispose all the interval when scope is destroyed.
Im working on an extjs application. We're have a page that is for looking at a particular instance of an object and viewing and editing it's fields.
We're using refs to get hold of bits of view in the controller.
This was working fine, but I've been sharding the controller into smaller pieces to make it more managable and realised that we are relying on a race condition in our code.
The logic is as follows:
Initialise the controller
parse the url to extract the id of the object
put in a call to load the model with the given view.
in the load callback call the controller load method...
The controller load method creates some stores which fire off other requests for bits of information using this id. It then uses some of the refs to get hold of the view and then reconfigures them to use the stores when they load.
If you try and call the controller load method immediately (not in the callback) then it will fail - the ref methods return undefined.
Presumably this is because the view doesnt exist... However we aren't checking for that - we're just relying on the view being loaded by the time the server responds which seems like a recipe for disaster.
So how can we avoid this and be sure that a view is loaded before trying to use it.
I haven't tried rewriting the logic here yet but it looks like the afterrender event probably does what I want.
It seems like waiting for both the return of the store load and afterrender events should produce the correct result.
A nice little abstraction here might be something like this:
yourNamespace.createWaitRunner = function (completionCallback) {
var callback = completionCallback;
var completionRecord = [];
var elements = 0;
function maybeFinish() {
var done = completionRecord.every(function (element) {
return element === true
});
if (done)
completionCallback();
}
return {
getNotifier: function (func) {
func = func || function (){};
var index = elements++;
completionRecord[index] = false;
return function () {
func(arguments);
completionRecord[index] = true;
maybeFinish();
}
}
}
};
You'd use it like this:
//during init
//pass in the function to call when others are done
this.waiter = yourNamespace.createWaitRunner(controller.load);
//in controller
this.control({
'SomeView': {
afterrender: this.waiter.getNotifier
}
});
//when loading record(s)
Ext.ModelManager.getModel('SomeModel').load(id, {
success: this.waiter.getNotifier(function (record, request) {
//do some extra stuff if needs be
me.setRecord(record);
})
});
I haven't actually tried this out yet so it might not be 100% but I think the idea is sound
I was wondering about the best pattern/approach here. This is a function in my router, so the user hits 'quotes/:id', but for that view to render, I need a list of their projects, customers and currencies. What would be the best way to make sure all 3 fetches() have occurred before trying to instantiate the quotesEdit view? Is it considered bad practice to grab all the information when the user clicks something?
quotesEdit: function(id) {
kf.Collections.quotes = kf.Collections.quotes || new kf.Collections.Quotes();
kf.Collections.projects = kf.Collections.projects || new kf.Collections.Projects();
kf.Collections.currencies = kf.Collections.currencies || new kf.Collections.Currencies();
//do a fetch() for the 3 above
kf.Collections.customers = kf.Collections.customers || new kf.Collections.Customers();
var quote = kf.Collections.quotes.where({Id: parseInt(id, 10)});
kf.Utils.ViewManager.swap('sectionPrimary', new kf.Views.section({
section: 'quotesEdit',
model: quote[0]
}));
}
I find a combination of jQuery deferreds and underscore's invoke method solves this elegantly:
//call fetch on the three collections, and keep their promises
var complete = _.invoke([quotes, projects, currencies], 'fetch');
//when all of them are complete...
$.when.apply($, complete).done(function() {
//all ready and good to go...
});
Promises! Specifically jQuery.when
You can do something like this:
$.when(
kf.Collections.quotes.fetch(),
kf.Collections.projects.fetch(),
kf.Collections.currencies.fetch()
).then(function(){
// render your view.
});
jQuery.ajax (and by extension backbone fetch) returns a promise and you can use $.when to set a callback function once multiple promises are resolved.
Backbone's fetch returns a jQuery Deferred object (a promise). So you can use jQuery's when function to wait for all of the promises to resolve:
quotesEdit: function(id) {
kf.Collections.quotes = kf.Collections.quotes || new kf.Collections.Quotes();
kf.Collections.projects = kf.Collections.projects || new kf.Collections.Projects();
kf.Collections.currencies = kf.Collections.currencies || new kf.Collections.Currencies();
//do a fetch() for the 3 above
var quotePromise = kf.Collections.quotes.fetch();
var projectsPromise = kf.Collections.projects.fetch();
var currenciesPromise = kf.collections.currencies.fetch();
// wait for them to all return
$.when(quotePromise, projectsPromise, currenciesPromise).then(function(){
// do stuff here, now that all three have resolved / returned
kf.Collections.customers = kf.Collections.customers || new kf.Collections.Customers();
var quote = kf.Collections.quotes.where({Id: parseInt(id, 10)});
kf.Utils.ViewManager.swap('sectionPrimary', new kf.Views.section({
section: 'quotesEdit',
model: quote[0]
}));
};
}
I've written a bit about promises and jQuery's when, here:
http://lostechies.com/derickbailey/2012/03/27/providing-synchronous-asynchronous-flexibility-with-jquery-when/
http://lostechies.com/derickbailey/2012/07/19/want-to-build-win8winjs-apps-you-need-to-understand-promises/
that second link is still valid, in spite of the primary subject being Win8 JS