Flutter listen to one document change using provider - firebase

I have seen examples of listening to document changes in streambuilder, but is it possible to use it in providers? I want to listen to changes in the document in userinfo collection.
Here is my code:
in databaseservice.dart
Stream <DocumentSnapshot> get info{
return userinfo.doc(uid).snapshots();
}
In main
return MultiProvider(
providers: [
StreamProvider<DocumentSnapshot>.value(
value: DatabaseService().info
), // other providers
In wrapper where I need to see the change:
final info = Provider.of<DocumentSnapshot>(context).data();
However, I'll first get error:
The method 'data' was called on null.
Receiver: null
Tried calling: data()
And later, the info variable is giving me null instead of a map.
I want to let users input their name and age after their first signup, but not when they sign in. So my idea is that when users sign up, there will be a new document in the collection, "userinfo", which sets their age and name as null at first.
Then the wrapper checks if the name is null. If null, it will turn to the gather information page. If it has a value, it will turn to the home page.
Could anyone tell me where I am doing wrong with my document snapshot thing, or have a better idea to implement this?

Related

How to trigger onCreate() when a new field is created in an existing Firestore document?

I want to create a new node in firebase realtime database when a field is created in an existing Firestore document.
I have been trying this:
exports.addUserCredentials = functions.firestore
.document(`Users/{UserID}/{username}`)
.onCreate((snapshot, context) => {
const newUserData = snapshot.data()
const newUserUsername = newUserData
const newUserUidDoc = context.params.UserID
return admin.database().ref(`/userCredentials/${newUserUsername}`).set({"UID": newUserUidDoc})
})
I have searched around the web I saw the path must be directed towards a document only and not a collection. BUT username in the path is a FIELD in the document.
I am getting this error while deploying and I have seen all similar questions but those didn't perfectly answered mine:
! functions: failed to update function addUserCredentials
HTTP Error: 400, The request has errors
The reason is can't change my path to Users/{UserID} which will make my code run perfectly is the fields of document are not added at once.
Here is screenshot of my firestore structure:
The 4 fields of document are updated in 2 batches.
The EMAIL and timeCreated fields are added first and those create the document.
While on the other hand, phoneData and username are fields are CREATED [not updated] after 5 seconds of Email and timeCreated.
So if I use onCreate() on the path Users/{UserID}, it will return UNDEFINED to my realtime database as the username field is ABSENT at that instant.
Is there any way to apply onCreate() on a specific field of the document?
[I am doing this to create a separate node which contains username and UID, this is to check if an username exists when a new user is trying to sign up]
So if the node is created with value undefined it will be an issue.
It will be like this:
The EMAIL and timeCreated fields are added first and those create the document. While on the other hand, phoneData and username are fields are CREATED [not updated] after 5 seconds of Email and timeCreated.
No matter what fields are adding once you created a document,it will be considered as an update operation against that document.As you mentioned in the question,there will be no field with field name called username with a document while you creating document.So it is not possible to get the value of username while you creating the document.
According to your explanation the field username will be only available with the onUpdate trigger.
So the code should be something like below
exports.addUserCredentials = functions.firestore
.document(`Users/{UserID}`)
.onUpdate((snapshot, context) => {
const beforeData = snapshot.before.data()
const afterData = snapshot.after.data()
if(!beforeData.username && afterData.username){
return admin.database().ref(`/userCredentials/${newUserUsername}`).set({"UID": newUserUidDoc})
}
})

Determine RTDB url in a trigger function

i m bulding a scalable chat app with RTDB and firestore
here is my raw structure of shards
SHARD1
Chats {
chat01: {
Info: {
// some info about this chatroom
},
Messages ...
}, ....
}
SHARD2...
now i have write triggers on all the info nodes of all the shards.
i want get the ID of the shard
How do i know what shard it actually ran on ?
[EDIT]
console.log(admin.app().name); // it prints "[DEFAULT]" in console
Puf and team please help
When a Realtime Database trigger is invoked, the second argument is an EventContext object that contains information about the database and node that was updated. That object contains a resource string, which has what you're looking for. According to the documentation for that string, it's name property will be formatted as:
projects/_/instances/<databaseInstance>/refs/<databasePath>
The databaseInstance string is what you're looking for. So, you can just split the string on "/" and take the 4th element of that array:
export const yourFunction = functions.database
.instance('yourShard')
.ref('yourNode')
.onCreate((snap, context) => {
const parts = context.resource.name.split('/')
const shard = parts[3]
console.log(shard)
})
If all you need is a reference to the location of the change, in order to perform some changes there, you can just use the ref property on the DataSnapshot that was delivered in the first argument, and build a path relative to there.

change.after.val() returns full JSON object rather than value of the object

I'm working on a Firebase Cloud Function. When I log the value of change.after.val() I get a printout of a key-value pair
{ DCBPUTBPT5haNaMvMZRNEpOAWXf3: 'https://m.youtube.com/watch?v=t-7mQhSZRgM' }
rather than simply the value (the URL). Here's my code. What am I not understanding about .val() ? Shouldn't "updated" simply contain the URL?
exports.fanOutLink = functions.database.ref('/userLink').onWrite((change, context) => {
const updated = change.after.val();
console.log(updated);
return null
});
If you want only the URL value, you should include a wildcard in your trigger path for the URL key:
exports.fanOutLink = functions.database.ref('/userLink/{keyId}').onWrite((change, context) => {
console.log('keyId=', context.params.keyId);
const updated = change.after.val();
console.log(updated);
return null
});
In the Realtime Database, data is modeled as a JSON tree. The path specified in an event trigger identifies a node in the tree. The value of the node, being JSON, includes all child nodes. The change parameter for the trigger event refers to the value of the entire node.
I indicated above that you can change the trigger path to refer one level down. An alternative is to access the children of the node using the child() method of DataSnapshot.
Without knowing your use-case, it's hard to be more specific about the trigger event path you should use. Keep in mind that the event fires when any element of the node value changes, whether it be a simple value at the root level, or a value of a child node. It is often the case that you want the trigger to be as specific as possible, to better identify what changed. That's where wildcards in the path are useful. As I showed in the code I posted, the string value of a wildcard is available from the context parameter.

firebase function af.database.object unable to return each object in a variable

I want get the user profile data from firebase of a specified user.
By instance I want return the displayName and the email address of the user where UID HBJKD12345
I use this function :
this.userProfile = this.af.database.object('/users/'+this.authInfo.uid)
But with a console.log(this.userProfile.email) I get a "Undefined"
In my view I have to use : {{(userProfile | async )?.email}}
With this line I can show the email address of the user but why I get "Undefined" when I try with console.log ?
Your view works because of async. It unwraps the actual value. For console.log(), you would need to do something similar. Try this.userProfile.$value().
Take a look at https://github.com/angular/angularfire2/blob/master/docs/2-retrieving-data-as-objects.md#meta-fields-on-the-object or https://github.com/angular/angularfire2/blob/master/docs/2-retrieving-data-as-objects.md#meta-fields-on-the-object

How do I fetch user from pointer on Parse.com?

I need help using fetch() in CloudCode on Parse.com
RIght now everything I do throws an error that says " Cannot create a pointer to an unsaved ParseObject".
I am starting to think that CloudCode is broken because it doesn't make sense at all because I'm not creating anything new or creating pointers.
It's simple as
query.find().then(function(lists){
lists.forEach(elem, i){
elem.get('owner').fetch({
success: function(user){
console.log(user);
}
})
}
});
Where 'owner' is a pointer to a user object and the query is on a class that contains a Pointer (owner) and an Array of objects. Essentially I want to get the email of the owner to compare to the Array of objects that also contain emails.
Trying to use include with the query also doesn't work.

Resources