I'm attempting to get the length of the array of a simple angularFireCollection and can't seem to:
var stf = new FireBase("http://myfirebase-app.firebaseio.com/staff");
function staffListCtrl($scope, angularFireCollection){
$scope.staff = angularFireCollection(stf);
console.log($scope.staff.length);
}
The output in the console says:
0
Which I know is incorrect. It should be returning somewhere around 5 as the length (see screenshot below for the output of $scope.staff.
Any help is a appreciated as I can't seem to get past this absolutely, utterly simple JS task.
In this case, the correct way to print the number of retrieved elements within the callback would be following:
var stf = new FireBase("http://myfirebase-app.firebaseio.com/staff");
function staffListCtrl($scope, angularFireCollection) {
$scope.staff = angularFireCollection(stf, function() {
console.log(stf.numChildren());
});
}
The reason for this is that the initial callback function is called before actual assignment to $scope.staff takes place. So within the callback function you can access only the Firebase DataSnapshot object stf. Hope this helps.
You're trying to access the length immediately after calling angularFireCollection, but the actual data is retrieved over the network and therefore it takes a little while for the array to be updated. You can pass a function as an argument to angularFireCollection to be notified about when the initial data is loaded, like so:
var stf = new FireBase("http://myfirebase-app.firebaseio.com/staff");
function staffListCtrl($scope, angularFireCollection) {
$scope.staff = angularFireCollection(stf, function() {
console.log($scope.staff.length);
});
}
ah, I see it in angularfire.js. Line 298 wraps adding an item in a $timeout. That's causing the initalCb() to get called before the data has been added to the collection. I pulled the $timeout and it worked. However, then I had to call $scope.$apply() to reflect the added items. I ended up passing scope into angularFireCollection to be sure $apply() gets called. No idea what else I broke by pulling the timeout.
this is a view of the issue: plunkr
EDIT:
as far as I can tell and showed in the plunkr, this doesn't work.
$scope.staff = angularFireCollection(stf, function() {
console.log($scope.staff.length);
});
and while pulling the $timeout from angularfire.js did fix that particular issue, it caused all kinds of other headaches with databinding(as expected), so I put it back. It seems the way to go is to use the snapshot that is passed in the callback. I can't find a lot of documentation on it, but here's what worked for me:
$scope.staff = angularFireCollection(stf, function(snapshot) {
angular.forEach(snapshot, function(item){
//will let you iterate through the data in the snapshot
});
});
Related
I wanted to use rxjs for the first time but am a bit stucked 'cause it doesn't behave exactly like I want it to: In my scenario I want to create an observable from a promise. But I want the promise only being called once (not on every subscription) and I want it not being called on creation time (defer the call to the first subscription).
First I tried this:
var source = Rx.Observable.fromPromise(_this.getMyPromise())
which causes a call to the getMyPromise function right on creation time. This is not satisfying because at that time I don't know if the source really will be used.
Then I tried:
var source = Rx.Observable.defer(function() { return _this.getMyPromise() })
which causes a call to the getMyPromise function each time a new subscription is being made to source. This makes way too many unnecessary calls to the web server. The Rx.Observable.create function seems to have the same issue.
So what is left or what am I missing?
.shareReplay() does this, e.g.:
var source = Rx.Observable.defer(function() { return _this.getMyPromise() }).shareReplay();
If you're using rxjs5, you'll want to read: Pattern for shareReplay(1) in RxJS5
In answer to your comment below, I can think of a fairly straightforward extension to the above logic that will do what you want, but it has a caveat. Let's say the events you want to use to trigger a "refresh" are represented in a stream, s$, then you could do something like:
var source = Rx.Observable.of({}).concat(s$)
.flatMapLatest(function() {
return Rx.Observable.defer(function() {
return _this.getMyPromise()
})
})
.shareReplay(1)
What we have here is a stream starting with a dummy object to get things rolling, followed by a stream consisting of your refresh events. Each of these is projected into a new observable created from a fresh invocation of your getMyPromise method, and the whole thing is flattened into a single stream. Finally, we keep the shareReplay logic so we only actually make calls when we should.
The caveat is that this will only work properly if there's always at least one subscriber to the source (the first subscription after all others are disposed will run the promise again, and will receive both the previously-cached value and the result of the promise it caused to run).
Here is an answer that does not require at least one subscriber at the source at all times using a simple helper:
var _p = null;
var once = function() { return _p || (_p = _this.getMyPromise());
var source = Rx.Observable.defer(once);
Or if you're using lodash, you can _.memoize your getMyPromise and get this automatically.
I have this code in a Meteor.methods definition:
update_field: function(collection,document_id,field,value) {
obj = {};
obj[field] = value;
console.log(obj);
if (collection == 'clients') {
var Collection = Clients;
} else if(collection = 'sites') {
var Collection = Sites;
}
Collection.update(
{
_id: document_id
}, {
$set: obj
}, function(error,id) {
console.log(error,id);
return(error,id);
}
);
}
This method is called from several client-side helpers events, and updates the field as needed. But whenever it runs once, it never stops running. Sometimes it runs infinitely even when all the Meteor.call('update_field')s have been commented out. I have tried including a 'caller' parameter and adding that to all the possible calls to figure out why it keeps getting called to no avail. Any ideas why this is looping?
Edit: this runs 2,000/minute
Edit2: this is called in one of two ways: on a keyup code==13 (enter) in an appropriate field or a field blur. However, event when these calls are commented out, the issue persists.
Especially your second comment worries me:
However, even when these calls are commented out, the issue persists.
Then who is calling it? The behaviour you're describing points to some helper executing the method. The method changes some data, which re-executes the helper (reactivity) and we end up with a classic endless loop.
Check your entire source code for references to this method:
$ grep -r "update_field" *
Maybe you set a variable somehow and then use the variable to call the method. Also: Have you declared the method inside a Meteor.methods({ ... }) block?
I think the issue was that one of my methods blurred the input field but preventDefaulted. Then the blur handler was called and caused the loop from there. This is my first Meteor project, so I'm chalking this one up to not quite understanding the system sufficiently. I still find it strange that the method was getting called when the callers were commented out, but I'll figure that one out another day.
I'm displaying a series of images in a loop, and I'm trying to implement some sort of nudity filter so I'm using nude.js, a library that can somewhat detect nudity. Here's the code:
// we're inside a loop
$(".images").prepend($("<img>").attr({src: whatever, id: uniqueid}).load(function(e) {
nude.load(e.target.id);
nude.scan(function(result) { if (!result) $(e.target).detach(); });
});
However, it detaches all of the wrong images because nude.js is slow and it completes after the loop has gone on to the later iterations, detaching those images instead of the one it was working on.
I've tried using a function factory:
function generateCallback(arg) {
return function(result) { if (!result) $(arg).detach(); };
}
and
nude.scan( generateCallback(e.target) )
but the same thing happens.
What I want is a load event that will remove the image if it seems to contain nudity. How can I do this properly?
EDIT: nude.js works like this:
nude.load(imageid);
nude.scan(callback); // it'll pass true or false into the callback
another edit: accidentally omitted the id setting from the code I posted, but it was there in my real code, so I added it here.
I suspect the case here is that this kind of sequential processing won't work with nude.js.
Looking at the nude.js code, I think your problem is occurring in the call to nude.scan. nude.js has a variable that stores the function to invoke after the scan has completed. When calling nude.scan(callback), this variable is set to be callback.
From your PasteBin, it seems as though the callback gets assigned as expected on the first call, but on the second and subsequent calls, it gets replaced, hence why the second image is detached and not the first.
What happends to your script, is that the e var is global to the function and so after each loop it gets replaced with the new one. So when the first image is scanned, e already became the event of the second image, which get detached.
To solve your problem, use closures. If you want to know more about closures, have a look here.
Otherway, here's the solution to your problem :
$(".images").prepend($("<img>").attr({src: whatever, id: uniqueid}).load(function(e) {
(function(e) {
nude.load(e.target.id);
nude.scan(function(result) { if (!result) $(e.target).detach(); });
}) (e);
});
EDIT: AS nick_w said, there is var that contains the callback and probably gets replaced each time so this is why it isn't the right picture getting detached. You will probably have to modify the script yourself
I didn't know a better way to phrase that question. I've written a basic service to be used by two controllers.
JsFiddle: http://jsfiddle.net/aditya/2Nd8u/2/
Clicking 'notify' works as expected; it adds a notification to the array. But 'reset' breaks it. Clicking either button after 'reset' doesn't do anything. Does anyone know what's going on here?
PS. I think it has something to do with Angular losing the reference since notifs is being re-assigned (technically), so I wrote getters and setters, but even emptying the array involves pop()ing till it's empty, which doesn't seem very efficient.
Plunkr if JsFiddle is down: http://plnkr.co/edit/mzfLLjFXCwsxM5KDebhc
I've forked your plunker and propose a solution:
In reset function, try to remove the objects of the array instead of declaring as new empty array:
notificationService.notifs.splice(0, notificationService.notifs.length);
Or, like suggested by #Wizcover:
notificationService.notifs.length = 0
This will notify angular that are modifications in the original array.
I changed your service to this:
.factory("notificationService", function(){
var notifications = [];
return {
notifs: notifications,
clear: function(){
angular.copy([], notifications);
},
get: function(){
return notifs;
}
}
})
and your controller :
$scope.reset = function(){
console.log("reset");
notificationService.clear();
console.log(notificationService);
}
and it works for me.
Naturally it should be a little bit tidier, in that instead of notifs you should have a get, and add and a remove method, but i just wanted to show you where the code changed.
The angular.copy is the method that makes sure the changes are made within angular's lifecycle.
As you can't bind variable but only methods, you could do this:
$scope.getNotifications = notificationService.get;
That should work.
I'm developing a small plugin that changes the favicons if there are unread messages in mailbox in Roundcubemail. However, the API sucks, and the event listupdate is fired only when the whole page is loaded, even if it is meant to fire when the list is updated.
However, I've managed to find out, that every time the list is updated, certain functions are called, such as set_unread_count. It gets the unread-count easily, so it would be great to somehow "append" stuff to this function. I just think based on hours of searching that there is no solution for this. Can I add a callback to be called when the set_unread_count is called? Can I somehow append stuff to that function? Any other ideas?
Create a little hook.
var _old_set_unread_count = set_unread_count;
set_unread_count = function() {
// do whatever you want here
// access arguments[x] to get arguments.
_old_set_unread_count.apply(this, arguments);
};
Demo: http://www.jsfiddle.net/4yUqL/69/