Watching Firebase query in angularjs - javascript

I'm building an application in Angular with Firebase and one aspect of it is one-to-one chat. I'm querying Firebase to see if a chat room exists already between the user currently accessing the application and the user they are attempting to chat with. If it exists, I am trying to apply that room to the scope as the selected room. I'm using "Messages" service to run the query.
this.roomQuery = function(user1ID, user2ID) {
roomsRef.orderByChild("user1").equalTo(user1ID).on("child_added", function(snapshot) {
if (snapshot.val().user2 == user2ID) {
self.selectedRoom = snapshot.key();
console.log(self.selectedRoom);
} else {
self.selectedRoom = null;
}
})
}
and in my controller I am using:
$scope.$watch(
function(){ return Messages.selectedRoom },
function(newValue, oldValue) {
$scope.selectedRoom = newValue;
}
)
This $scope.$watch method has worked for me with everything else and it seems to sometimes work in this case. The console log always prints out the correct value for Messages.selectedRoom, but the $scope.selectedRoom sometimes does not update. Any idea what is happening here? I'm very confused. If it's logging to the console properly, shouldn't it be updated in the scope?

Angular's $digest is unaware of when a your Firebase query completes. You might find it easier to use AngularFire in this case.
this.roomQuery = function(user1ID, user2ID) {
var query = roomsRef.orderByChild("user1").equalTo(user1ID);
return $firebaseObject(query).$loaded();
};
this.roomQuery("1", "2")
.then(function(data) {
// do your check here
});
The $firebaseObject() takes in a ref or a query and knows when to call digest on your behalf.
You might want to check out using resolve in the router to inject the roomQuery into the router, since it returns a promise with .$loaded().

David got me to the solution I needed. For anyone with a similar issue, here is how I implemented it:
this.roomQuery = function(user1, user2) {
var query = roomsRef.orderByChild("user1").equalTo(user1ID)
return $firebaseArray(query).$loaded();
}
I used $firebaseArray instead of Object and in my controller:
$scope.getRoom = function() {
Messages.roomQuery($scope.user1.id, $scope.user2.$id).then(function(data)
{
$scope.data = data;
for(var i=0, len = data.length; i < len; i++){
if (data[i].user2 == $scope.user2.$id) {
$scope.selectedRoom = data[i].$id;
}
}
}
)
}
Apologies for the variable names being a little confusing. I altered them for the sake of this post.

Related

SailsJS V1 - Blueprint query not working correctly

I'm trying to create a blueprint query for a sailsjs v1 model.
The model is a BlogPost which has 2 "options". One is the target and the other one is Status.
If the target is Site and the status is Published, the query should return, otherwise nope. I'm using the default REST routes provided by Sails (blueprints) and everything works fine if I try to find all of them. However, if I try to find one by ID...I can't even get back those that have a status of 'Unpublished'.
This is my code in blueprint.js parseBlueprintOptions ->
parseBlueprintOptions: function (req) {
var queryOptions = req._sails.hooks.blueprints.parseBlueprintOptions(req);
if (queryOptions.using === 'blogpost') {
if (req.options.blueprintAction === 'create') {
queryOptions.newRecord.user_id = req.session.user.id;
return queryOptions;
} else if (req.options.blueprintAction === 'update') {
queryOptions.criteria.where.user_id = req.session.user.id;
queryOptions.valuesToSet.user_id = req.session.user.id;
return queryOptions;
} else {
if (req.session.administrator) {
return queryOptions;
} else {
queryOptions.criteria.where.blogpost_target = 'Site';
queryOptions.criteria.where.blogpost_status = 'Published';
console.log(queryOptions);
return queryOptions;
}
}
}
}
};
Any tips on why the query does not get triggered for findOne? As i said, it returns regardless of the status/target.
Hey 'find' returns object array and 'findone' returns only an object. I guess therefor it won't work!

me/friends?fields=installed returns some installed = true when have not installed

