I have a small application where users can upvote or downvote players based on their recent performance in various sports.
When the upvote button is currently clicked, the UID of the voter (logged in via Google) is supposed to get pushed into the database of the corresponding player who was voted on. Like this:
However, instead, it's currently doing this:
Here is the code which is doing the Firebase work. I believe it is one of these lines that I have wrong.
this.database.child(playerId).transaction
or
ref = firebase.database().ref('players/voters');
The code:
this.database = firebase.database().ref().child('players');
upvotePlayer(playerId) {
this.state.user ?
this.database.child(playerId).transaction(function (player) {
if (player) {
console.log("UID: " + uid)
var ref = firebase.database().ref('players/voters');
ref.child(uid).set(1);
}
return player;
})
:
console.log("Must be logged in to vote.")
}
you're storing the votes in this path players/voters while it should be in this path players/$playerId/voters
checkout this
this.database = firebase.database().ref().child('players');
upvotePlayer(playerId) {
this.state.user ?
this.database.child(playerId).transaction(function (player) {
if (player) {
console.log("UID: " + uid)
var ref = firebase.database().ref('players/' + playerId + '/voters');
ref.child(uid).set(1);
}
return player;
})
:
console.log("Must be logged in to vote.")
}
transaction method have transactionUpdate function as argument which should return the new value and you didn't return any , also I think better solution is not use transaction
Edit : solution without transaction
upvotePlayer(playerId)
{
if(this.state.user)
{
let ref = firebase.database().ref('players/' + playerId + '/voters');
ref.child(uid).set(1);
}
else
{
console.log("Must be logged in to vote.")
}
}
The problem is because of this:
var ref = firebase.database().ref('players/voters');
it is adding the voters under the players node and not under the push id node.
So you have to retrieve the pushid from the database and then do this:
var ref=firebase.database().ref().child('players').child(playerId).child('voters');
so the voters will become one of the pushid child.
For Future Viewers:
It is better to use transactions since:
Using a transaction prevents upvote counts from being incorrect if multiple users upvote the same post at the same time or the client had stale data
Actually you are pushing the value to payers node with player id. you have push the data to child node of players id voters. Try this code:-
this.database = firebase.database().ref().child('players');
upvotePlayer(playerId) {
this.state.user ?
this.database.child(playerId).transaction(function (player) {
if (player) {
console.log("UID: " + uid)
player.ref.child(voters).push(uid);
}
return player;
})
console.log("Must be logged in to vote.")
}
Problem is your are actually taking the reference of players node and creating new node at same level of playerid named voters. Below line just create a new node voters at same level to playerid.
var ref = firebase.database().ref('players/voters');
ref.child(uid).set(1);
as you can see there is no node named voters in players root node. But you are setting the value so it is been created with new data using set() function
Related
I have been working to get a list of all the documents inside a firestore collection. I want to display all details of all documents inside a collection.
My document tree is ask follows-
'groups' COLLECTION----->Documents w 'groupID' as reference------>'tasks' COLLECTION------>Documents w 'taskId' as reference.
Now I want to get all documents and its details inside 'tasks' collection for a particular groupID.
Future<MyTask> getCurrentTask(String groupId) async {
MyTask retVal = MyTask();
try {
DocumentSnapshot _docSnapshot =
await _firestore.collection("groups").document(groupId).collection("tasks").get();
retVal.taskId = taskId;
retVal.taskName = _docSnapshot.data['taskName'];
retVal.dueTime = _docSnapshot.data['dueTime'];
retVal.member =_docSnapshot.data['member'];
retVal.completed = _docSnapshot.data['completed'];
} catch (e) {
print(e);
}
return retVal;
}
I tried this but it doesnt work as "The method 'get' isn't defined for the type 'CollectionReference'."
How to get around this please?
Simply do like this:
Firestore.instance.collection("groups").document(groupID).collection("task").snapshots().listen((event) {
retVal = event.documents.map((e) => MyTask.fromJson(e.data)).toList();
});
I assume your MyTask model already have fromJson method so do it like that. And change your retVal to List: List<MyTask> retVal = [];. It will get all of your document and also listen whether there's change's on the collection.
This is my database:
I want to keep fetching 2 document in the list on click of a button, I tried this code, but it didn't work. Like on
1st click, fetch doc_0 and doc_1,
2nd click, fetch doc_2 and doc_3, so forth and so on.
class _FetchState extends State<Fetch> {
final List<DocumentSnapshot> _list = [];
DocumentSnapshot _lastDoc;
void _fetch() async {
Firestore.instance.collection("collection")
.limit(2)
.startAtDocument(_lastDoc)
.snapshots()
.listen((snapshot) {
_list.addAll(snapshot.documents);
_lastDoc = _list.last;
print("length = ${_list.length}");
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: _fetch),
);
}
}
I think this could be solved with only two very simple steps without changing much of your code,
Add a field to your doc to sort items with( a string, int or a timestamp).
Then in your own code just add orderBy(yourValue).
Thats it !
What you're looking for is called Firestore Pagination in Flutter.
Basically you need to call the first page without the startAtDocument once, then save the last_document_reference, then start using startAtDocument fore all other pages with updating the last_document_reference. You can see an example here
EDIT: Here is part of your code modified to what you need:
void _fetch() async {
if(_lastDoc == null) {
Firestore.instance.collection("collection")
.orderBy("name") //you can change the key "name" to whatever field key you have
.limit(2)
.snapshots()
.listen((snapshot) {
_list.addAll(snapshot.documents);
_lastDoc = _list.last;
print("length = ${_list.length}");
});
}
else {
Firestore.instance.collection("collection")
.orderBy("name") //you can change the key "name" to whatever field key you have
.startAtDocument(_lastDoc)
.limit(2)
.snapshots()
.listen((snapshot) {
_list.addAll(snapshot.documents);
_lastDoc = _list.last;
print("length = ${_list.length}");
});
}
}
Note 1: You have to manage those listeners! With every call you'll be creating new listeners which you should some how manage. It's out of scope to talk about it here. And in case of documents being updated you have to manage the trigger accordingly.
Note 2: You should be using setState((){ _list.addAll(snapshot.documents) }); to update the build (or use other means to do so).
Edit 2:
Thinking about it, you only need a listener on the new messages, but the old ones should be fixed data without any modifications (no edits or deletes); So here the second part without any listeners:
else {
Firestore.instance.collection("collection")
.orderBy("name") //you can change the key "name" to whatever field key you have
.startAtDocument(_lastDoc)
.limit(2)
.getDocuments()
.then((snapshot) {
_list.addAll(snapshot.documents);
_lastDoc = _list.last;
print("length = ${_list.length}");
});
}
I haven't tested the code, but it should be something like that.
Now you only have one listener which should update with the new documents. You might still need to handle the changes.
I'm not used to firestore documents and all. But just in case you would like to add some more code this might help.
Initialize a variable button_clicks = 0;
When the button is clicked, try to fetch the file with name file_0 and then increasing the value of our variable, fetch the file_1.
Something like:
// on button click:
{
fetch_file('file_'+button_clicks); //Function which fetches file_0.. The firestore instance code..
button_clicks++ ; //Increment in variable.
fetch_file('file_'+button_clicks); //Function which fetches file_1
button_clicks++ ; //Increment in variable.
}
Now the next time you click the button , the same thing will start from file_2 because your variable button_clicks = 2.
I would strongly suggest to store this value somewhere in firebase-realtime-database separate for each user or as per your needs if you want the user to download from file_0 on app restart.
This logic should help. :-)
Requirement here is I wants to add number of customers on dwolla in one shot. By running dwolla create customer in loop. But things is some customer addition is failing with error,
Error: {“code”:“ServerError”,“message”:“A server error occurred. Error ID: 6188070b-8a1b-4d94-90a5-eb1333d3cd9e.”}
Code:
const client = new dwolla.Client({
key : dwollaCredentials.appKey,
secret : dwollaCredentials.appSecret,
environment : 'sandbox' // optional - defaults to production
});
client.auth.client().then(Meteor.bindEnvironment(function(appToken) {
var spaceProviders = getListofSpaceProvidersWithNoDwollaAcc();
console.log(spaceProviders.length);
for (var i = 0 ; i<spaceProviders.length ; i++) {
var spaceProviderId = spaceProviders[i].id;
var routingNumberUser = spaceProviders[i].routingNo;
var accountNumberUser = spaceProviders[i].accountNumber;
var bankName = spaceProviders[i].firstName+' '+spaceProviders[i].lastName+' Bank';
if (spaceProviders[i]) {
var requestBody = {
firstName : spaceProviders[i].firstName,
lastName : spaceProviders[i].lastName,
email : spaceProviders[i].email
};
console.log('requestBody: ',requestBody);
appToken
.post('customers', requestBody)
.then((res)=> {
var dwollaLocation = res.headers.get('location');
return Promise.resolve(dwollaLocation);
})
.then(Meteor.bindEnvironment((dloc) => {
console.log("dloc"+i+' '+dloc);
return Promise.resolve(dloc);
}))
.catch(error => console.log("Handled Exceptions user",i+' - '+error));
}
}//i
})
);
Somehow bulk customers account creation is failing, may be this is creating continues calls at dwolla and it is unable to handle this much big number, may be one request starts processing and another one is reaching like wise, so finally I am settling for individual "ADD" button for each customer and calling create dwolla customer api on click event.
I'm having a Firebase Handle like this:
private var typeIndicatorHandle: DatabaseHandle?
self.typeIndicatorHandle = self.dbRef.child("chats").child(chatId).child("typingIndicator").queryOrderedByValue().queryEqual(toValue: true).observe(.value, with: { (snapshot) in
print("new value")
})
somewhere else I do this:
if let typeIndicatorHandle = self.typeIndicatorHandle {
self.dbRef.removeObserver(withHandle: typeIndicatorHandle)
}
Now the problem is the observer still gets new values. How is that possible?
You need to remove the observer on original children where you attached it.
For Example:
private var typeIndicatorHandle: DatabaseHandle?
private var dbRef:DatabaseReference?
self.childRef= self.dbRef.child("chats").child(chatId).child("typingIndicator").queryOrderedByValue().queryEqual(toValue: true)
self.typeIndicatorHandle = childRef.observe(.value, with: { (snapshot) in
print("new value")
})
To Remove the listener:
if let typeIndicatorHandle = self.typeIndicatorHandle {
self.childRef.removeObserver(withHandle: typeIndicatorHandle)
}
Sorry for my bad syntax. I don't know swift that much in case of any correctness correct it.
But you need to remove the listener on the DatabaseReference on which you have added listener.
I'm using android SDK implement the search suggestion, code looks like below:
private void performSearch(CharSequence searchTerm) {
try {
DiscoveryRequest request = new SearchRequest( searchTerm.toString())
.setSearchCenter( mMap.getCenter() )
.setCollectionSize( 10 );
ErrorCode error = request.execute( mSearchRequestListener );
if ( error != ErrorCode.NONE ) {
Log.i( TAG, "Here API place search error: " + error.name() );
mSearchAdapter.notifyDataSetInvalidated();
}
} catch ( IllegalArgumentException ex ) {
Log.i( TAG, "Here API place search exception: " +
ex.getMessage() != null ? ex.getMessage() : "" );
mSearchAdapter.notifyDataSetInvalidated();
}
}
private ResultListener<DiscoveryResultPage> mSearchRequestListener =
new ResultListener<DiscoveryResultPage>() {
#Override
public void onCompleted(DiscoveryResultPage data, ErrorCode error) {
if ( error != ErrorCode.NONE ) {
Log.i( TAG, "Here API place search error: " + error.name() );
mSearchAdapter.notifyDataSetInvalidated();
return;
}
Log.d(TAG, "mSearchRequestListener.onCompleted: count=" + data.getItems().size() );
mResultList = new ArrayList<DiscoveryResult>( data.getItems());
String vicinity = mResultList.get(0).getVicinity();
//String location = ?
mSearchAdapter.notifyDataSetChanged();
}
};
How can I get the Location of a DiscoveryResult after I get DiscoveryResult list? It seems I didn't find this property in DiscoveryResult object. I need to add this location to my RoutePlan to calculate a route.
RoutePlan routePlan = new RoutePlan();
routePlan.addWaypoint(currentGeoCoordinate);
routePlan.addWaypoint(destGeoCoordinate);
routeManager.calculateRoute( routePlan, mRouteManagerListener );
I have a workaround for this in my code. After I get vicinity, I make a request at http://geocoder.cit.api.here.com/6.2/geocode.json?searchtext=" + vicinity + "gen=9"; to get the response. There is a Location property in response of this request. the disadvantage is I need to make a request to get location every time. Any suggestion to get the location without making a request to server?
Thanks in advance.
The data in the DiscoveryResultPage can be of several types, and you should sheck for the right type.
See documentation here:
https://developer.here.com/mobile-sdks/documentation/android-hybrid-plus/topics/places.html
The important part:
Calling DiscoveryResultPage.getItems(), returns a List containing one
of the following types of objects, which are DiscoveryResult
instances. DiscoveryResult is a collection of Link subtypes.
PlaceLink - Represents discovery information about a Place. The
PlaceLink contains a brief summary about a place. Details about a
place are available from the Place that the PlaceLink references.
DiscoveryLink - Represents a discovery-related API link used to
retrieve additional DiscoveryResultPage. This type of Link can be a
result item in an Explore or Here type of search. The DiscoveryLink
references refined discovery requests resulting in more specific
results. For example, the DiscoveryLink may link to a discovery
request to search for 'Eat & Drink', 'Going Out', 'Accommodation', and
so on. Since there may be new types of Link items in the future, it is
recommended that each type of DiscoveryResult be checked before it is
used (as shown in the following code snippet).
That means practically, that you iterate over data and check for the types like this:
// Implement a search result listener
ResultListener<DiscoveryResultPage> searchListener = new ResultListener<DiscoveryResultPage>() {
#Override
public void onCompleted(DiscoveryResultPage results, ErrorCode error) {
if (error == ErrorCode.NONE) {
// The results is a DiscoveryResultPage which represents a
// paginated collection of items.
List<DiscoveryResult> items = results.getItems();
// Iterate through the found place items.
for (DiscoveryResult item : items) {
// A Item can either be a PlaceLink (meta information
// about a Place) or a DiscoveryLink (which is a reference
// to another refined search that is related to the
// original search; for example, a search for
// "Leisure & Outdoor").
if (item.getResultType() == ResultType.PLACE) {
PlaceLink placeLink = (PlaceLink) item;
// PlaceLink should be presented to the user, so the link can be
// selected in order to retrieve additional details about a place
// of interest.
...
} else if (item.getResultType() == ResultType.DISCOVERY) {
DiscoveryLink discoveryLink = (DiscoveryLink) item;
// DiscoveryLink can also be presented to the user.
// When a DiscoveryLink is selected, another search request should be
// performed to retrieve results for a specific category.
...
}
}
} else {
// Handle search request error.
}
}
};
or only get the PlaceLink items directly like that:
public void onCompleted(DiscoveryResultPage data, ErrorCode error) {
List<PlaceLink> results = data.getPlaceLinks(); // we are only interested in PlaceLinks
if (results.size() > 0) {
for (PlaceLink result : results) {
// do something with the PlaceLink item
}
} else {
// handle empty result case
}
The PlaceLink object then has all the methods you would except, also getPosition() or getDistance() that you can use for putting results on the map or calculate a route.