I am using angularFire v 0.5.0
On my returned object I do a $add.
What would be the best way to get the generated key?
It must be something in the succes callback of the $add, because the key will come back asynchrounlusly?
scope.settings = $firebase(ref);
scope.settings.value = {
vat: {
inclusive: true
}
}
}
scope.settings.$add (scope.settings.value).then( function ( {
scope.settings.key = ???
});
note: this is not the actual code, this is just to clarify the problem.
The actiual code is inside a factory in the loaded event, and there is a check if there is already a settings object inside the database.
This somewhat strange construct is to make it easyer for consumers of the factory to change the settings. The settings is a singleton. AngularFire does not return a array of objects, but every item is a object in the settings, with its own key.
$add returns a Firebase ref. Call name() on that ref.
scope.settings.$add(…).name();
See https://github.com/firebase/angularFire/blob/master/angularfire.js#L127.
from the angularfire docs:
The $add method takes a single argument of any type. It will append this value as a member of a list (ordered in chronological order). This is the equivalent of calling push(value) on a Firebase reference.
from the firebase docs (about push()):
Returns
A Firebase reference for the generated location.
so, i think the "key" that you want is the return value of the $add.
Maybe they've changed it in newer versions, now it works like this:
$add() will return a promise. You have to use the callback which runs on success. For example:
var something = $scope.somethings.$add({name: "foo"});
something.then(function(e) {
console.log(e.name())
});
This allows you to perform different actions based on the outcome of the save/write attempt to Firebase.
Related
I had some apps that used the multi-location update method as below:
const updates = [];
updates['/location1'] = data;
updates['/location2'] = data2;
firebase.database().ref().update(updates);
Yet, as I'm developing a new app, I got the following message:
FIREBASE WARNING: Passing an Array to Firebase.update() is deprecated. Use set() if you want to overwrite the existing data, or an Object with integer keys if you really do want to only update some of the children.
With no real info anywhere about how to perform multi-location atomic updates anywhere, and the docs are still with the old method. Chaining then() is not a good idea, as it would be a nightmare to rollback.
Any clues and/or information on new multi-location updates methods?
I didn't even know you could pass an array. You should instead pass an object:
const updates = {}; // this line is different
updates['/location1'] = data;
updates['/location2'] = data2;
firebase.database().ref().update(updates);
The array syntax we use for setting properties works on a JavaScript object too.
I made a small app on firebase. I have been using a type provided by angularfire called a FirebaseListObservable which allows you to observe changes to your data. The problem I'm having is that Angularfire also provides a way to query the data, by attaching / passing in a query object to the request.
The following returns a FirebaseListObservable.
this.resources$ = <FirebaseListObservable<IResource[]>> this.af.list(`/resources/${this.auth.id}`) as FirebaseListObservable<IResource[]>;
//This returns a regular observable.
this.resources$ = <FirebaseListObservable<IResource[]>> this.af.list(`/resources/${this.auth.id}`,
{
query: {
orderByChild: `${property}`
}
}
) as FirebaseListObservable<IResource[]>;
But this returns a plain old observable. The issue is that I need a FirebaseListObservable because it has added CRUD functionality and I'm trying to write simple filter / sorting methods.
I can see they have an Open Issue for this but I'm hoping that there is a way around this issue sooner rather than later. The solution discussed in the ticket describes extending the FirebaseListObservable and overriding the lift method to return the custom observable. I attempted to create a CustomFirebaseListObservable that does just that, but I don't seem to have access to the correct properties (observable.source and observable.operator from within my custom observable. I'm hoping for a way to cast an observable to a firebaseListObservable. Any workaround would do really.
If you are still having this issue, maybe you can create the firebase reference with the ordering and then passing that into angularfire?
let refPath = `/resources/${this.auth.id}`,
ref = firebase.database().ref(refPath).orderByChild(property);
this.resources$ = <FirebaseListObservable<IResource[]>>this.af.list(ref);
I know there is another library called angular-cache on npm/bower. But adding a new library is quite a process where I work. So I want to use $cacheFactory's or core Javascript functions to solve this.
I saw a lot of responses in stackoverflow for getting all the keys of a hasmap, so I tried this:
var keys = [];
for(var key in parameterCache) {
keys.push(key);
console.log(key);
}
The outputs on the console were weird. They were the names of functions in $cacheFactory (remove, destroy, info etc.)
There is no built-in way of listing all keys from a $cacheFactory instance and there are no plans to include this to the core library, so you will have to either use a replacement such as angular-cache library or add these methods by yourself. I needed to add this functionality to my $http cache and I couldn't find a way to do this cleanly (with decorators or something) so I had to resort to monkey-patching it in my .run() block:
// create a new $cacheFactory instance
var httpWithKeysCacheFactory = $cacheFactory('$httpWithKeys'),
// save the original put() method
originalPut = httpWithKeysCacheFactory.put;
// add a property to hold cache keys
httpWithKeysCacheFactory.keys = [];
// overwrite put() with a custom method
httpWithKeysCacheFactory.put = function (key, value) {
// call original put() and save the key
if (originalPut(key, value)) {
this.keys.push(key);
}
};
// tell $http to use your monkey-patched cache factory
$http.defaults.cache = httpWithKeysCacheFactory;
Doing so allowed me to access my cache keys from a controller like this:
$cacheFactory.get('$httpWithKeys').keys;
Note: this is a very naive approach that does not check for key duplicates and does not modify remove() and removeAll() methods to update keys when an entry is removed from cache.
You could use this module : angular-cache
So you can use this CacheFactory.keys() wich return you all id of registered caches
Using IndexedDB for a local html5 app, and specifically the YDN-DB wrapper, I often need to query a store using dynamically obtained store names.
When the store does not exist, I come away with an error, and javascript execution aborts. The error looks like so:
Uncaught ydn.error.ArgumentException: Store "client_store" not found.
Of course, I know the store does not exist, but how best can I code to 'catch' this error more elegantly?
Thank you.
You can use this little utility function:
function storeExists(name) {
var exists = false;
db.getSchema().stores.forEach(function (store) {
if (store.name === name) {
return exists = true;
}
});
return exists;
}
Execution
storeExists('client_store');
The best is you can avoid dynamically changing database schema.
You can also check existence of store name by checking in schema db.getSchema().stores.contains('new store').
So I want to subscribe to a publish function which only returns one object of a collection.
Meteor.publish("singleobject", function(bar) {
return MyCollection.find({foo: bar});
});
This should give me the SINGLE one object of the collection "MyCollection" where the foo property is equal to "bar", right? (There is only one object where this is true ... so findOne() should also work). What it does instead is returning me ALL objects of my collection, even those where foo is NOT equal to bar.
It works perfectly with another collection where there are more than one object where foo: "bar" is true.
I can't really see what I am doing wrong. Can I not subscribe to a publish function which returns only one object?
Anyone has any ideas on this?! :-)
best regards
Patrick
The code you've used:
Meteor.publish("singleobject", function(bar) {
return MyCollection.find({foo: bar});
});
doesn't return just one object, but every object that has foo equal to bar. If you want just one (and no matter which one), you should use findOne instead:
Meteor.publish("singleobject", function(bar) {
return MyCollection.findOne({foo: bar});
});
If you see also objects that have foo !== bar, it means that you fetch them elsewhere. There are two possible explanations:
You have another publish method for the same collection, or
You have autopublish package still on.
Take care of these two things and you should be fine.
For subscription, this is the usual pattern:
Deps.autorun(function(){
Meteor.subscribe('channel');
});
If you want the subscription to only work from time to time, there are few ways to achieve it.
The simplest one is to add a boolean argument to the subscription, and set it to true only if you want the channel to work. In the publish method you then simply return null if the flag is false.
More clean way is to track all your subscription handles and call stop() on those you don't want to use at this moment. It's nice, but hard to recommend in this version of Meteor as everything has to be done manually, which adds some not really necessary work.
So ... do NOT put
Meteor.subscribe()
inside of
Meteor.autorun()
or
Deps.autorun()
. Everything inside Meteor.autorun()/Deps.autorun() is ALWAYS executed, even if it's inside a template specific .js file. I was thinking every single one of these js files is only loaded when the according template is loaded, which of course is totally wrong.
This is only for structuring your app properly.
So if you want to subscribe to a publish function depending on the template loaded, put the Meteor.subscribe into the router callback functions and subscribe there.
Works perfect for me now! :)