I have this query:
FB.api('/me/friends?fields=installed,name&<?php echo $access_token?>', { data: response.data }, function(res) {
}
But in the response, are few installed attribute it's true when they're not, haven't installed (because i store any registered user in my database and they are not there)
I checking like this:
if(response.data.length > 0){
for(i = 0; i < response.data.length; i++){
var guy = response.data[i];
if( guy.installed == true ) { // also tried with ===
// do stuff
}
}
}
Is there any better way to check for sure?
PS: Just thinking, this could be because they acept but something failed y my callback?
Not sure if I really got your problem, but I'd trust the FB data more then my own data concerning the installed flag. So maybe there's a glitch in storing the people which are using your app in your database?
Secondly, /me/friends should AFAIK only return those friends which are using your app, so it should be always true for those. I did a test with one of my apps, and this seems to be correct.
Thirdly, it should be sufficient to check
if (guy.installed) {
// do stuff
}

Sending data to parse.com and update angular $scope

I am sending data to a Class at parse.com, I would like to run this function and update the $scope without having to reload the view.
To create a Programme running the function below works fine, however it sometimes does not update the view following creating a new programme and it requires a page refresh (when the whole function is called as seen at the bottom - getProgrammes();
getProgrammes = function() {
$ionicLoading.show();
var programmesArray = [];
var QueryProgramme = Parse.Object.extend("Programme");
var query = new Parse.Query(QueryProgramme);
query.equalTo("userId", userId);
query.find({
success: function(results) {
for(var i=0; i < results.length; i++) {
var object = results[i];
var programmeData = { title : object.get('programmeTitle'),
id : object.id,
exercises : object.get('exerciseData')
};
programmesArray.push(programmeData);
}
$scope.programmes = programmesArray;
$scope.$apply();
$ionicLoading.hide();
},
error: function(error) {
alert('You do not have any Programmes - please create one');
}
})
};
getProgrammes();
I think I may be hacking this by using $scope.apply() - therefore making it unreliable. Assistance on the correct function to use in order to automatically update $scope would be great.
I've been using a library found here:
https://github.com/brandid/parse-angular-patch
Essentially I just reference the parse-angular.js file, and then include the dependency in my app, e.g.:
angular.module('app', [
'parse-angular',
]);
Then I just use Parse queries anytime I like and they participate correctly with $scope and $apply. The library just does some simple wrapping on the Parse methods to make the async calls obey the rules of angular.

Javascript library for real-time and offline web app

I have a back-end server with a REST api. On the front-end, I use Angular.js. To handle the real-time part, I would like to use a third-party service such as Pusher.
I'm looking for a simple library that could handle the M of the mvc part of the front-end. More specifically, I would like a Model interface that would abstract away the complexity of the offline and real-time aspects.
For instance, from the Angular side, I would like to subscribe to my Model and get notified when it changes. I would also like to have a .save() method that would handle the syncing with the server and other clients.
That library should:
Work offline: it will save the data in the local_storage and sync back with the server when it gets back online.
Listen to real-time changes, update its model and propagate the changes to the listeners.
Work well with a standard REST interface.
So, just as a quick pseudocode example, in Angular I would like to do:
my_controller = function($scope) {
User.find_all(function(users) {
$scope.users = users;
});
}
User is the model abstraction.. when it gets a real-time update, my $scope.users should change accordingly.
$scope.users[0].set('name', 'testing)
This should save the model to the server. Or, if offline, should save it locally and sync it later on when it's back online.
I know there are online services trying to accomplish that, such as Firebase and kinvey. The problem with these tools is that it only offers a hosted solution. I need to controller the REST server and the database. So, basically, I'm looking for a "Firebase" library - without all the authentications and authorizations - that could work for with a REST server and pubsub third party.
Thanks!
this is a bit long for an answer, but i don't have it published yet.
function monitor(obj, callBack){
var api={
patch: patchObjectWithDiff,
init: init,
resolve: resolve,
snapshot: snapshot,
diff: diff,
update: changeMonitor
};
function merge2(o, ob) {
for (var z in ob) {
if (ob.hasOwnProperty(z)) {
if(typeof ob[z]=="object"){
if(ob[z]==null){
delete o[z];
}else{
merge2( o[z] || {}, ob[z]);
}
}else{
o[z] = ob[z];
}
}
}
return o;
}
function snapshot(obj) {
var out = [];
function merge3(ob, path) {
path = path || [];
var tp;
for(var z in ob) {
if(ob.hasOwnProperty(z)) {
if(ob[z] && typeof ob[z] == "object" && [Date, RegExp].indexOf(ob[z].constructor) == -1) {
tp=path.concat(z);
out.push({
path: tp.join("`"),
path2: tp,
dt: "set",
date: +new Date,
v: Array.isArray(ob[z]) ? "[]" : "{}"
});
merge3(ob[z], path.concat(z));
} else {
tp=path.concat(z);
out.push({
path: tp.join("`"),
path2: tp,
type: "set",
dt: +new Date,
v: JSON.stringify(ob[z])
});
}
}
}
}
merge3(obj);
return out;
};
function diff(d1, d2){
var out=d2.filter(function(a,b,c){
var ov=JSON.stringify(a.v);
return d1.some(function(aa,bb){ return aa.path==a.path && JSON.stringify(aa.v) != ov; });
}),
// find deletions
dels=d1.filter(function(a,b,c){
return !d2.some(function(aa,bb){ if(aa.path==a.path ){ return true; }; });
}),
allPaths=dels.map(function(a){return a.path}).sort(),
dels2=dels.filter(function eliminateUnneededSubBranches(a){
var pos=allPaths.indexOf( a.path2.slice(0,-1).join("`") );
return pos==-1 || pos >= allPaths.indexOf(a.path);
}).map(function(a){a.type="del"; delete a.v; return a;});
[].push.apply(out, dels2);
//find inserts
var outNew=d2.filter(function(a,b,c){
var ov=JSON.stringify(a.v);
return !d1.some(function(aa,bb){ return aa.path==a.path });
});
[].push.apply(out, outNew);
return out.map(function(a){
var x= {
dt: a.dt,
k: a.path2
};
if(a.hasOwnProperty("v")){ x.v=a.v; }
return x;
a.k=a.path2;
delete a.path;
delete a.path2;
delete a.type;
return a;
});
}
function resolve(path, object){
var tob=object;
path.map(function(a){ return (tob=tob[a])||tob; })
return tob;
}
function patchObjectWithDiff(diff, object){
diff.forEach(function(a,b,c){
var p= resolve(a.k.slice(0,-1), object),
k= a.k.slice(-1)[0];
if(a.hasOwnProperty("v")){ //set:
p[k]=JSON.parse(a.v);
if(String(p[k]).match(/Z$/)){ p[k]=new Date(''+p[k]) || p[k]; }
}else{ // del:
if(Array.isArray(p)){ p.splice(k,1); }else{ delete p[k]; }
}
});
return object;
}
var init=snapshot(JSON.parse(JSON.stringify(obj))),
id=Math.random()+ Number(new Date());
var init=snapshot(obj);
function changeMonitor(){
var thisTime=snapshot(obj),
diffs=diff(init, thisTime);
if(diffs.length){
api.diffs=diffs;
(callBack||console.log.bind(console))("objectUpdate", diffs );
init=thisTime;
}//end if change?
}
setInterval(changeMonitor, 2500);
return api;
}
demo / example usage:
var obj={a:1, b:[1,2,3], c: false}; // a model object
var dupe=JSON.parse(JSON.stringify(obj)); // a cheap clone of the data for demo use
//subscribe this object to updates
var mon=monitor(obj, function(type, changes){console.log(type, changes); });
// make some changes to the object:
obj.e="cool!";
obj.b.push(5);
obj.a=7;
// manually call update instead of waiting for the bundler:
// (this is needed for this demo so we can reconcile the changes in sync and view the output)
mon.update();
// now apply stored changes to the clone of the orig data:
var updatedDupe= mon.patch(mon.diffs, dupe);
// use a cheap and easy but not production-reliable to compare the objects:
JSON.stringify(updatedDupe)==JSON.stringify(obj); // should be true
tested in chrome and firefox.
be aware that this particular demo's use of JSON depends on some luck, and consistent key ordering, which is not guaranteed by the JS spec. Key order doesn't really matter, but it might cause the JSON.stringify() == comparison to fail, even though the object's properties are indeed sync'd. This is just for demonstration's sake to get a true/false answer if it works, don't beat me up...
you can give it a custom callback to send("diff", {diffs:mon.diffs}) the changes as they happen and then use a subscribed event from pusher et al like on("diff", function(e){mon.patch(e.diffs, obj);}); to apply your changes and trigger the view update in your MVC.
I'll leave it to you to work localStorage and online/offline in there as you need, it should be really easy after getting this far.
All diffs in the change list come with three keys:
{"dt":1392348959730,"k":["b","3"],"v":"5"}
dt: a timestamp of when the change was discovered
k: the key path where the change was detected
v: what the discovered changed value is as of dt
This script is hot off the press and i haven't had time to write proper documentation, but i figure it might help or at least inspire a solution that works for you.
I think you should start researching HTML5 WebSockets: http://www.websocket.org/
It allows bi-directional communication between server and client, client pull and server push.
Then look at SignalR, the asp.net implementation of HTML5 WebSockets: http://www.asp.net/signalr

Unsubscribe from all property change events with Breeze.js

Following the documentation on subscribing to change events for a single entity is working easy peasy -
var token;
var myEntity = ko.observable();
if (token == null) {
token = myEntity().entityAspect.propertyChanged.subscribe(function (changeArgs) { trackChanges(changeArgs); });
}
But if I want to subscribe to an observableArray and keep track of the tokens
var tokens = ko.observableArray();
var myEntitys = ko.observableArray();
if (tokens().length === 0) {
ko.utils.arrayForEach(myEntitys(), function (entity) {
var etoken = entity.entityAspect.propertyChanged.subscribe(function (changeArgs) { trackChanges(changeArgs); });
tokens.push(etoken);
});
}
console.log(tokens());
The subscriptions are working fine but each of the tokens are equal to the same value
//This works fine
myEntity().entityAspect.propertyChanged.unsubscribe(token);
//**This does not work because myEntitys does not have an entityAspect, of course **
ko.utils.arrayForEach(tokens(), function (token) {
myEntitys().entityAspect.propertyChanged.unsubscribe(token);
});
//This works, but I am not sure why because token was set above to equal a single entity (myEntity())
ko.utils.arrayForEach(myEntitys(), function (entity) {
entity.entityAspect.propertyChanged.unsubscribe(token);
});
Is there a way to either unsubscribe EVERYTHING when leaving a view or a proper method to do what I am attempting above? If I reload the same view again it is double-subscribed to propertyChange events.
We were able to reproduce the issue and are investigating the problem.

Categories